Next.js/Next 13

[Routing] Fundamentals

atmosg 2023. 5. 2. 07:14

1. Terminology

13버전부턴 App Router라는게 새로 생겼는데, 새로운 파일 시스템 기반 라우터로 레이아웃, 중첩 라우팅(nested routing), 로딩 상태, 에러 핸들링 등을 지원하는 서버 컴포넌트 위에 구축되었다. 그럼 실제로 라우팅 모델이 어떻게 된다는건지 핵심 개념부터 짚고 넘어가보자.

 

앞으로 공식문서 전체에 걸쳐서 다음과 같은 용어들을 만날텐데 명확한 정의부터 살펴보고 가자.

  • Tree : 계층 구조를 시각화하기 위한 규칙. 부모/자식 컴포넌트로 이루어진 컴포넌트 트리나 폴더 구조 같은것들이 있다.
  • Subtree(하위트리) : 트리의 일부로, 새로운 루트에서 시작해 잎Leaf에서 끝나는 부분 트리를 말한다.
  • Root(루트) : 트리나 하위 트리의 첫 번째 노드
  • Leaf(잎) : 하위 트리에서 더이상 자식이 없는 노드. URL 경로의 마지막 세그먼트같은 것들이 그 예이다.
  • URL Segement(URL 세그먼트) : 슬래시로 구분되는 URL 경로의 일부
  • URL Path(URL 경로) : 도메인 뒤에 오는 URL의 일부(세그먼트로 구성됨)

 

 

2. app 디렉토리

App Router는 app이라는 새 디렉토리에서 동작한다. app 디렉토리는 pages 디렉토리와 함께 동작시킬 수 있으므로 애플리케이션의 특정 부분만 따서 13버전 방식으로 동작시킬 수 있다.

기본적으로 app 디렉토리내 컴포넌트들은 리액트 서버 컴포넌트로, 성능 최적화와 개발 용이성을 위해 설정되었다. 물론 원한다면 클라이언트 컴포넌트로 쓸 수도 있긴 하다.

 

app 디렉토리내 폴더와 파일들은 단순히 폴더나 파일로서의 역할뿐만 아니라 Next앱의 경로Route 생성에 핵심 역할을 하는데, 각각 다음과 같은 역할을 한다.

  • 폴더 : 경로 정의를 위해 사용된다. 즉, Next 앱에서 경로는 폴더들의 계층 구조를 바탕으로 정의되는데, 루트 폴더에서 page.js 파일을 포함하는 말단 폴더까지의 구조를 이어붙여 경로를 만든다.
  • 파일 : 라우트 세그먼트에 표시되는 UI를 만드는데 사용된다. 자세한건 밑에있는 파일 컨벤션에서 나옴.

 

2.1 Route Segements

경로상의 각 폴더는 곧 라우트 세그먼트가 되는데, 각 세그먼트는 URL 경로상의 세그먼트로 매핑된다.

위 그림에서도 알 수 있듯, 중첩 경로를 만들고 싶다면 app 디렉토리에 폴더들을 네스팅 시켜가며 만들어주기만 하면 된다.

 

2.2 파일 컨벤션

Next 앱의 라우팅에서 특별한 역할을 담당하는 파일들이 있는데, 이 파일들은 네이밍이 정해져있으므로 반드시 따라야한다. 참고로 밑에 파일 확장자를 .js로 써놨지만 당연히 .ts, .jsx, .tsx에도 적용된다.

File Behavior
page.js 특정 경로상에서 표출되고 접근 가능한 고유 UI를 만드는 파일.
┗ route.js 특정 경로의 서버사이드 API 앤드포인트를 만드는 파일.
layout.js 한 세그먼트와 그 자식들에서 공유할 수 있는 UI를 만드는 파일. 이 레이아웃은 한 페이지, 혹은 자식 세그먼트를 래핑한다.
template.js layout.js와 비슷하나 네비게이션에 새로운 컴포넌트 인스턴스가 마운팅된다는 점이 구별된다.
따라서 네비게이션이 필요치 않은이상 layout.js를 쓰는게 좋다
loading.js 한 세그먼트와 그 자식들에서 쓰일 로딩 UI를 만드는 파일.
이 로딩파일은 리액트 Suspense Boundary내 한 페이지, 혹은 그 자식 세그먼트를 래핑하면서 데이터 로드중 로딩 UI를 표출한다.
error.js 한 세그먼트와 그 자식들에서 쓰일 에러 UI를 만드는 파일.
리액트 Error Boundary내 한 페이지, 혹은 그 자식 세그먼트를 래핑하면서 에러 발생시 에러 UI를 표출한다.
┗ global-error.js 루트 layout.js에서 에러가 발생하면 에러 UI를 표출시키는 파일.
not-found.js 적절한 URL로 접속하지 않거나, 특정 경로상에서 notFound를 리턴하는 경우가 발생시 표출시킬 UI를 만드는 파일.

 

