cs

앱 디렉터리와 앱 라우터

ssoonn 2024. 4. 17. 22:47

page directory & page router

page directory

pages 폴더 하위의 모든 폴더, 파일명을 경로로 사용할 수 있음

conponent, lib 등은 pages 폴더 외부에 작성해야 함.

pages/index.tsx → /

pages/about.tsx → /about

pages/content/index.tsx → /content

 

pages/index.ts

웹 애플리케이션의 시작점. 루트(/) 경로페이지

pages/_app.ts

공통 레이아웃, 전역 상태 관리 페이지

프로젝트 최상위 컴포넌트

pages/_documents.ts

SSR시 사용됨. 초기 HTML, CSS 구조 정의

<html>, <body> 등 전체 페이지 구조와 메타태그. 외부스크립트, 글꼴 등 설정

pages router

Next.js 13 이전 Next.js에서 경로를 설정하는 주요 방법

 

 

app directory & app router

app router는 app directory에서 작동한다.

앱 디렉터리는 페이지 디렉터리와 함께 작동하여 점진적인 적용을 허용한다 따라서, 일부 경로를 새로운 route로 선택하며

https://velog.io/@moonworks/Next.js-13-의-app-디렉토리의-주요-특징
https://nextjs.org/blog/layouts-rfc
https://www.smashingmagazine.com/2023/02/understanding-app-directory-architecture-next-js/

 

구성 요소

앱 디렉터리는 페이지별 레이아웃 아키택쳐를 기반으로 함.

  • _app _document 컴포넌트가 루트 layout.jsx 컴포넌트로 대체됨
  • root layout: 전체 애플리케이션을 래핑하는 레이아웃
    • 서버가 전체 앱에 반환하는 HTML을 한번에 조작하는 방법 = 서버 컴포넌트
    • 페이지 이동 시 다시 렌더링되지 않음
      • 레이아웃의 모든 데이터, state는 앱의 라이프사이클 기간동안 유지됨
  • 다른 root component
    • loading.jsx: 전체 라우트의 Suspense 바운더리 정의
    • error.jsx: 전체 라우트의 에러 바운더리 정의
    • template.jsx : 레이아웃과 유사. 모든 페이지 이동 시 다시 렌더링
      • in/out transition과 같은 경로간 상태 처리에 유용
  • 모든 라우트에 대한 page.jsx가 있어야 함
    • url segment에 대해 렌더링할 주요 컴포넌트를 정의
      • url segment: 컴포넌트를 넣는 위치
    • url segment와 정확하게 일치하는 경우에만 DOM에 표시됨

 

데이터 절감을 통한 성능 개선

React 18의 새로운 Server Component 아키텍쳐 지원

app/ 경로에 있는 리액트 컴포넌트는 Server Component*로 동작

 

*React Server Component (RSCs)

*SSR과 서버 컴포넌트는 대체관계가 아님

서버 데이터 fetching을 위한 인터페이스 일원화

fetch API로 통일한 새로운 Data Fetching 방식 지원

  • fetch API 하나로 데이터 fetching, cacheing, revalidate 작업 가능
  • getStaticProps, getServerSideProps, getInitialProps 대체 가능
    • app에서 getStaticProps, getServerSideProps, getInitialProps 미지원
      • fetch로 대체해야
// 임의로 무효화 할 때까지는 요청이 캐싱 (기본값)
// (=`getStaticProps`.)
fetch(URL, { cache: 'force-cache' });

// 모든 요청을 다시 가져옴
// (=`getServerSideProps`)
fetch(URL, { cache: 'no-store' });

// 일정 시간 동안만 캐싱됨
// (=`getStaticProps` with the `revalidate`)
fetch(URL, { next: { revalidate: 10 } });
  • fetch의 확장
    1. React Server Component에서, 중복된 요청을 제거해주는 처리를 자동으로 해주도록 확장
    2. Next.js에서, fetch 요청 시 캐싱과 무효화 옵션을 지정할 수 있도록 확장 
      • force-cache: 기본값. 새로운 match를 찾아 반환
      • no-store no-cache: 모든 요청에 대해 원격 서버에서 페칭
      • next.revalidate: 리소스를 새 리소스로 간주하는 엄격한 임계값을 지정
      • 캐싱을 통해 request 분류
        • 정적 데이터: 더 오래 유지 (블로그 게시글 등)
          • 기본적으로 모든 데이터는 정적 데이터로 간주됨 (force-cache가 기본 캐싱 전략)
        • 동적 데이터: 자주 변경되거나, 사용자 인터랙션의 결과물 (댓글, 장바구니 등)
