라이브러리&프레임워크/Next.js

[Next.js] Routing

youngble 2022. 11. 18. 01:03

Router

Next.js의 Router는 file-system 기반

File-system?

파일을 만들면 해당 파일이름으로 라우터로 즉각적으로 인지하여 주소와 docking이 된다.

pages/

src/pages/

 

만약 src/pages 도 있고 pages/  경로도 있다면 어느게 우선순위가 높고 어떤걸로 인지하는가?

만약 pages/ 가 있다면 이것을 우선순위로 인지하여 실행되고 아래 src/pages는 인지하지않는다. 


Nested routes

pages/products/first-item.js => /product/first-item

pages/settings/my/info.js => /settings/my/info

 

이때 경로 depth  길어질수록, import 할때 상대경로의 …/../ 등 일정하지않고 길어지는 경우가 생기기 때문에 절대경로를 설정해준다.

jsconfig.json 파일을 만들고

{
  "compilerOptions": { "baseUrl": "src" }
}

을 입력


Dynamic Routes

pages/category/[slug].js => /category/:slug (ex: /category/food)

pages/[username]/info.js => /:username/info (ex: /jimmy/info)

그런데 위에 username 어떤걸 넣어도 info.js 경로로 이동하게 되는데 만약 그 자리에 category를 입력한다면 category/[slug]경로로 갈지 아니면 /category/info 갈 것인가?

정답은 명시되어있는 파라미터 경로로 이동한다. 따라서 /categoru/[slug] 이동한다 이때 [slug] 값은 info 넣어줬을테니 /category/info 되고 아래와같이 보인다.

브라우저 실행화면

[…] 을 사용

무한한 파라미터 경로 depth를 가질수 있다

pages/cart/[…slug].js => /cart/* (ext: /cart/2022/06/04)

 

 

 

만약 쿼리에 똑같이 slug라는 쿼리로 만들어주면 어떤걸로 인식할까?

파라미터로 명시되어있는 것이 우선순위로 인식하여 값을 얻는다

 

Ex) category/[slug].js 라는 파일과 경로인데  category/sports?from=event&age=123&slug=food 라고 맨뒤에 slug=food 라고 써도 처음 파라미터로 온 sports가 slug 값으로 인식됨

만약 그럼 query로 만든 from을 맨뒤에 중복해서 다른값을 넣어준다고하면 어떻게될까?

category/sports?from=event&age=123&slug=food&from=hello 라고 했을때

from은 하나의 string이 아닌 배열 array로 담긴다. from :[“event”, ”hello”]


다중 슬러그

pages/[username]/[info].js 의 폴더/파일 구조일때

const {username, info} = router.query 로 쓰고

pages/cart/[…slug].js 의 폴더/파일 구조일때

const {slug} = router.query 라고 쓰고

 

뒤에오는 파라미터들을 array로 만들어서 가진다. Ex) /cart/hello/my/name/is/youngble =>  Slug = [“hello”, “my”, “name”, “is”, “youngble”]

 

옵셔널 slug

근데 만약에 /cart 라고만 치면 slug 값이 없기때문에 404 error page가 뜨는데 이때 index.js없이도 가능하게 할려면 […slug].js[[…slug]].js 라고하면 다른 파라미터 경로가 없어도 된다. 

 

<Link>를 통해서 페이지 이동도 가능하지만 Link말고도 router.push()메서드를 사용해서도 가능하다. 만약 버튼을 만들고 /cart/2022/06/24 로 이동하고싶다면 다음과 같이 쓰면된다.

   <button onClick={() => router.push('/cart/2022/06/24')}>
        2022년 6월 24일로
      </button>

 

Shallow Routing

Data fetching 메서드(getServerSideProps, getStaticProps, getInitialProps)다시 실행없이 URL을 바꿀수 있게해준다.

getServerSideProps/ getStaticProps 등을 다시 실행시키지 않고 현재 상태를 잃지 않고 url 바꾸는 방법

 

상태는 유지하면서 URL 만 바꾸고 싶은 경우?

사용자가 어떤 동작을 했고, 그 기록을 query로 남기고 싶을때 * query로 남기면 사용자가 새로고침을 해도 유지됨

Data Fetching을 일으키고 싶지 않다면?

 

url을 바꾸는 3가지 방법

  • Location.replace(“url”): 로컬 state 유지안됨(리렌더)
  • router.push(url): 로컬 state 유지/ data fetching은 일어남
  • router.push(url, as, {shallow: true }): 로컬 state 유지/ data fetching X.

테스트를 위해

Settings/my/info.js 에서 getServerSideProps 함수를 만들어서 console.log을 찍어보고 MyInfo 컴포넌트 안에 useStateuseRouter를 사용하여 쿼리 status값이 위의 3가지의 경우 어떤식으로

바뀌는지 테스트 해보면 1번째 location.replace는 state도 유지가 안되고 server data fetching 호출도 다시 일어나고, 2번째 경우는 state는 유지, data fetching 요청은 다시 일어나고 3번째는 둘다 유지된다.

단, query가 바뀌는게 아닌 url의 파라미터가 바뀐다거나 하면 이에 해당하지 않고 유지되지 않고 리렌더링 되는건 당연하다.

따라서 query 바뀐다면 state data fetching 유지  있다.

import Layout from 'components/Layout';
import SubLayout from 'components/SubLayout';
import { useRouter } from 'next/router';
import { useState } from 'react';

export async function getServerSideProps() {
  console.log('server');
  return {
    props: {}, // 형식에 맞ㅝㅓ props 라는 것으로 내보내야함
  };
}

export default function MyInfo() {
  const router = useRouter();
  const [clicked, setClicked] = useState(false);
  const { status = 'initial' } = router.query;
  return (
    <>
      <h1 className="title">MyInfo </h1>
      <h1 className="title">Clicked {String(clicked)}</h1>
      <h1 className="title">Status {status}</h1>
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          location.replace('/settings/my/info?status=editing');
        }}
      >
        edit(replace)
      </button>
      <br />
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/settings/my/info?status=editing');
        }}
      >
        edit(push)
      </button>
      <br />
      <button
        onClick={() => {
          alert('edit');
          setClicked(true);
          router.push('/', undefined, {
            shallow: true,
          });
        }}
      >
        edit(shaloow)
      </button>
    </>
  );
}

MyInfo.getLayout = function getLayout(page) {
  return (
    <Layout>
      <SubLayout>{page}</SubLayout>
    </Layout>
  );
};