Next.js 13 App Router 살펴보기
React Essentials

하나의 페이지를 구상할 때, non-interactive한 부분은 서버 컴포넌트로 서버에서 렌더링한다. interactive한 부분은 클라이언트 컴포넌트를 이용한다. 이것이 Next.js의 server-first approach다.
Server Components
-
서버 컴포넌트 활용시의 이점
-
data fetching 로직을 서버로 이전할 수 있다. 이는 client - server간 네트워크 워터폴을 줄인다.
-
DB 등의 서버 인프라를 활용할 수 있다.
-
서비스에 필요한 패키지를 브라우저까지 가져올 필요가 없게 된다. 일부 패키지는 서버에서만 사용하여 클라이언트에 전송되는 자바스크립트 번들 사이즈를 획기적으로 줄일 수 있다. 이는 브라우저의 초기 로딩 시간 감축에 큰 도움이 된다.
-
Client Components
-
Next에서 클라이언트 컴포넌트는 서비스에 인터랙션을 추가하는 역할이다.
-
Next에서 클라이언트 컴포넌트는 서버에서 pre-render되고 클라이언트에서 hydration된다.
-
기존 Pages 라우터에서의 컴포넌트 동작 방식이 바로 클라이언트 컴포넌트다.
-
use client지시문을 통해 클라이언트 컴포넌트임을 명시할 수 있다. app 라우팅에서 default는 서버 컴포넌트다.
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}-
서버 컴포넌트가 default이기 때문에,
use client지시문이 없는 모든 컴포넌트는Server Component module graph의 일부가 된다. -
use client지시문이 포함된 클라이언트 컴포넌트는, 해당 파일에 포함된 모든 imported 모듈(자식 컴포넌트에 정의된 것들까지)을client bundle의 일부로 여긴다.
언제 어떤 컴포넌트를 활용해야 하는가?
- 클라이언트 컴포넌트의 사용이 필요하겠다 싶은 생각이 들기 전에는 서버 컴포넌트를 사용하라.

Routing
(번역) Next.js의 app 디렉터리 아키텍처 이해하기
Next 12의 pages 라우트

Next 13의 App 라우트


-
App 디렉터리는 Next.js에서 라우트를 처리하고 뷰를 렌더링하기 위한 새로운 전략이다. 이 전략은 리액트 Concurrent 기능을 최대한 활용할 수 있도록 고안되었다. (Suspense)
-
각 폴더는
route segement를 나타낸다. route segement는 URL path의 segement와 매핑된다.
컴포넌트 계층
- 특정 route segement에 정의되어 있는
special files는 계층적으로 렌더링된다.
layout.jstemplate.jserror.js(React error boundary)loading.js(React suspense boundary)not-found.js(React error boundary)page.jsor nestedlayout.js

- /dashboard/settings 구조의 nested route는 아래와 같은 계층이 형성된다

co-location
-
route segement에는 위에서 언급한 special files 외에 own files(components, styles, test 등)이 위치할 수 있다.
-
당연하게도 이것들은 URL path에 포함되지 않는다

page
-
페이지는 rotue에 unique한 UI다. route에 page.js파일을 정의함으로써 route를 접근 가능하게 개방한다.
-
page.js는 항상 route subtree의 leaf다.

Page, layout 컴포넌트는 각각 page, layout 파일로부터 default export되어야 한다.
layout 컴포넌트는 반드시 children 프로퍼티를 포함해야 한다.
layout
-
레이아웃은 multiple pages에 공유되는 UI다.
-
Navigation에 있어 레이아웃은 상태를 보존하고 interactive를 유지하며 리렌더링되지 않는다.
-
루트 레이아웃 : app 폴더에 정의된 layout.js. 루트 레이아웃은 반드시 정의되어야 하며 애플리케이션의 모든 라우트 페이지에 적용된다.
-
루트 레이아웃은 pages 라우팅의 _app.js, _document.js를 대체한다
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
- 특정 폴더에 정의된 레이아웃은 해당 route segement에 적용된다. 또한 중첩되어서 Children 프로퍼티를 통해 자식 레이아웃음 감싼다.
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return <section>{children}</section>;
}
template
-
템플릿은 자식 레이아웃, 페이지를 감싼다는 점에서 레이아웃과 유사하지만 레이아웃처럼 navigation간 상태를 유지하지는 못한다. 템플릿은 navigation 시 새 인스턴스를 생성한다.
-
즉, 템플릿 navigation 시 컴포넌트의 새 인스턴스가 마운트되고, DOM 요소가 다시 생성되며, 상태가 보존되지 않고, 효과가 다시 동기화된다.
-
따라서 경우에 따라 layout이 아닌 template을 선택해야 할 때가 있다.
-
useState, useEffect에 의존하는 경우
-
CSS를 활용한 Enter/Exit 애니메이션 또는 애니메이션 라이브러리를 사용할 경우
-
loading
- special file인
loading.js는 React Suspense와 활용되어 유의미한 Loading UI를 만들어낸다.

// app/dashboard/page.tsx
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 Handling
-
error.jsspecial file은 nested routes에서 런타임 에러를 우아하게 처리하는 방법을 제공한다. -
error.js 파일은 자동으로 route segement(page.js)와 nested children을 React Error Boundary로 감싼다.
-
앱의 나머지 기능은 유지하면서 에러가 발생한 세그먼트만 격리한다.
-
전체 페이지를 새로고침하지 않고 에러 복구를 시도하는 기능을 추가한다.

- 에러바운더리가 중첩된 컴포넌트 예시

