Routing
Router
:
을 사용해주어야 한다.[]
로 감싸주면 된다.🤔 만약
pages
와src/pages
폴더가 모두 존재한다면?
pages
폴더에 작성한 코드가 우선적으로 적용된다.src/pages
폴더 내부의 코드는 무시된다.👩🏫 Next.js에서 생성한 페이지 파일은 어떻게 라우팅 될까요?
pages/about.js
pages/index.js
➡️ /
pages/posts/index.js
➡️ /posts
pages/posts/firstPost.js
➡️ /posts/firstPost
pages/board/user/userId.js
➡️ /board/user/userId
를 사용하여 동적 라우팅을 해줄 수 있다./posts/[postId].js
/posts/postId
로 설정하는 대신 postId를 placeholder로 인식하고, 해당 컴포넌트에 접근하여 여러 데이터를 불러올 수 있다.posts/list.js
가 존재하는 상황에서 위의 동적 라우팅의 postId 값에 list를 넣을 경우 원래 존재하던 posts/list.js
파일로 이동한다.pages/[userId]/modifyInfo.js
➡️ /:userId/modifyInfo (/foo/modifyInfo)
pages/board/[slug].js
➡️ /board/:slug (board/firstPost)
pages/posts/[...all].js
➡️ /posts/* (/posts/2023/title/user)
👩🏫 위와 같은 라우팅을 구현하기 위해서는 어떤 방식을 사용해야 할까요?
next/link
👩🏫 페이지를 완전히 새로고침하므로 아래의 <Link> 태그를 사용하는 것이 좋습니다!
👩🏫 Next.js는 Link 컴포넌트를 "Client-Side Navigation"이라고 설명합니다.
Client-Side Navigation
Code Splitting
href (필수)
// 사전 정의된 경로: /about?name=test
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
// 동적 경로: /blog/my-post
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: 'my-post' },
}}
>
as
passHref
prefetch
prefetch={false}
전달 시 비활성화시킬 수 있다.replace
scroll
shallow
getStaticProps
, getServerSideProps
, getInitialProps
를 실행하지 않고 현재 페이지의 경로만을 업데이트한다.locale
useRouter
와 withRouter
가 반환하는 값이다.pathname (string)
/pages
뒤에 오는 파일명을 의미한다.query (object)
asPath (string)
isFallback (boolean)
basePath (string)
next.config.js
에서 설정할 수 있다.module.exports = {
basePath: "/docs",
};
/docs
가 붙게 된다./user
➡️ /docs/user
locale (string)
locales (string[])
isReady (boolean)
isPreview (boolean)
next/router
: useRouter
import { useRouter } from "next/router";
export default function Home() {
const router = useRouter();
}
router
객체에 접근할 수 있게 해주는 훅 함수useLocation
, useHistory
의 기능을 떠올리면 이해하기 쉽다.useHistory (useNavigate)
의 기능과 유사하다.router.push(url, as, options);
url (필수!)
<button
type="button"
onClick={() => {
router.push({
pathname: "/post/[pid]",
query: { pid: post.id },
});
}}
>
Click me
</button>
as
options
Option | type | 기본값 | 설명 |
---|---|---|---|
scroll | boolean | true | 페이지 이동 후 상단 스크롤 |
shallow | boolean | false | getStaticProps, getServerSideProps, getInitialProps 실행 없이 현재 페이지 경로 업데이트 |
locale | string | 새 페이지의 locale |
⚠️ 외부 URL로의 이동 시 router.push 보다 window.location을 사용하는 것이 더 적합하다!
router.replace(url, as, options)
<button type="button" onClick={() => router.replace("/home")}>
Click me
</button>
next/link
가 없는 탐색에서 유용한 기능이다.router.prefetch(url, as);
const handleSubmit = useCallback((e) => {
e.preventDefault();
fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
}).then((res) => {
if (res.ok) router.push("/dashboard");
});
}, []);
useEffect(() => {
// Prefetch the dashboard page
router.prefetch("/dashboard");
}, []);
router.beforePopState(callbackFunc);
useEffect(() => {
router.beforePopState(({ url, as, options }) => {
// 오직 아래의 두 경로만 허용하고 싶을 때
if (as !== "/" && as !== "/other") {
// 다른 주소인 경우 SSR 렌더링이 404 상태를 갖게 한다.
window.location.href = as;
return false;
}
return true;
});
}, []);
<button type="button" onClick={() => router.back()}>
Click here to go back
</button>
window.history.back()
을 실행한다.<button type="button" onClick={() => router.reload()}>
Click here to reload the page
</button>
window.history.reload()
을 실행한다.
reouteChangeStart(url, {shallow})
useEffect(() => {
// 이벤트 구독
const handleRouteChange = (url, { shallow }) => {
console.log(
`App is changing to ${url} ${
shallow ? "with" : "without"
} shallow routing`
);
};
router.events.on("routeChangeStart", handleRouteChange);
// 컴포넌트가 마운트되지 않은 경우 off 메서드로 구독을 취소한다.
return () => {
router.events.off("routeChangeStart", handleRouteChange);
};
}, []);
routeChangeComplente(url, {shallow})
routeChangeError(err, url, {shallow})
useEffect(() => {
const handleRouteChangeError = (err, url) => {
if (err.cancelled) {
console.log(`Route to ${url} was cancelled!`);
}
};
router.events.on("routeChangeError", handleRouteChangeError);
// 컴포넌트가 언마운트 되면 구독을 취소한다.
return () => {
router.events.off("routeChangeError", handleRouteChangeError);
};
}, []);
beforeHistoryChange(url, {shallow})
hashChangeStart(url, {shallow})
hashChangeComplete(url, {shallow})
👩🏫
useRouter은 React Hook이므로 클래스 컴포넌트에서는 사용이 불가능합니다.
그렇다면 클래스 컴포넌트에서는 어떻게 router 객체에 접근할 수 있을까요?
next/router
: withRouter
import { withRouter } from "next/router";
function Page({ router }) {
return <p>{router.pathname}</p>;
}
export default withRouter(Page);
[slug]
부분에 query 값을 담아 보내면 params로 전달된다.⚠️ 주의할 점
🤠 as props를 추가해서 문제를 방지해줍시다!
import Link from "next/link";
function Home() {
return (
<ul>
<li>
<Link
// /about?name=test
href={{
pathname: "/about",
query: { name: "test" },
}}
as={`/about/question/${post.name}`}
>
<a>About us</a>
</Link>
</li>
<li>
<Link
// /blog/my-post
href={{
pathname: "/blog/[slug]",
query: { slug: "my-post" },
}}
>
<a>Blog Post</a>
</Link>
</li>
</ul>
);
}
export default Home;
[slug]
부분에 query 값을 담아 보내면 params로 전달된다.import { useRouter } from "next/router";
export default function ReadMore({ post }) {
const router = useRouter();
return (
<button
type="button"
onClick={() => {
router.push(
{
pathname: "/post/[pid]",
query: { pid: post.id },
},
"/order"
);
}}
>
Click here to read more
</button>
);
}
query
키 값을 사용하면 URL으로 전송된 query 데이터를 받아올 수 있다.import { useRouter } from 'next/router'
export default () => {
const router = useRouter()
const {pid} = router.query
...
}
useSearchParams
로 query-string 받아오기// client-component에서 사용할 수 있다.
"use client";
import { useSearchParams } from "next/navigation";
export default function SearchBar() {
const searchParams = useSearchParams();
const search = searchParams.get("search");
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
return <>Search: {search}</>;
}
URLSearchParams.get(searchParameter)
URLSearchParams.has(searchParameter)
page
컴포넌트에서 params, query-string 받아오기// app/blog/[slug]/page.tsx
export default function Page({
params,
searchParams,
}: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}) {
return <h1>My Page</h1>
}
page
의 props를 사용하여 params와 query-string을 받아올 수 있다.params (optional)
예시 | URL | params |
---|---|---|
app/post-list/[slug]/page.js | /post-list/1 | { slug: '1' } |
app/post-list/[user]/[date]/post.js | /post-list/Jane/0822/1 | { user: "Jane", date: "0822" } |
app/post-list/[...slug]/page.js | /post-list/1/2/3 | { slug: ['1', '2', '3'] } |
searchParams (optional)
URL | searchParams |
---|---|
/post?user=Jane | {user: "Jane"} |
/post?user=Jane&date=0822 | {user: "Jane", date: "0822"} |
/post?id=1&id=2 | {id: ["1", "2"]} |