X(구 트위터) 클론 코딩에서 App Router를 사용할 예정이라 이번 기회에 정리하고 넘어가려 한다.
Next.js 13버전에서 beta였던 App Router가 13.4버전에 들어서면서 Stable로 변경 되었다.
Next 공식 도큐먼트에도 Using App Router
와 Using Pages Router
두 버전으로 나눠져 있는 것처럼 큰 차이가 있다고 생각한다.
아래 내용들은 Next App Router 공식문서를 정리하신 분의 글을 참고하여 구글링한 내용들을 더해 나에게 맞게 다시 정리했다.
기존 /pages 파일 시스템 기반의 라우팅 방법에서 /app 디렉토리 구조의 라우팅 방법으로 변화됨에 따라 공유 레이아웃
, 중첩 라우팅
, 로딩 상태
, 에러 핸들링
등 유용한 기능들을 지원한다.
UI를 핸들링할 수 있는 특정 파일명이 있다. 공식문서 참고
page.js
: 경로(route)에 접근할 수 있는 파일이다 ( 12버전에서의 index.js )route.js
: route에 대해 server-side API 엔드포인트를 생성한다.layout.js
: 동일한 폴더 내에 있는 페이지들에 공통 UI를 생성한다.( 이전에는 _app.js을 사용했다. )loading.js
: 동일한 폴더 내에 있는 페이지들에 공통 로딩 UI를 생성한다.error.js
: 동일한 폴더 내에 있는 페이지들에 공통 에러 UI를 생성한다.global-error.js
: root의 layout.js에 대한 에러를 catch한다.not-found.js
: url에 일치하는 route가 없을 때 표시된다.Server Component
로 생성된다.형제 관계인 route 사이에서 페이지 이동 시 이동하는 route의 page와 layout만 리렌더링 한다. 즉, 상위 route의 layout을 포함한 세그먼트들은 유지되어 불필요한 리렌더링을 방지할 수 있다.
loading.js 파일은 콘텐츠가 로드 되는 동안 서버로부터 즉각적인 로딩 상태를 보여주는 파일이다.
<head/>
title 태그나 meta 태그 같은 head 태그 내 HTML 엘리먼트는 내장 SEO 지원을 통해 변경이 가능하다.
Metadata는 레이아웃이나 페이지 파일에서 object나 generateMetadata
함수를 export해서 정의할 수 있다.
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Next.js',
};
export default function Page() {
return '...';
}
이때 head 태그로 직접 선언하면 안되고 Metadata API를 사용해야 streaming 과 head 엘리먼트에 대한 중복제거가 된다고 한다.
SSR은 아래와 같은 프로세스를 지나 사용자에게 보여진다
즉, 모든 데이터가 fetch 되어야 페이지에 대한 HTML을 렌더링 할 수 있다.
Streaming은 각각의 컴포넌트를 작은 chunk 단위로 나누어 점진적으로 전달하는 방식이다.
시간이 오래 걸리는 fetching 작업이 필요없는 컴포넌트 부터 화면에 보여지게 되면서 TTI(Time To Interactive)를 향상시킬 수 있다.
예시) Suspense를 사용해 점진적 렌더링
import { Suspense } from 'react';
import { PostFeed, Weather } from './Components';
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
);
}
error.js
는 route segment와 자식들을 React Error Boundary로 감싸 공통으로 처리할 수 있다.
특정 컴포넌트만 error처리할 수 있기 때문에 full page reload 없이 부분적으로 처리할 수 있다.
또한 error 컴포넌트는 'use client'를 명시해 Client Component로 사용해야한다.
'use client'; // client component
import { useEffect } from 'react';
type Props = {error: Error, reset:void}
export default function Error({error, reset}: Props) {
// reset: 사용자가 recover할 수 있게 error boundary내 콘텐츠를 re-render하는 함수를 제공한다.
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
Try again
</button>
</div>
);
}
코드에서 보는 것 처럼 error.js는 에러가 발생하면 보여지는 fallback Ui이고 layout의 child이기 때문에 동일한 segment 내의 layout의 오류는 catch 할 수 없다.
global 하거나 root의 오류를 핸들링 하기 위해서는 global-error.js를 사용해주면 된다.
참조
Defining Routes
Pages and Layouts
Route Group
Loading and streaming
Error Handling
@khy226
@asdf99245