fetch(`https://route`, { cache: "force-cache", next: { revalidate: 60 } });

Streaming 지원을 통한 더 나은 반응성

더보기
  • Next.js에서 SSR이 동작하는 방식
    • 페이지 요청 시 서버에서 1) Data 수집하여 2) HTML로 만들고 3) 클라이언트로 다운로드하여 뿌려준 후 4) hydration 과정을 통해 interaction이 가능한 페이지로 만듦
    • sequantial and blocking
      • 서버가 모든 데이터를 가져온 후에 페이지의 HTML을 렌더링할 수 있음
      • 클라이언트에서는 페이지의 모든 컴포넌트에 대한 코드가 다운로드된 후에만 hydration 가능
    • 유저에게 non-interactive한 페이지를 빠르게 보여줌
      • 서버에서 모든 data fetching을 완료해야 함 (느린 속도)

Streaming: 페이지의 HTML을 더 작은 청크로 분할 → 클라이언트에 점진적 전송

  • 모든 데이터가 로드될 때까지 기다릴 필요 없이, 페이지의 일부를 더 빠르게 표시할 수 있음
  • 각 컴포넌트를 하나의 chunk로 간주
    • 우선순위가 높거나, 데이터가 필요하지 않는 컴포넌트를 우선 전송할 수 있음
    • react가 더 빨리 hydration 시작할 수 있음
    • 우선순위가 낮은 컴포넌트는 데이터를 가져온 후 server request로 전송할 수 있음
  • 덜 중요한 컨텐츠들을 늦게 내려주고, 대신 로딩 상태를 보여줌
    1. Suspense 기능으로 특정 영역을 Wrapping (react 18)
    <Suspense fallback={<Loading />}>
      <SomeComponent />
    </Suspense>
    
    1. Next.js의 loading.tsx 예약 파일 정의 → app 폴더의 각 컴포넌트별 로딩 상태 보여줄 수 있음
    // 예) app/dashboard/loading.tsx
    export default function Loading() {
      return <p>Loading...</p>
    }
    
  • 긴 data request로 페이지 렌더링이 차단되는 것을 방지
    • TTFB(Time To First Byte), FCP(First Contentful Paint) 줄일 수 있음
    • TTI(Time to Interactive) 개선에 도움

app router의 라우팅 패턴

  • parallel routes
    • 독립적으로 탐색할 수 있는 두개 이상의 페이지를 동시에 표시
      • 자체 sub-navigation이 있으면 split view에 사용할 수 있음 (ex.대시보드)
  • intercepting routes
    • 경로를 가로채서 다른 경로의 컨텍스트에 표시할 수 있음
    • 현재 페이지의 컨텍스트를 유지하는 것이 중요한 경우 사용
      • ex. 하나의 작업을 편집하는 동안 모든 작업 보기, 피드에서 사진을 확장

app router

page router보다 더 많은 유연성과 제어 기능을 제공하는 라우팅 매커니즘

  • page routere
    • file-based routing: pages 디렉터리에 파일을 추가하여 경로 만듦
    • static and dynamic routing: 간단한 명명 규칙으로 정적, 동적 라우팅 모두 지원
    • 자동 코드 분할: 각 페이지의 별도의 번들로 구성, 로드 시간 개선
  • 프로그래밍 방식 라우팅: 매니페스트 파일을 사용해 경로 정의, 명시적 제어
  • 미들웨어 통합: 미들웨어를 완벽하게 지원. 사용자 지정 기능 강화
  • advanced loading strategies: pre-fetching, data fetching 세밀하게 제어

Feature App Router Page Router

File-based routing Uses nested folders to define routes Files directly represent routes
Components Server Components by default Client Components by default
Data fetching fetch function for data fetching getServerSideProps, getStaticProps, getInitialProps
Layouts Layouts can be nested and dynamic Layouts are static
Dynamic routes Supported, but syntax differs Supported
Client-side navigation Supported with router.push Supported with Link component
Priority Takes precedence over Page Router Fallback if no matching route in App Router

