본문 바로가기

Frontend/Next.js

Next.js를 배워보자-2

페이지 & 파일 기반 라우팅

React 앱 상에서는 라우팅을 사용하기 위해서는 라우트에 관련된 다양한 컴포넌트를 react-router-dom으로부터 추가한다.

 

하지만 Next.js는 React 라우터를 설치하지 않고, 라우트나 페이지 구조를 정의하는 데에 JSX나 js 코드를 전혀 사용하지 않는다.

Next.js에서는 React 컴포넌트 파일을 생성한다.

이후, 설정된 폴더 구조로부터 프로젝트의 라우트를 도출해 내도록 만든다.

Next.js는 pages 폴더를 자동으로 확인해서 라우트 구조를 도출해 낸다.

 

pages 내부 폴더 구조가 다음과 같다고 해보자.

pages
│  about.js => my-domain.com/about
│  index.js => my-domain.com/
│
└─products
   index.js => my-domain.com/products
   [id].js => my-domain.com/products/1...

index.js로 정의하게 되면 Next.js에서 이 파일 명은 경로로 인식하지 않고 index.js의 컴포넌트를 주 컴포넌트로 불러오기 때문에 주소 뒤에 아무것도 추가로 붙지 않는다.

[id]와 같이 파일 명을 지정하면 동적 경로를 추가하는 데 사용한다는 뜻이다.

 

하지만, 동적 페이지 파일보다 이미 정의된 정적 라우팅 페이지 파일을 우선한다는 점을 주의해야 한다.

예를 들어, products 폴더 내부에 list.js라는 파일과 [id].js라는 파일이 있을 때 products/ 뒤 어떤 것을 적어도 [id].js 내부 컴포넌트가 렌더링될 테지만, products/list라고 적는 경우에는 list.js 내부 컴포넌트가 렌더링될 것이다.

 

my-domain.com/about의 경우, pages 폴더 내부 about 폴더를 생성하고, 그 안에 index.js를 만들어줘도 된다.

 

이번에는 동적 경로의 세그먼트 데이터를 추출해보자.

사용자가 URL에 입력한 구체적인 값을 가져와보자는 것이다.

 

이때, next/routeruseRouter 훅을 사용해보면 된다.

import { useRouter } from "next/router";

const PortfolioProjectPage = () => {
  const router = useRouter();

  console.log(router.pathname);  // /portfolio/[projectId]
  console.log(router.query);  // {projectId: 'hello'}

  return (
    <div>
      <h1>The Portfolio Project Page</h1>
    </div>
  );
};

export default PortfolioProjectPage;

router.pathname을 입력해보면 URL이 뜨는 게 아니라 Next.js에 의해 추론된 경로가 나온다.

즉, 해당 페이지 출력을 위한 컴포넌트 파일 경로인 것이다.

 

router.query에서는 query라는 라우터 프로퍼티로 인해 URL에 부호화된 구체적인 데이터에 접근할 수 있다.

즉, 동적 경로 세그먼트에 대한 구체적인 값에 액세스할 수 있는 것이다.

 

그래서, 위에서는 router.query.projectId를 입력하면 원하는 값을 도출해낼 수 있는 것이다.

 

그리고 아래와 같이 동적 폴더도 생성할 수 있다.

// [clientProjectId].js
import { useRouter } from "next/router";

const SelectedClientProjectPage = () => {
  const router = useRouter();

  console.log(router.query);  // {id: 'person1', clientProjectId: 'project1'}

  return (
    <div>
      <h1>The Project Page for a Specific Project for a Selected Client</h1>
    </div>
  );
};

export default SelectedClientProjectPage;

 

이번에는 모든 라우트를 한 번에 확보하는 방식으로 해보자.

이는 Catch-All 라우트를 사용하면 되는데, 파일 이름으로 중괄호와 식별자만 작성하지 않고 구문이나 특수한 표기법을 추가할 수 있다.

[…id].js와 같이 파일 명을 작성하면 된다.

// […id].js
import { useRouter } from "next/router";

const BlogPostsPage = () => {
  const router = useRouter();

  console.log(router.query);

  return (
    <div>
      <h1>The Blog Posts</h1>
    </div>
  );
};

export default BlogPostsPage;

http://localhost:3000/blog/2020/12/blog1라고 URL에 입력하고 router.query를 출력해보면 배열이 나온다.

URL 안에서 /로 구분해서 나타낸다.

 

