Next.js Routing & 폴더구조 & Layout

이수빈·2023년 8월 31일
1

Next.js

목록 보기
1/15
post-thumbnail
  • page Router 기준 공부내용

  • pages 폴더안에 모든것을 정의해야 인식함.

  • 기본 폴더구조

project-root
├── pages
│ ├── index.js // 홈 페이지
│ ├── about.js // About 페이지
│ ├── blog
│ │ ├── index.js // 블로그 메인 페이지
│ │ ├── [postId].js // 블로그 개별 포스트 페이지
│ │ └── latest.js // 최신 블로그 포스트 페이지
│ └── contact.js // Contact 페이지
├── components // 재사용 가능한 컴포넌트들
├── styles // 전역 스타일과 스타일 유틸리티
└── public // 정적 파일들 (이미지, 폰트 등)

Routing Convention

  • 폴더이름, 파일이름이 라우팅경로이기 때문에 정해진 Convention에 따라 사용해야함.

  • pages 디렉토리 내에 생성된 파일 이름은 해당 파일이 나타내는 페이지의 URL 경로와 일치함.

  • 예를 들어, pages/about.js 파일은 /about 경로에 해당하는 페이지가 된다.

  • pages 디렉토리 내의 서브 디렉토리 구조도 URL 경로에 영향을 미친다.

  • 예를 들어, pages/blog/[slug].js 파일은 /blog/:slug 경로에 해당하는 페이지가 됨. slug는 경로 매개변수를 나타낸다.

  • 각 폴더마다 index.tsx가 존재해야함(홈페이지)

Special

  • page directory 최상단에 위치

  • app에는 공통 컴포넌트들을 정의함.

  • _app .js .jsx .tsx 맞춤형 앱
  • _document .js .jsx .tsx 맞춤 문서
  • _error .js .jsx .tsx 사용자 정의 오류 페이지
  • 404 .js .jsx .tsx 404 오류 페이지 => 최상단에 정의해야 인식함.
  • 500 .js .jsx .tsx 500 오류 페이지

Dynamic Routing

  • 동적 Routing을 구현할 때 => 폴더이름을 중괄호로 하거나 , 파일이름을 중괄호로 감싸서 동적 Routing을 구현 할 수 있음.
  • 폴더 규칙
    [folder]/index .js .jsx .tsx 동적 경로 세그먼트
    [...folder]/index .js .jsx .tsx 포괄 경로 구간
    [[...folder]]/index .js .jsx .tsx 선택적 포괄 경로 구간
  • 파일 규칙
    [file] .js .jsx .tsx 동적 경로 세그먼트
    [...file] .js .jsx .tsx 포괄 경로 구간
    [[...file]] .js .jsx .tsx 선택적 포괄 경로 구간

선택적 포괄경로

  • [...slug].js 파일은 /blog/*와 같은 경로에 매핑됩니다. 여기서 ...slug는 선택적 포괄경로를 나타내며, 여러 경로 요소를 캡처하는 배열로 표현됨.
// pages/blog/[[...slug]].js
import { useRouter } from 'next/router';

const BlogPost = () => {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <div>
      <h1>Blog Post: {slug.join('/')}</h1>
    </div>
  );
};

export default BlogPost;

포괄경로

  • 포괄경로는 선택적 포괄경로와 비슷하지만 경로 요소가 반드시 존재해야 한다는 차이가 있음.

  • [...slug].js 파일은 /categories/*와 같은 경로에 매핑됨

// pages/categories/[...slug].js
import { useRouter } from 'next/router';

const Category = () => {
  const router = useRouter();
  const { slug } = router.query;

  return (
    <div>
      <h1>Category: {slug.join('/')}</h1>
    </div>
  );
};

export default Category;

Layout

  • page 최상단에 _app.tsx에는 공통 layout을 정의할 수 있음.
import Layout from "@/components/Layout";
import "@/styles/globals.css";
import type { AppProps } from "next/app";

/*앱 */

export default function App({ Component, pageProps }: AppProps) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}
  • 만약 App에서 페이지가 공용 layout과 다른 layout을 갖거나, Nested Layout이 필요한 구조라면, page 계층에 getLayout 속성을 따로 정의 가능함

  • 먼저 다른 layout을 정의할 페이지 계층에 getLayout 속성을 정의함.

  • getLayout은 React.Element를 파라미터로 받아서 다른 Layout 구조를 감싸서 return함.

// /todolist/index
TodoList.getLayout = function getLayout(page: ReactElement) {
  return (
    <Layout2>
      <div className="text-6xl">Nested Layout is Applied</div>
      {page}
    </Layout2>
  );
};
  • _app.tsx에서 layout에 따라 getLayout 프로퍼티가 있는 구조라면, currying을 이용해 React Element를 감싸서 반환함.

  • 먼저 NextPageWithLayout라는 새로운 type을 정의함. 이는 NextPage type에 getLayout Prop을 확장한 형태임. optional property로 지정해서 getLayout 프로퍼티가 없는 경우도 고려함.

  • 이를 이용해 AppPropsWithLayout을 정의 => &연산자를 통해 Component 프로퍼티의 값을 덮어씌움

  • TypeScript에서 & 연산자를 사용한 타입 확장은 두 개 이상의 타입을 병합하여 새로운 타입을 생성하는 기능을 제공함. 이 연산자를 사용하면 여러 타입의 프로퍼티와 메서드를 하나의 타입으로 합칠 수 있음.

  • 또한, & 연산자를 이용해 두 개의 타입을 병합할 때, 같은 프로퍼티가 존재하는 경우 후술된 타입의 프로퍼티가 우선적으로 사용됨.

  • null 병합 연산자 사용 => getLayout이 있다면, page계층에서 정의한 함수가 없다면 받은 페이지를 받아서 반환하는 커링함수를 반환.

  • getLayout 유무에 따라 다른 Layout을 적용함.

// _app.tsx
type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode;
}; // ?로 optional로 지정함.

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
}; //

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page);
  //null병합 연산자 => null or undefined가 아니라면 a이고 나머지는 b

  console.log("getLayout", getLayout);

  /*없다면 page를 그대로 반환하는 함수가 반환됨. */
  /* getLayout이 있다면 => Curing 형식으로 Page계층에서 정의한 함수가 반환됨. ReactElement를 감싸서 반환 */

  if (!Component.getLayout) {
    return (
      <Layout>
        <Component {...pageProps} />
      </Layout>
    );
  }

  return getLayout(<Component {...pageProps} />);
}
  • Layout에서 data Fetching시 useEffect나 SWR를 사용해 data를 가져옴. page directory 바깥에 있기 때문에 getStaticProps or getServerSideProps 사용불가능.

ref) Next.js 공식문서 : https://nextjs.org/docs/pages/building-your-application/routing

profile
응애 나 애기 개발자

0개의 댓글