[Next] Dynamic Routes / Router Hook / Catch All
1. Dynamic Routes
NextJS는 폴더와 파일명을 이용해 라우팅을 할 수 있다.
/movies/all이라는 URL을 만들고 싶다면 movies 폴더를 만들고 그 안에 all.js 파일을 생성하면 된다.
그냥 /movies라고만 하고 싶다면 movies 폴더 안에 index.js 파일을 만들어주면 된다.
URL에 변수를 넣고 싶을 때는 대괄호 안에 사용하고자 하는 변수명을 적어서 Dynamic Route를 생성할 수 있다.
만일 page/movies/[id].js 파일을 생성했다면 /movies/1, /movies/abc 등과 같은 경로와 모두 일치한다.
router를 콘솔에 찍어보면 query를 찾을 수 있다.
query: {id: '1'}
query에는 id가 있는데 이 id는 바로 URL에 있는 것과 같다.
query 프로퍼티의 이름은 파일명에 쓰인 변수명이랑 동일하다.
2. Router Hook
Link를 사용하지 않고 navigating 하는 다른 방법은 router hook을 사용하는 것이다.
router의 push 옵션이 바로 그 방법이다.
영화 상세 정보를 가져오기 위해 rewrite를 추가한다.
source: "/api/movies/:id",
destination: `https://api.themoviedb.org/3/movie/:id?api_key=${API_KEY}`,
중요한건 source랑 destination에 들어가는 명칭을 동일해야 한다는 점이다.
(source에도 id, destination에도 id)
NextJS의 router 시스템은 router.push를 하거나 Link를 사용하는 것 외에도 URL에 정보를 숨겨 보낼 수 있다.
push를 할 때 URL을 string으로 보낼수도 있지만 객체로 보낼 수도 있다.
URL에서 URL로 state를 넘겨주고 그것을 숨기기 위해 아래와 같이 작성한다.
...
export default function Home({ results }) {
const router = useRouter();
const onClick = (id, title) => {
router.push(
{
pathname: `/movies/${id}`,
query: {
title,
},
},
`/movies/${id}`
);
};
return (
...
{results?.map((movie) => (
<div
onClick={() => onClick(movie.id, movie.original_title)}
className="movie"
key={movie.id}
>
...
as는 필수는 아니지만 브라우저의 URL을 마스킹한다.
router를 콘솔에 찍어보면 query에 데이터가 들어있다.
이 정보를 위 코드처럼 URL을 통해 보낼 수 있다.
<Link
href={{
pathname: `/movies/${movie.id}`,
query: {
title: movie.original_title,
},
}}
as={`/movies/${movie.id}`}
>
<a>{movie.original_title}</a>
</Link>
Link를 사용하는 경우 위와 같이 작성한다.
3. Catch All
catch-all URL은 뭐든 캐치해내는 URL이다.
[...id].js 파일명을 이렇게 작성한다면 "Spider-Man: No Way Home" 제목이 들어간 URL로도 상세페이지에 접근할 수 있다.
router를 콘솔에 찍고 query의 id를 보면 배열 아이템에 제목과 아이디가 들어있다.
router.push(`/movies/${title}/${id}`);
router.query.params가 두 개의 element를 가지는 배열이라는 걸 알았으니 Detail 페이지에서 구조 분해 할당을 사용할 수 있다.
const [title, id] = router.query.params
하지만 시크릿 창에서 접속한다면 에러가 발생한다.
에러가 발생하는 이유는 이 페이지가 서버에서 pre-render 되기 때문이다.
그리고 서버에는 router.query.params가 존재하지 않는다.
const [title, id] = router.query.params || [];
초기에는 빈 배열을 추가해줘서 오류가 발생하지 않도록 해준다.
이건 우리가 client-side rendering만 해준 것이다.
검색엔진은 소스코드 어디에서도 이런 제목을 찾을 수 없다.
getServerSideProps을 사용한다면 request에 대한 정보를 얻을 수 있고, 영화 제목도 얻을 수 있다.
NextJS는 server-side context를 제공해준다.
콘솔에 context를 찍고 페이지를 새로고침하면 서버에 params가 있는 것을 확인할 수 있다.
params: { params: [ 'Spider-Man: No Way Home', '634649' ] }
export async function getServerSideProps() {
const { results } = await (
await fetch(`http://localhost:3000//api/movies`)
).json();
return {
props: {
results,
},
};
}
이 경우에는 단순히 영화제목과 아이디를 얻기 위해 getServerSideProps를 사용했다.
API로 데이터를 fetch를 해오기 위함이 아니라 조금 더 빠르게 데이터를 가져오기 위해서이다.
context 안에서 params를 가져오고 props에 그 params를 넣어줬다.
Detail에서는 prop으로 params를 받아와서 아래에 넣어준다.
const [title, id] = params || [];
이제 새로고침을 하고 소스코드를 들여다보면 백엔드에서 가져온 데이터를 확인할 수 있다.
컴포넌트 내부에서 router를 사용하면 router는 프론트(클라이언트 사이드)에서만 실행이 된다.
만약 URL에 들어있는 영화제목을 사용해서 구글 SEO에 최적화 하고, 유저가 접속하기 전에 탭 제목을 바꾸고 싶고, 기본적으로 페이지를 pre-render 하고 싶다면 server-side에서 정보를 얻기 위한 getServerSideProps를 실행하면 된다.
server-side에서 받아온 정보를 페이지로 넘겨주면 페이지는 그 정보를 받아서 보여준다.
server-side에서 pre-render 된 것이다.
404페이지는 pages 폴더 밑에 404.js 파일을 만들어 주면 된다.
📌 아래 강의의 내용을 정리한 글입니다.