1. Routing

nextjs는 파일 시스템을 기반으로 라우팅을 구현합니다.

1-1. built-on

App Router

서버 중심 라우팅

  • react server components를 기반으로 구축되어 있습니다. 가장 핵심적인 변화입니다.
  • 서버 데이터 가져오기에 맞춰져 있습니다.
  • 하지만 경로 이동시 페이지를 다시 렌더링하지 않고, SPA처럼 URL만 업데이트하고 next는 변경된 세그먼트만 렌더링합니다.

Pages Router

클라이언트 중심 라우팅

1-2. location

App Router

  • app 디렉토리를 사용합니다.
  • app 하위에 모든 파일을 함께 구성할 수 있습니다. (colocation)
    • 디렉토리로 경로를 정의합니다.
    • 페이지를 위한 파일은 page.js
    • server-side API를 위한 파일은 route.js
    • 나머지 파일 컨벤션은 여기를 참고
  • pages router 보다 우선순위가 높습니다.
src/app
├─layout.js // root layout. 필수
├─page.js // root page
├─a-page
   └─page.js
└─b-page
  └─page.js
  └─component.js // 라우팅과 관련없는 코드. 라우팅의 대상이 되지 않습니다
    └─b-subpage
    └─page.js

Pages Router

  • pages 디렉토리를 사용합니다.
  • pages 하위에 있는 모든 JS 파일이 페이지/API 경로가 됩니다.
    • 디렉토리, 파일명으로 경로가 설정됩니다.
      • pages/index.js =>/
      • pages/a-page.js => /a-page
      • pages/b-page/index.js => /b-page
      • pages/b-page/b-subpage.js => /b-page/b-subpage
      • pages/b-page/component.js => /b-page/component // 모든 파일이 경로가 되어버림
    • 따라서 component.js 같은 파일은 pages 디렉토리 외부에 위치합니다.
src/pages
├─_app.js // root layout
├─index.js // root page
├─a-page.js
└─b-page
  └─index.js
   └─component.js // 라우팅과 관련없는 코드지만 b-page/component라는 경로가 생깁니다.
   └─b-subpage.js

1-3. Layout

App Router

  • app 디렉토리 내에 root layout을 필수로 포함해야합니다.
  • root layout뿐 아니라 각 layout을 compose 할 수 있습니다.
  • 데이터 패칭 또한 동시에 가능합니다.

Pages Router

  • 전역 공유 layout을 지정하기 위해 _app 을 사용합니다.
    • 단, 여러 layout을 compose 수 없습니다.
    • data fetching과 component를 함께 배치할 수 없습니다.

1-4. Advanced

App Router

  • Parallel Routes : 동일한 레이아웃에서 하나 이상의 페이지를 동시/조건부로 렌더링할 수 있습니다.
  • Intercepting Routes : 현재 페이지의 컨텍스트를 유지하면서 현재 레이아웃 내에서 경로를 로드할 수 있습니다.
    • ex) A 페이지(/feed)에서 모달을 띄울 때, A페이지를 유지하면서 모달의 경로로 url을 변경 (post/13)할 수 있습니다. 이때, A페이지는 모달 뒤에 유지됩니다.

2. Rendering

렌더링은 작성한 코드를 사용자 인터페이스로 변환하는 것입니다

next는 기본적으로 모든 경로의 페이지에 대한 HTML 파일을 사전 렌더링합니다.

브라우저에서 해당 페이지 경로에 접근하면, 사전 렌더링된 HTML과 연결된 최소한의 JS 파일이 전달되고 브라우저에서 JS가 실행되면서 페이지와 완전히 상호작용합니다 (이를 Hydration라고 합니다)

2-1. SSG (static stie generation)

App Router

  • server/client component에 따라 다르게 동작합니다.
    • server component는 렌더링되어 HTML을 생성합니다.
      • 관련 javascript 코드가 클라이언트로 전송되지 않습니다.
    • client component는 HTML 및 JSON을 미리 렌더하고, 서버에 캐싱됩니다.
      • 캐싱 결과는 클라이언트로 전송되어 hydration 됩니다