위에서 지정된 파일들은 특수한 계층 구조를 이루면서 렌더링되는데, 아래는 구체적인 예시를 나타낸 그림이다. 특히, 표에서도 언급했듯이 error.jsloading.js같은 경우 각각의 바운더리 내에 위치하게 되므로 주의할 것.

 

아래는 네스팅된 중첩 경로로, 부모 세그먼트 안에 자식 컴포넌트가 어떻게 네스팅되는지 보여준다.

 

위에서 지정된 특수한 파일들을 제외하면 나머지 파일들은 한 폴더내에서 자유롭게 위치시킬 수 있다. 예를 들어 CSS나 test파일, 기타 컴포넌트 파일들을 함께 위치시킬 수 있다.

Colocation

 

3. 서버중심 라우팅(with Client-side Navigation)

클라이언트 사이드 라우팅을 사용하는 pages 디렉토리와는 달리, app 디렉토리의 새로운 라우터는 서버중심의 라우팅을 사용한다. 이를 통해 라우트를 서버 컴포넌트와 서버의 데이터 가져오기에 맞춰 조정한다.

 

서버 중심 라우팅을 사용하면 클라이언트는 route map을 다운로드할 필요가 없고, 서버 컴포넌트에 대한 동일 요청을 경로 조회에 사용할 수 있다. 이러한 최적화는 모든 애플리케이션에서 유용하지만, 많은 경로를 가지는 애플리케이션일수록 특히 더 유용하다.

 

물론 라우팅이 서버 중심으로 돌아가긴 하지만, SPA와 동작방식과 유사한 Link 컴포넌트와 함께 클라이언트 사이드 네비게이션을 사용한다. 즉, 유저가 새 경로로 이동할 때 브라우저가 페이지를 새로 고침하지 않는다는 뜻이다. 대신, URL만 업데이트되고 Next.js는 변경된 세그먼트만 새로 렌더링한다.

 

또한, 유저가 앱을 탐색하는 동안 라우트넌 리액트 서버 컴포넌트의 페이로드 결과를 클라이언트측 인메모리 캐시에 저장해둔다. 이 캐시는 라우트 세그먼트별로 분할되어 관리되는데, 어떤 레벨에 위치하건 캐시 무효화를 시킬 수 있고 렌더링 전반에 걸쳐 일관성을 보장한다. 말이 좀 어려운데, 이전에 fetched된 세그먼트의 캐시를 그대로 재사용해 성능을 향상시킬 수도 있는 등 써먹을 구석이 많다는 뜻.

 

3.1 부분 렌더링

형제 경로sibling routes를 탐색할 때, Next.js는 변화가 발생하는 페이지나 레이아웃외 데이터만을 fetch 및 렌더링한다. 예를 들어, /dashboard/settings와 /dashboard/analytics 사이를 탐색할 때, 아래 그림처럼 서로 공유하는 레이아웃은 그대로 두고 변화가 발생한 부분만 리렌더링한다.

 

만약 이러한 부분 렌더링이 없다면 매 탐색마다 전체 페이지를 서버에서 다시 렌더링해야될텐데, 이는 서버에 엄청난 부담이된다. 때문에 부분 렌더링을 사용함으로써 데이터 전송량과 실행 시간 감소 및 성능 향상을 꾀하는 것이다.

 

3.2 고급 라우팅 패턴

App Router에선 고급 라우팅 패턴을 위한 컨벤션도 제공한다. 이 기능들을 사용하면 소규모 팀이나 개인 개발자가 구현하기엔 복잡했던 기능들을 좀뒤 손쉽게 구현할 수 있다. 자세한건 나중에 다룰 예정이니 지금은 간단히 확인만 하고 넘어가기로 하자.

  • Parallel Routes 

    하나의 뷰에서 독립적으로 탐색이 가능한 복수의 페이지를 그려내는 기능.
    이를 통해 자체 하위 탐색을 가진 분할된 뷰를 표출시킬 수 있는데, 대시보드 같은걸 생각하면 편할 것이다.

  • Intercepting Routes

    경로를 가로채서 다른 경로의 컨텍스트상에 표시하는 기능. 현재 페이지의 컨텍스트를 유지하면서 다른 페이지를 본다거나 할 수 있다.