[Next.js 탐험기] 4. Next.js + 티스토리 API 블로그 만들기 - app router 에서의 동적 라우팅 및 파라미터 사용법

서론

Pages Router에서는 next/router로부터 useRouter를 가져와서 사용했지만, App Router에서는 next/navigation으로부터 useRouter를 가져와야 한다. 또한 기존의 useRouter에서 제공하는 기능들 중 라우팅과 관련된 기능들만 담당한다. pathname, query와 관련된 기능은 next/navigation의 usePathname, useSearchParams로 분리되었는데, 사용법은 차차 탐구하기로 한다. 본 포스팅에서는 필요한 url을 얻어 사용할 것이다.

 

폴더구조

폴더구조는 아래와 같다. 본 프로젝트에서 사용하는 티스토리 블로그 카테고리는 서브카테고리까지 포함하여 2개의 카테고리까지만을 제공한다. 때문에 2 Depth 형식의 변화무쌍한 URI를 받아들이기 위해 [categoryName],[subCategoryName] 2가지를 사용하였다.

src/
└── app/
    └── [categoryName]/
        └── page.js
        └── [subCategoryName]/
            └── page.js

이제부터 본격적인 기능을 구현하려면 공식 문서를 참고해야 한다.

 

Functions: useRouter | Next.js

API reference for the useRouter hook.

nextjs.org

 

 

동적 라우팅

Next.js에서 동적 라우팅을 사용하여 URI가 변동적인 페이지를 처리하려면, 파일 및 폴더 이름에 대괄호([])를 사용하여 동적 경로 세그먼트를 정의해야 한다. 이렇게 하면 다양한 URL이 동일한 페이지 컴포넌트로 라우팅될 수 있다. 나는 이 점을 적극 활용하였다. 이전 포스팅에서 만든 SideBar에서 표출된 Category List에 Link 태그를 사용하여 categoryName/subCategoryName 형식의 URI를 만들었다.

http://localhost:3000/[categoryName]/[subCategoryName]

 

이 경우, /[categoryName], /[categoryName]/[subCategoryName] 등으로 라우팅될 때, 각각의 page.js 파일에 정의된 컴포넌트가 렌더링된다. 해당 파일 내에서는 위에서 잠시 언급한 usePathname 훅을 사용하여 동적 경로 세그먼트의 실제 값을 얻을 수 있다.

import React from 'react';
import { usePathname} from 'next/navigation';

const CategoryPage = () => {
    const pathname = usePathname();

    return <div>여기는 {pathname} 카테고리 페이지입니다.</div>;
};

export default CategoryPage;

이렇게 코드를 작성했다. 내 예상대로라면 정확하게 메인 페이지에 "여기는 {pathname} 카테고리 페이지입니다." 라는 문구가 나왔어야 했는데, 내 예상과는 다르게 아래 오류가 발생하였다.

자세히 보니 usePathname은 client components에서만 동작한다는 것이다. 때문에 "use client"를 file 맨 위에 기입해야 한다는 문구였다. 그 이후로는 정상적으로 아래와 같이 문구가 출력되는 모습을 확인할 수 있었다.

 

 

파라미터

파라미터의 값을 사용하고 싶다면, useSearchParams()을 사용하면 된다. get 메서드로 query의 value 값을 가져올 수 있고 has 메서드로 query 키의 존재 여부를 확인할 수 있다.

'use client'

import React from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';

const CategoryPage = () => {
    const pathname = decodeURI(usePathname());
    const params = useSearchParams();

    return(
        <>
            <div>여기는 {pathname} 카테고리 페이지입니다.</div>
            <div>여기는 {params.get('categoryId')} categoryId 페이지입니다.</div>
        </>
    );
};

export default CategoryPage;