시작
$ npx create-next-app next-tutorial --typescript
pages/index.tsx의 내용을 보면 react-helmet와 같이 Head를 통해서 헤더를 관리할 수 있다. (이걸 사용해서 페이지별로 문서의 타이틀도 정할 수 있고 검색엔진 최적화에도 도움이 될 수 있을 것 같음)
// pages/index.tsx
import type { NextPage } from 'next';
import Head from 'next/head';
const Home: NextPage = () => {
return (
<div>
<Head>
<title>타이틀 지정</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<p>hello world</p>
</main>
</div>
)
}
export default Home
react에서 라우팅 처리를 위해 react-router-dom을 추가적으로 설치했다면 next.js는 추가적인 설치 없이 pages라는 디렉토리에 기반하여 라우팅을 할 수 있다.
이제 pages 디렉토리 하위에 search.tsx를 생성하고나면 다음과 같이 url로 페이지 이동이 가능한 것을 확인할 수 있다.
// pages/search.tsx
import Head from "next/head";
const Search = () => {
return (
<div>
<Head>
<title>검색 페이지</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<p>Search</p>
</div>
);
}
export default Search;
처음에 파일명을 Search.tsx 대문자로 하였을 때는 url을 쳤을 때 자동으로 대문자에서 소문자로 리다이렉트되어 404 에러가 발생하였다.
https://nextjs.org/learn/basics/navigate-between-pages/pages-in-nextjs
에서도 파일명은 소문자로 시작하는 걸 보면 지켜줘야하는 약속인 것 같다.
react에서도 외부페이지로 이동하지 않는 한 react-router-dom에서 제공해주는 기능으로 어플리케이션 내부에서 페이지 이동을 한 것처럼 next.js에서는 Link라는 컴포넌트를 사용해서 페이지간의 이동을 한다.
메인에서도 사용하고 검색에서도 사용하기 때문에 컴포넌트로 따로 만들어주고
// components/Navigation.tsx
import Link from "next/link";
const Navigation = () => {
return (
<header>
<ul>
<li>
<Link href='/'>
<a>Main</a>
</Link>
</li>
<li>
<Link href='/search'>
<a>Search</a>
</Link>
</li>
</ul>
</header>
);
}
export default Navigation;
메인페이지와 검색페이지에 Navigation 컴포넌트를 불러와주면 다음과 같이 페이지간에 리로드 없이 이동이 가능해진다.
// pages/search.tsx
const Search = () => {
return (
<div>
<Head>
// 생략...
</Head>
<Navigation />
// 생략...
</div>
);
}
그리고 추가적으로 pages내의 파일들은 빌드 과정에서 분할되는 코드 스플리팅을 자동적으로 지원해준다고 한다.
Each file inside your pages/ directory will be code split into its own JavaScript bundle during the build process.
https://nextjs.org/docs/migrating/from-react-router#code-splitting
확인해보니 메인페이지에서는 검색페이지와 관련된 코드를 불러오지 않고 메인페이지에 필요한 것만 불러오는 것을 확인할 수 있었다. (페이지를 추가적으로 작성하고나면 서버를 껐다켜야 정상적으로 작동했다.)
url이 상황에 따라 변화는 다이나믹 라우팅 처리 또한 pages 디렉토리 하위에서 처리해주면 된다.
우선, 페이지 이동을 위해 만들어두었던 navigation 컴포넌트에 추가적인 코드를 작성해둔다.
// components/Navigation.tsx
const Navigation = () => {
return (
<header>
<ul>
// 생략...
<li>
<Link href="/post/first">
<a>First Post</a>
</Link>
</li>
<li>
<Link href="/post/second">
<a>Second Post</a>
</Link>
</li>
</ul>
</header>
);
}
이제 pages 디렉토리 하위에 post라는 디렉토리를 만들고 그 하위에 [id].tsx
파일을 생성한다.
import { useRouter } from "next/router";
const Post = () => {
const router = useRouter();
const { id } = router.query;
return (
<div>
<p>Post: {id}</p>
</div>
);
}
export default Post;
이렇게 작성하고 나면 /post/1, /post/abc, /post/first 와 같은 경로는 pages/post/[id].tsx에 매치되어 query object에 결과가 나오게 된다.
/post/second
{ "id": "second" }
쿼리 스트링을 사용한 케이스도 query object에 담기게 된다.
/post/abc?foo=bar
{ "foo": "bar", "id": "abc" }
다음은 네비게이션의 Second Post를 클릭했을 때 나오는 결과이다.
그렇다면 /post만 하였을 때는 어떻게 될까?
당연하게도 404에러가 발생하며 해결하려면 /pages/post에 index.tsx를 작성해주면 된다.
non-dynamic route는 dynamic route보다 우선시 되기 때문이다.
// pages/post/index.tsx
const Index = () => {
return (
<div>
<p>Default 페이지</p>
</div>
);
}
export default Index;