Next.js에서는 몇몇 강력한 라우팅 시스템을 제공합니다. Next.js의 라우팅 관련 주요 개념들을 살펴보겠습니다.
먼저 useRouter를 사용하면서 다음과 같은 문제들을 발견할 수 있습니다.
Next.js에서는 먼저 useRouter 훅을 import하여 라우팅 관련 기능을 활용할 수 있습니다. 이때 중요한 점은 import 경로에 따라 다른 동작을 합니다.
// App Router용 (Next.js 13+)
import { useRouter } from 'next/navigation';
// Pages Router용 (기존 방식)
import { useRouter } from 'next/router';
여기서 App Router와 Pages Router의 방식은 호환되지 않으므로, 사용 중인 라우팅 방식과 Next.js 버전에 맞는 import 경로를 사용합니다.
이건 버그는 아니지만
const router = useRouter();
console.log(router);
위 코드를 실행하면 특정 상황에 console에 router 객체가 두 번 출력되는 것을 볼 수 있습니다. 이는 Next.js가 쿼리스트링을 읽는 과정에서 컴포넌트를 한 번 더 렌더링하기 때문입니다. 이럴 때에는 두 번째 console.log()에만 쿼리스트링에 대한 정보가 담겨있습니다.
Next.js는 다양한 형태의 동적 라우팅을 지원합니다. 여기선 두 가지 Catch All Segments 방식과 Optional Catch All Segments 방식을 연구해보겠습니다.
URL이 /CAS/123/456/789...와 같이 여러 ID 값을 포함할 경우, 파일을 계속 중첩해서 만들지 않고, /CAS/[...id].tsx로 지정하면 모든 ID 값을 배열 형태로 받을 수 있습니다.
// /CAS/[...id].tsx
const CASPage = () => {
const router = useRouter();
console.log(router.query.id); // ['123', '456', '789', ... ]
return <> ... </>;
};
이러한 방식을 catch all segment라고 부르며, 특정 경로 이후의 모든 세그먼트를 캡쳐합니다.
만약 /OCAS 와 같이 index.tsx가 없는 URL에서도 같은 페이지를 보여주고 싶다면, 파일명을 /OCAS/[[...id]].tsx와 같이 이중 대괄호로 감싸면 됩니다.
// /OCAS/[[...id]].tsx
const OCASPage = () =>{
const router = useRouter();
console.log(router.query.id); // id가 없으면 undefined, 있으면 배열 형태
return <> ... </>;
};
이러한 방식은 optional catch all segment라고 부릅니다.
Next.js에서는 페이지 이동 방식이 여러 가지 있습니다.
<a> 태그<a href="/about">About</a>
이 방식은 서버에 새로운 페이지를 요청하는 방식으로, 클라이언트 사이드 렌더링이 아닌 전통적인 페이지 이동 방식입니다.
<Link> 컴포넌트import Link from 'next/link';
<Link href='/about'>About</Link>
<Link>컴포넌트는 클라이언트 사이드 렌더링 방식으로 페이지를 이동시키며, SPA처럼 동작합니다.
const router = useRouter();
// 이벤트 핸들러 등에서 사용
const handleClick = () => {
router.push('/about');
};
router.push() 메서드도 CSR 방식으로 페이지를 이동시킵니다.
여기서 중요한 Next.js의 핵심 기능 중 하나는 'pre-fetching' 입니다. 이는 사용자가 현재 페이지에서 이동할 가능성이 있는 페이지를 사전에 불러놓는 기능입니다.
Next.js는 번들링 시 현재 페이지에 필요한 JS 번들만 전달합니다. 모든 페이지의 번들을 한꺼번에 전달하면 초기 로딩 시간이 길어지고 TTI가 늦어지기 때문입니다. 그에 반해 현재 페이지에 필요한 번들만 있으면, 사용자가 페이지를 이동할 때마다 새 번들을 받아야 하는 비효율이 발생합니다. 이 문제를 해결하기 위해서 Next.js는 pre-fetching 기능을 제공합니다.
<Link> 컴포넌트의 Pre-fetching<Link> 컴포넌트는 기본적으로 pre-fetching을 수행합니다. 이를 통해 사용자가 링크를 클릭하면 이미 필요한 리소스가 로드되어 있어 빠른 페이지 전환이 가능합니다.
// 기본적으로 pre-fetching이 활성화됨
<Link href="/about">About</Link>
// pre-fetching을 비활성화하려면
<Link href="/about" prefetch={false}>About</Link>
router.push()와 같은 프로그래매틱 페이지 이동에는 기본적으로 pre-fetching이 적용되지 않기 때문에 컴포넌트가 마운트 되었을 때 router.prefetch() 메서드 등으로 pre-fetching을 수행할 수 있습니다.
useEffect(() => {
router.prefetch('/about');
}, []);
이렇게 하면 컴포넌트가 마운트될 때 '/about' 페이지에 대한 pre-fetching이 수행됩니다.
Pre-fetching은 개발 모드에서 확인할 수 없습니다. npm run build 후 npm run start로 프로덕션 환경에서 확인해야 합니다.