Pages Router

  • app router의 client component와 동일하게 동작합니다.

2-2. ISR (incremental static regeneration)

App Router

  • 공식문서(app router)에서 언급이 사라졌습니다.

Pages Router

  • SSG는 최초 빌드시에 생성한 정적페이지를 캐싱하고 계속 사용하지만, ISR은 주기적으로 정적페이지를 재생성합니다
    • getStaticProp의 반환값에 revalidate 필드를 추가하면 됩니다

2-3. SSR (server side rendering)

App Router

  • 공식문서(app router)에서 Dynamic Rendering이란 명칭으로 언급됩니다
  • 정적 렌더링 중에 동적기능 / 동적 fetch(), searchParams prop 등이 감지되면 해당 경로를 Dynamic Rendering 대상으로 판단합니다
    • 동적기능 : cookies(), headers() in server component
    • 동적 fetch() : no-store , revalidate : 0 옵션이 있는 fetch

Pages Router

  • 페이지에 접근할 때 마다 필요한 데이터를 가져오고 서버에서 렌더링합니다
  • getServerSIdeProps를 사용합니다

3. Data Fetching

3-1. Method

App Router

  • getServerSideProps, getStaticProps, getInitialProps와 같은 메서드는 더이상 사용하지 않습니다.
  • react server component 기반이기 때문에, 일반적인 방법으로 서버 데이터를 가져옵니다.
    • 서버 데이터베이스 리소스에 직접 접근 가능
    • 민감한 정보 클라이언트에 노출 x
    • 성능향상
    • 빌드(next build)시에 데이터 패칭이 이루어지고, 캐싱됩니다
    • useEffect를 사용하여 데이터를 패칭하고, 상태를 변경하는 방식에서 벗어납니다
  • 클라이언트 측에서 데이터를 가져오는 것도 여전히 가능합니다
  • 요청을 캐싱하고, 중복을 제거합니다 (POST 제외)

Pages Router

  • getinitialProps : 이미 서버에 있는 데이터를 이용해서 서버 사이드 렌더링을 할 떄 사용합니다
  • getServerSIdeProps : 페이지 접근할 때 마다 서버 사이드 렌더링에 필요한 데이터를 가져올 때 사용합니다(최신 데이터가 필요할 때)
  • getStaticProps : next build 시 정적 페이지를 생성할 때 필요한 데이터를 가져올 때 사용합니다

4. Static Export

next는 기본적으로 모든 경로의 페이지에 대한 HTML 파일을 사전 렌더링합니다. SSG (Static-Site-Generation)

공식문서에서는 app router를 사용하는 것을 권장합니다. (static export와 관해 기능이 추가되었습니다.)

4-1. Data Fetching

App Router

  • react server component를 사용하기 때문에 getStaticProps가 필요하지 않습니다. async/await 문법으로 바로 데이터를 가져올 수 있습니다

Pages Router

  • getStaticProps : 정적페이지 생성시 필요한 데이터를 패칭해서 컴포넌트에 전달합니다

4-2. Dynamic Path

App Router

Pages Router

  • getStaticPaths : 동적 경로의 정적페이지를 생성할때 사용합니다.

4-3. Supported Feature

App Router

  • image optimization
    • 구현체가 변경되었습니다
      • 기존(next@12) next/image → next/legacy/image로 변경
      • 기존 next/futrue/image → next/image로 변경됨.
      • next/image를 사용하려면 이미지들에 대한 alt를 모두 넣어주지 않으면 빌드가 되지 않고, px값을 모두 명시적으로 적어줘야한다고 함.
  • Server/Client Component
    • use server, use client 지시문 사용
  • route handlers
    • next build 시 정적 응답 객체 생성 가능
  • Browser APIs
    • Client Component의 경우에 window 객체에 접근한다던가 가능
  • 상세내용

app router

https://www.freecodecamp.org/news/routing-in-nextjs/

app router vs pages router

app router에서 기본적으로 server component가 적용된다.

 

cf. migration

베타 버전 마이그레이션 후기 https://jha-memo.tistory.com/95

useId hydration error https://jha-memo.tistory.com/195

공식문서 https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration