본문 바로가기
웹 프로그래밍/Next.js

[유데미 | winterlood 이정환] 프로젝트로 배우는 React.js & Next.js 마스터리 클래스 강의 내용 정리 (섹션 13: 페이지 라우터)

by 청량리 물냉면 2024. 1. 21.
반응형

섹션 13: Next.js 페이지 라우터

페이지 라우터

정적 라우팅. 요청 폴더 아래 index.js가 수행됨
url 파라미터에 대응하는 동적 라우팅
대응하는 페이지가 없는 경우 404 에러 반환

 

 

_app.js / _document.js

  • Next 앱에서 _app.js와 _document.js는 page의 역할을 하는 컴포넌트는 아니다.
  • 대신 Next 앱에 반드시 존재해야 할 필수 컴포넌트를 정의하는 파일.
  • 모든 페이지 or 모든 컴포넌트에 적용되어야 하는 공통 로직이나 데이터를 다루는 컴포넌트.

 

_app.js

import '@/styles/globals.css'

export default function App({ Component, pageProps }) {
  return <Component {...pageProps} />
}
  • _app.js는 리액트의 App.jsx와 유사한 역할을 수행한다. (모든 페이지의 root 컴포넌트 역할)
  • 모든 페이지의 컴포넌트는 _app에 자식 컴포넌트로 배치되어 화면에 렌더링 된다.
  • { Component, ...} : 현재 경로에 맞는 페이지 역할을 하는 컴포넌트를 받아, 해당 컴포넌트를 자식 컴포넌트로 렌더링 한다.

about 페이지를 만들어서 접속해 보기

http://localhost:3000/about 접속 결과는 아래와 같다. 

 

_documet.js

import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}
  • _document.js는 모든 페이지에서 공통적으로 적용되어야 하는 html 태그들을 관리한다.
  • 리액트 앱의 index.html과 유사한 역할을 수행.
  • 전체 페이지의 메타 태그 설정, 폰트 불러오기, character set 설정 등의 역할을 맡는다.

 

페이지 라우터 실습

pages 폴더 아래 search 폴더를 만들고, 안에 index.js 파일을 만든다.

파일에는 다음과 같이 코드를 작성해 보았다.

export default function Search() {
  return <div>Search Page</div>;
}

이후 http://localhost:3000/search 로 접속하니, search 폴더의 index.js 파일이 실행된 것을 확인할 수 있다.

 

쿼리스트링을 이용해 특정 국가의 정보를 동적으로 라우팅 하기 위해서는 아래와 같은 경로로 이동해야 한다.

http://localhost:3000/country/KOR

위의 쿼리를 가져오기 위해서는 아래와 같이 폴더를 구성해야 한다.

 

[code].js

import { useRouter } from "next/router";

export default function Country() {
  return <div>Country</div>;
}

라우터 객체의 query 파라미터로 전달된 국가 코드 값이 담겨 있다. 

이 값을 꺼내어 컴포넌트 내에서 적절하게 사용하기 위해서는 useRouter 훅이 필요하다. 

useRouter 훅은 next가 제공하는 커스텀 훅으로써, router 객체를 반환한다. 라우팅에 필요한 대부분의 정보가 저장되어 있다. 

 

 

[code].js

import { useRouter } from "next/router";

export default function Country() {
  const router = useRouter();
  // const code = router.query.code; //URL파라미터로 전달된 국가코드 꺼내오기
  const { code } = router.query; //구조분해 할당

  return <div>Country {code}</div>;
}

만약 http://localhost:3000/country/KOR/2024 와 같이 뒤에 '/'가 더 붙는 경로에 대응하는 컴포넌트가 필요하다면, [code].js 대신 [...code].js (Catch-all Segments)를 사용한다. [관련 공식 문서]

 

또한 http://localhost:3000/country 에도 대응 가능하게 하기 위해서는 [[...code]].js 를 사용한다. 대괄호로 두 번 감싸서, 괄호 내부의 [...code] 부분이 optional 함을 알려줌으로써 기본 경로에서도 해당 컴포넌트를 불러오게 할 수 있다.

 

 

페이지 이동하기

Link

  • Next.js에서 기본으로 제공하는 기능
  • CSR 방식 이용: 브라우저에서 서버에 별도 요청 없이 브라우저 자체에서 필요한 컴포넌트만 교체하는 방식
  • React Router의 Link 컴포넌트와 거의 동일하게 동작한다.

 

정적 이동

import Link from "next/link";

export default function Home() {
  return (
    <div>
      Home page
      <div>
        <Link href={"/search"}>Search Page 이동</Link>
      </div>
    </div>
  );
}

 

동적 이동

      <div>
        <Link href={`/country/${code}`}>{code} 페이지로 이동</Link>
      </div>

 

조금 더 구조화된 방식 - url 오브젝트 사용

      <div>
        <Link href={{ pathname: "/country/[code]", query: { code: code } }}>
          {code} 페이지로 이동
        </Link>
      </div>
  • pathname: 이동하고자 하는 페이지의 경로, 해당 페이지를 작성한 컴포넌트의 경로
  • query: pathname의 [ ] 경로 내부에 query 값을 담아서 보내줌

 

버튼 클릭 시 페이지 이동  - useRouter() 사용

import Link from "next/link";
import { useRouter } from "next/router";

export default function Home() {
  const code = "KOR";

  const router = useRouter();

  const onClickButton = () => {
    // router.push("/search");
    router.push({ pathname: "/country/[code]", query: { code: code } }); // object 사용도 가능
  };

  return (
    <div>
      Home page
      <div>
        <button onClick={onClickButton}>Search 페이지로 이동</button>
      </div>
     ...
    </div>
  );
}

 

+추가) router 객체에 사용 가능한 추가적인 메서드

  • replace: 뒤로 가기 방지하면서 이동
  • back: 뒤로 가기
  • reload: 새로고침 발생시키기

 

 

전체 페이지 레이아웃 설정하기

_app.js

  • 모든 컴포넌트의 부모역할
  • 모든 페이지에 공통적으로 적용될 레이아웃을 설정해 주는 컴포넌트

 

Layout.js

import style from "./Layout.module.css";

export default function Layout({ children }) {
  return (
    <div>
      <header className={style.header}>NARAS🌍</header>
      <main className={style.main}>{children}</main>
    </div>
  );
}

 

_app.js

import Layout from "@/components/Layout";
import "@/styles/globals.css";

export default function App({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

 

Layout 컴포넌트로 _app.js를 감싸서 전체 컴포넌트 배치를 관리해 준다.

 

 

개별 페이지 레이아웃 설정하기

SubLayout.js

import style from "./SubLayout.module.css";

export default function SubLayout({ children }) {
  return (
    <div className="SubLayout">
      <div>{children}</div>
      <footer className={style.footer}>footer</footer>
    </div>
  );
}

일부 페이지에만 SubLayout을 적용하고 싶을 때, 아래와 같이 설정해 준다.

 

search > index.js

import SubLayout from "@/components/SubLayout";

export default function Search() {
  return <div>Search Page</div>;
}

Search.Layout = SubLayout;
//Search: 객체(자바스크립트의 모든 함수는 객체이므로)
//객체에 Layout이라는 프로퍼티를 추가하고 그 값으로 SubLayout 객체를 저장

subLayout 적용을 위한 컴포넌트들만 아래에 

Search.Layout = SubLayout;

객체 할당을 해서 레이아웃을 적용해 준다. 위 코드는 Search함수의 Layout 프로퍼티에 SubLayout 객체 값을 저장해 준다는 의미이다.

 

_app.js

import Layout from "@/components/Layout";
import "@/styles/globals.css";

export default function App({ Component, pageProps }) {
  //console.log(Component.Layout);
  //Component가 Search 컴포넌트라면 SubLayout 값이 호출된다.

  const EmptyLayout = ({ children }) => <>{children}</>;
  //컴포넌트에 붙어있는 레이아웃이 있다면 그걸 사용하고 아니라면 그냥 children을 감싸주기만 하는 빈 레이아웃 적용
  const SubLayout = Component.Layout || EmptyLayout;

  return (
    <Layout>
      <SubLayout>
        <Component {...pageProps} />
      </SubLayout>
    </Layout>
  );
}

 

 

 

출처

https://kmooc.udemy.com/course/react-next-master/learn/lecture/39610746

반응형