이 값들은 데이터베이스에 요청을 보낼 때 사용하거나 2020년 12월에 올린 블로그 게시물을 필터링하는 경우에 사용할 수도 있다.

 

이제 Link에 대해서 알아보자.

a 태그로도 구현할 수 있긴 한데, a 태그를 사용하면 React 앱을 실행할 때 갖는 state가 유지되지 않는다.

따라서 next/linkLink 태그를 사용해보자.

import Link from "next/link";
import styles from "../styles/Home.module.css";

const HomePage = () => {
  return (
    <div className={styles.container}>
      <h1>The Home Page</h1>
      <ul>
        <li>
          <Link replace href="/portfolio">
            Portfolio
          </Link>
        </li>
        <li>
          <Link href="/clients">Clients</Link>
        </li>
      </ul>
    </div>
  );
};

export default HomePage;

Link 태그에 기능을 추가해볼 수 있는데, 링크에 마우스를 갖다대면 미리 이동하는 페이지의 데이터를 페칭하는 방법이 있다.

이는 나중에 추가해보자.

 

Link 태그에 replace 프로퍼티를 설정하면 새로운 페이지를 또 띄우지 않고 현재 페이지에 화면을 띄운다.

이 경우 뒤로 가기를 못하게 한다.

 

다음과 같이 코드를 작성하면 동적 페이지로 접근할 수 있게 된다.

import Link from "next/link";

const ClientsPage = () => {
  const clients = [
    { id: "max", name: "Maximilian" },
    { id: "manu", name: "Manual" },
  ];

  return (
    <div>
      <h1>The Clients Page</h1>
      <ul>
        {clients.map((client) => (
          <li key={client.id}>
            <Link href={`/clients/${client.id}`}>{client.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ClientsPage;

 

아래와 같이 변경해도 동일하다.

import Link from "next/link";

const ClientsPage = () => {
  const clients = [
    { id: "max", name: "Maximilian" },
    { id: "manu", name: "Manual" },
  ];

  return (
    <div>
      <h1>The Clients Page</h1>
      <ul>
        {clients.map((client) => (
          <li key={client.id}>
            <Link
              href={{
                pathname: "/clients/[id]",
                query: { id: client.id },
              }}
            >
              {client.name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default ClientsPage;

 

Link 태그를 안 쓰고도 다음과 같이 나타낼 수도 있다.

그리고 router.replace()를 쓰게 되면 뒤로 가기를 막은 채 해당 페이지로 내비게이트한다.

import { useRouter } from "next/router";

const ClientProjectsPage = () => {
  const router = useRouter();

  function loadProjectHandler() {
    // load data ...
    router.push("/clients/max/projectA");
  }

  return (
    <div>
      <h1>The Projects of a Given Client</h1>
      <button onClick={loadProjectHandler}>Load Project A</button>
    </div>
  );
};

export default ClientProjectsPage;
import { useRouter } from "next/router";

const ClientProjectsPage = () => {
  const router = useRouter();

  function loadProjectHandler() {
    // load data ...
    router.push({
      pathname: "/clients/[id]/[clientProjectId]",
      query: { id: "max", clientProjectId: "projectA" },
    });
  }

  return (
    <div>
      <h1>The Projects of a Given Client</h1>
      <button onClick={loadProjectHandler}>Load Project A</button>
    </div>
  );
};

export default ClientProjectsPage;

 

커스텀 404 페이지도 만들어 볼 수 있는데, pages 폴더 바로 하위에 404.js를 만들어주면 된다.

 

그리고 pages 폴더 내부 _app.js 파일도 있는데, 루트 컴포넌트로서 여러 페이지 컴포넌트가 렌더링되는 곳이다.

그래서 헤더나 레이아웃과 같이 공통으로 들어가야 하는 부분은 여기에 추가해주는게 좋다.

import { Fragment } from "react";
import MainHeader from "./MainHeader";

const Layout = (props) => {
  return (
    <Fragment>
      <MainHeader />
      <main>{props.children}</main>
    </Fragment>
  );
};

export default Layout;
import Layout from "../components/layout/Layout";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

export default MyApp;

'Frontend > Next.js' 카테고리의 다른 글

Next.js를 배워보자-4  (0) 2023.07.31
Next.js를 배워보자-3  (0) 2023.07.31
Next.js를 배워보자-1  (0) 2023.07.31