앱 라우터 점진적 적용 가이드
- Published on
- 페이지 라우터에서 앱 라우터로 기존의 Next.js 애플리케이션을 업그레이드하는 방법을 배워보세요.
Table of Contents
이 가이드는 다음을 도와줍니다.
- Next.js 애플리케이션을 버전 12에서 버전 13으로 업데이트
pages
및app
디렉터리에서 작동하는 기능을 업그레이드- 기존 애플리케이션을
pages
에서app
으로 점진적으로 마이그레이션
업그레이딩
Node.js 버전
최소 Node.js 버전은 이제 v16.14 입니다. Node.js 문서에서 자세한 내용을 확인하세요.
Next.js 버전
Next.js 버전 13으로 업데이트하려면 선호하는 패키지 매니저를 사용하여 다음 명령을 실행하세요.
npm install next@latest react@latest react-dom@latest
ESLint 버전
ESLint를 사용하는 경우 ESLint 버전을 업그레이드해야 합니다.
npm install -D eslint-config-next@latest
ESLint 변경 사항이 적용되려면 VS Code의 ESLint 서버를 재시작해야 할 수 있습니다. 명령 팔레트 (
Mac에서 cmd+shift+p
;Windows에서 ctrl+shift+p
)를 열고ESLint: ESLint 서버 재시작
을 검색하세요.
다음 단계
업데이트한 후 다음 섹션을 참조하여 다음 단계를 확인하세요.
- 새로운 기능 업그레이드: 개선된 이미지 및 링크 컴포넌트와 같은 새로운 기능으로 업그레이드하는데 도움을 주는 가이드.
pages
에서app
디렉터리로 마이그레이션:pages
에서app
디렉터리로 점진적으로 마이그레이션하는데 도움을 주는 단계별 가이드.
새로운 기능 업그레이드
Next.js 13은 새로운 기능과 규칙을 가진 새로운 앱 라우터를 도입했습니다. 새로운 라우터는 app
디렉터리에서 사용할 수 있으며 pages
디렉터리와 함께 존재합니다.
Next.js 13으로 업그레이드하는 것은 새로운 앱 라우터를 사용하는 것을 요구하지 않습니다. 두 디렉터리에서 모두 작동하는 새로운 기능, 예를 들어 업데이트된 이미지 컴포넌트, 링크 컴포넌트, 스크립트 컴포넌트, 폰트 최적화와 같은 것을 계속 사용할 수 있습니다.
<Image/>
컴포넌트
Next.js 12는 임시 임포트인 next/future/image
와 함께 이미지 컴포넌트에 새로운 개선 사항을 도입했습니다. 이러한 개선 사항에는 클라이언트 측 자바스크립트 감소, 이미지 확장 및 스타일링을 더 쉽게 하는 방법, 더 나은 접근성, 기본 브라우저 지연 로딩이 포함되었습니다.
버전 13에서는 이 새로운 동작이 이제 next/image
에 대한 기본값이 되었습니다.
새로운 이미지 컴포넌트로 마이그레이션하는데 도움이 되는 두 가지 코드 모드가 있습니다.
next-image-to-legacy-image
코드모드: 안전하게 및 자동으로next/image
임포트를next/legacy/image
로 이름을 변경합니다. 기존 컴포넌트는 동일한 동작을 유지합니다.next-image-experimental
코드모드: 위험하게 인라인 스타일을 추가하고 사용되지 않는 속성을 제거합니다. 기존 컴포넌트의 동작이 새로운 기본값과 일치하도록 변경됩니다. 이 코드모드를 사용하려면 먼저next-image-to-legacy-image
코드모드를 실행해야 합니다.
<Link>
컴포넌트
<Link>
컴포넌트는 이제 자식으로 <a>
태그를 수동으로 추가하는 것을 필요로 하지 않습니다. 이 동작은 버전 12.2에서 실험적 옵션으로 추가되었으며 이제 기본값입니다. Next.js 13에서는 <Link>
가 항상 <a>
를 렌더링하며 기본 태그에 대한 속성을 전달할 수 있습니다.
import Link from 'next/link'
// Next.js 12: `<a>`가 중첩되어 있지 않으면 제외됩니다.
<Link href="/about">
<a>About</a>
</Link>
// Next.js 13: `<Link>`는 항상 내부적으로 `<a>`를 렌더링합니다.
<Link href="/about">
About
</Link>
Next.js 13으로 링크를 업그레이드하려면 new-link
코드모드를 사용할 수 있습니다.
<Script>
컴포넌트
next/script
의 동작이 업데이트되어 pages
및 app
모두를 지원하게 되었습니다. 그러나 원활한 마이그레이션을 위해 몇 가지 변경이 필요합니다.
- 이전에
_document.js
에 포함했던beforeInteractive
스크립트를 루트 레이아웃 파일(app/layout.tsx
)로 이동하세요. - 실험적인
worker
전략은app
에서 아직 작동하지 않으며, 이 전략으로 표시된 스크립트는 제거하거나 다른 전략(예:lazyOnload
)을 사용하여 수정해야 합니다. onLoad
,onReady
,onError
핸들러는 서버 컴포넌트에서 작동하지 않으므로 클라이언트 컴포넌트로 이동하거나 완전히 제거해야 합니다.
폰트 최적화
이전에는 Next.js가 폰트 CSS 인라인을 통해 폰트를 최적화하는 데 도움을 주었습니다. 버전 13은 성능과 프라이버시를 보장하면서 폰트 로딩 경험을 사용자 정의할 수 있는 새로운 next/font
모듈을 도입했습니다. next/font
는 pages
와 app
디렉토리 모두에서 지원됩니다.
pages
에서는 CSS 인라인이 여전히 작동하지만 app
에서는 작동하지 않습니다. 대신 next/font
를 사용해야 합니다. next/font
의 사용 방법은 폰트 최적화 페이지에서 알아볼 수 있습니다.
pages
에서 app
으로 마이그레이션
🎥 시청하기: 앱 라우터를 점진적으로 채택하는 방법을 배워보세요 → YouTube (16분).
앱 라우터로 이동하는 것은 리액트의 서버 컴포넌트, Suspense 등 Next.js가 기반으로 하는 기능을 처음 사용하는 경우일 수 있습니다. 특별한 파일 및 레이아웃과 같은 새로운 Next.js 기능과 결합할 때, 마이그레이션은 새로운 개념, 사고 모델 및 행동 변경을 익혀야 합니다.
이러한 업데이트의 복잡성을 줄이기 위해 마이그레이션을 더 작은 단계로 나누는 것이 좋습니다. app
디렉토리는 pages
디렉토리와 동시에 작동하도록 의도적으로 설계되었으므로 페이지별로 점진적으로 마이그레이션 할 수 있습니다.
app
디렉토리는 중첩된 루트와 레이아웃을 모두 지원합니다. 자세히 알아보기.- 중첩된 폴더를 사용하여 루트를 정의하고 특별한
page.js
파일을 사용하여 루트 세그먼트를 공개적으로 접근 가능하게 만듭니다.
app
디렉토리 생성
1단계: 최신 Next.js 버전으로 업데이트하세요. (13.4 이상 필요)
npm install next@latest
그런 다음 프로젝트의 루트(또는 src/
디렉토리)에 새로운 app
디렉토리를 생성하세요.
2단계: 루트 레이아웃 생성
app
디렉토리 내에 새로운 app/layout.tsx
파일을 생성합니다. 이것은 app
내의 모든 루트에 적용되는 루트 레이아웃입니다.
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
JavaScript
export default function RootLayout({
// Layouts must accept a children prop.
// This will be populated with nested layouts or pages
children,
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
app
디렉토리는 반드시 루트 레이아웃을 포함해야 합니다. 루트 레이아웃은 <html>
, <body>
태그를 정의해야 합니다. Next.js는 자동으로 이들을 생성하지 않습니다.
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
JavaScript
export const metadata = {
title: 'Home',
description: 'Welcome to Next.js',
}
_document.js
및 _app.js
마이그레이션
기존의 _app
또는 _document
파일이 있다면 그 내용을 루트 레이아웃 (app/layout.tsx
)으로 복사할 수 있습니다. app/layout.tsx
의 스타일은 pages/*
에는 적용되지 않습니다. 마이그레이션 중에 pages/*
라우트가 중단되는 것을 방지하기 위해 _app
/_document
를 유지해야 합니다. 완전히 마이그레이션 된 후에 안전하게 삭제할 수 있습니다.
리액트 Context 제공자를 사용하고 있다면, 이들은 클라이언트 컴포넌트로 이동해야 합니다.
getLayout()
패턴을 레이아웃으로 마이그레이션 (선택사항)
Next.js는 pages
디렉터리에서 페이지별 레이아웃을 달성하기 위해 페이지 컴포넌트에 속성을 추가하는 것을 권장했습니다. 이 패턴은 app
디렉터리에서 중첩 레이아웃을 자연스럽게 지원하는 것으로 대체될 수 있습니다.
next/head
마이그레이션
단계 3: pages
디렉터리에서는 next/head
리액트 컴포넌트를 사용하여 title
및 meta
와 같은 <head>
HTML 요소를 관리합니다. app
디렉터리에서는 next/head
가 새로운 내장 SEO 지원으로 대체됩니다.
변경 전:
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>나의 페이지 제목</title>
</Head>
</>
)
}
JavaScript
import Head from 'next/head'
export default function Page() {
return (
<>
<Head>
<title>나의 페이지 제목</title>
</Head>
</>
)
}
변경 후:
import { Metadata } from 'next'
export const metadata: Metadata = {
title: '나의 페이지 제목',
}
export default function Page() {
return '...'
}
JavaScript
export const metadata = {
title: '나의 페이지 제목',
}
export default function Page() {
return '...'
}
단계 4: 페이지 마이그레이션
app
디렉터리의 페이지들은 기본적으로 서버 컴포넌트입니다. 이는pages
디렉터리의 페이지들이 클라이언트 컴포넌트인 것과는 다릅니다.app
에서의 데이터 가져오기 방식이 변경되었습니다.getServerSideProps
,getStaticProps
및getInitialProps
는 더 간단한 API로 대체되었습니다.app
디렉터리는 루트를 정의하기 위해 중첩 폴더를 사용하며, 루트 세그먼트를 공개적으로 접근 가능하게 하는 특별한page.js
파일을 사용합니다.pages
디렉터리app
디렉터리루트 index.js
page.js
/
about.js
about/page.js
/about
blog/[slug].js
blog/[slug]/page.js
/blog/post-1
페이지의 마이그레이션을 두 가지 주요 단계로 나누어 진행할 것을 권장합니다.
- 단계 1: 기본으로 내보낸 페이지 컴포넌트를 새 클라이언트 컴포넌트로 이동합니다.
- 단계 2: 새 클라이언트 컴포넌트를
app
디렉터리 내의 새page.js
파일로 가져옵니다.
알아두면 좋은 점: 이 방법은
pages
디렉터리와 가장 유사한 동작을 가지기 때문에 가장 쉬운 마이그레이션 경로입니다.
단계 1: 새로운 클라이언트 컴포넌트 생성
app
디렉터리 내에 새로운 파일(예:app/home-page.tsx
또는 유사한 이름)을 만들어 클라이언트 컴포넌트를 내보냅니다. 클라이언트 컴포넌트를 정의하려면 파일 상단(모든 가져오기 이전)에'use client'
지시문을 추가합니다.- 기본 내보내기된 페이지 컴포넌트를
pages/index.js
에서app/home-page.tsx
로 이동합니다.
'use client'
// 이것은 클라이언트 컴포넌트입니다. 데이터를 props로 받아오며
// `pages` 디렉터리의 페이지 컴포넌트와 마찬가지로 상태와 효과에 접근할 수 있습니다.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
JavaScript
'use client'
// 이것은 클라이언트 컴포넌트입니다. 데이터를 props로 받아오며
// `pages` 디렉터리의 페이지 컴포넌트와 마찬가지로 상태와 효과에 접근할 수 있습니다.
export default function HomePage({ recentPosts }) {
return (
<div>
{recentPosts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
)
}
단계 2: 새 페이지 생성
app
디렉터리 내에 새app/page.tsx
파일을 생성합니다. 이것은 기본적으로 서버 컴포넌트입니다.home-page.tsx
클라이언트 컴포넌트를 페이지로 가져옵니다.pages/index.js
에서 데이터를 가져오고 있었다면, 새로운 데이터 가져오기 API를 사용하여 데이터 가져오기 로직을 직접 서버 컴포넌트로 이동시킵니다. 자세한 내용은 데이터 가져오기 업그레이드 가이드를 참고하세요.app/page.tsx// 클라이언트 컴포넌트를 가져옵니다. import HomePage from './home-page' async function getPosts() { const res = await fetch('https://...') const posts = await res.json() return posts } export default async function Page() { // 서버 컴포넌트 내에서 직접 데이터를 가져옵니다. const recentPosts = await getPosts() // 가져온 데이터를 클라이언트 컴포넌트로 전달합니다. return <HomePage recentPosts={recentPosts} /> }
이전 페이지에서
useRouter
를 사용하였다면, 새로운 라우팅 후크로 업데이트해야 합니다. 자세히 알아보기.개발 서버를 시작하고
http://localhost:3000
을 방문합니다. 이제 app 디렉터리를 통해 제공되는 기존 인덱스 루트를 볼 수 있어야 합니다.
단계 5: 라우팅 후크 마이그레이션
새로운 라우터가 app
디렉터리의 새로운 동작을 지원하기 위해 추가되었습니다.
app
에서는 next/navigation
에서 가져온 세 개의 새 후크를 사용해야 합니다. useRouter()
, usePathname()
, 그리고 useSearchParams()
.
- 새로운
useRouter
후크는next/navigation
에서 가져와지며,next/router
에서 가져온pages
의useRouter
후크와는 다른 동작을 합니다.next/router
에서 가져온useRouter
후크는app
디렉터리에서는 지원되지 않지만,pages
디렉터리에서는 계속 사용할 수 있습니다.
- 새로운
useRouter
는pathname
문자열을 반환하지 않습니다. 대신 별도의usePathname
후크를 사용합니다. - 새로운
useRouter
는query
객체를 반환하지 않습니다. 대신 별도의useSearchParams
후크를 사용합니다. - 페이지 변경사항을 감지하기 위해
useSearchParams
와usePathname
을 함께 사용할 수 있습니다. 자세한 내용은 라우터 이벤트 섹션을 참고하세요. - 이 새로운 후크들은 클라이언트 컴포넌트에서만 지원됩니다. 서버 컴포넌트에서는 사용할 수 없습니다.
'use client'
import { useRouter, usePathname, useSearchParams } \
from 'next/navigation'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// ...
}
더불어 새로운 useRouter
후크에는 다음과 같은 변경사항이 있습니다.
isFallback
은fallback
이 대체되었기 때문에 제거되었습니다.locale
,locales
,defaultLocales
,domainLocales
값들은app
디렉터리에서 더 이상 필요하지 않은 내장 i18n Next.js 기능들 때문에 제거되었습니다. i18n에 대해 자세히 알아보기.basePath
가 제거되었습니다. 대체 방법은useRouter
의 일부가 아닙니다. 아직 구현되지 않았습니다.asPath
가 제거되었습니다. 왜냐하면 새로운 라우터에서as
개념이 제거되었기 때문입니다.isReady
는 더 이상 필요하지 않기 때문에 제거되었습니다. 정적 렌더링 중에useSearchParams()
후크를 사용하는 모든 컴포넌트는 프리렌더링 단계를 건너뛰고 대신 런타임에서 클라이언트에 렌더링됩니다.
단계 6: 데이터 가져오기 방법 마이그레이션
pages
디렉터리는 페이지의 데이터를 가져오기 위해 getServerSideProps
와 getStaticProps
를 사용합니다. app
디렉터리 내에서 이전의 데이터 가져오기 함수들은 fetch()
와 async
리액트 서버 컴포넌트 위에 구축된 더 간단한 API로 대체됩니다.
export default async function Page() {
// 이 요청은 수동으로 무효화될 때까지 캐시되어야 합니다.
// `getStaticProps`와 유사합니다.
// `force-cache`는 기본값이며 생략할 수 있습니다.
const staticData = await fetch(`https://...`,
{ cache: 'force-cache' })
// 이 요청은 모든 요청마다 다시 가져와야 합니다.
// `getServerSideProps`와 유사합니다.
const dynamicData = await fetch(`https://...`,
{ cache: 'no-store' })
// 이 요청은 10초 동안 캐시되어야 합니다.
// `revalidate` 옵션을 가진 `getStaticProps`와 유사합니다.
const revalidatedData = await fetch(`https://...`, {
next: { revalidate: 10 },
})
return <div>...</div>
}
getServerSideProps
)
서버 사이드 렌더링 (pages
디렉터리에서 getServerSideProps
는 서버에서 데이터를 가져와 파일 내 기본으로 내보낸 리액트 컴포넌트에 props를 전달하는데 사용됩니다. 페이지의 초기 HTML은 서버에서 사전 렌더링되고, 이후 브라우저에서 페이지를 "hydrate"합니다(상호 작용 가능하게 만듭니다).
// `pages` 디렉터리
export async function getServerSideProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
app
디렉터리에서는 서버 컴포넌트를 사용하여 리액트 컴포넌트 내부에 데이터 가져오기를 함께 위치시킬 수 있습니다. 이렇게하면 클라이언트로 보내는 자바스크립트 양을 줄이면서 서버에서 렌더링된 HTML을 유지할 수 있습니다.
cache
옵션을 no-store
로 설정함으로써 가져온 데이터가 저장되지 않아야 함을 나타낼 수 있습니다. 이는 pages
디렉터리의 getServerSideProps
와 유사합니다.
// `app` 디렉터리
// 이 함수는 어떠한 이름으로든 지정할 수 있습니다.
async function getProjects() {
const res = await fetch(`https://...`, { cache: 'no-store' })
const projects = await res.json()
return projects
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((project) => (
<li key={project.id}>{project.name}</li>
))}
</ul>
)
}
요청 객체에 접근하기
pages
디렉터리에서는 Node.js HTTP API를 기반으로 요청 기반 데이터를 검색할 수 있습니다.
예를 들어, getServerSideProps
에서 req
객체를 검색하고 요청의 쿠키와 헤더를 검색하는 데 사용할 수 있습니다.
// `pages` 디렉터리
export async function getServerSideProps({ req, query }) {
const authHeader = req.getHeaders()['authorization'];
const theme = req.cookies['theme'];
return { props: { ... }}
}
export default function Page(props) {
return ...
}
app
디렉터리는 요청 데이터를 검색하기 위한 새로운 읽기 전용 함수를 제공합니다.
headers()
: Web Headers API를 기반으로 하며 서버 컴포넌트 내에서 요청 헤더를 검색하는 데 사용할 수 있습니다.cookies()
: Web Cookies API를 기반으로 하며 서버 컴포넌트 내에서 쿠키를 검색하는 데 사용할 수 있습니다.
// `app` 디렉터리
import { cookies, headers } from 'next/headers'
async function getData() {
const authHeader = headers().get('authorization')
return '...'
}
export default async function Page() {
// 서버 컴포넌트 내에서 `cookies()`나 `headers()`를
// 직접 사용하거나 데이터 가져오기 함수에서 사용할 수 있습니다.
const theme = cookies().get('theme')
const data = await getData()
return '...'
}
getStaticProps
)
정적 사이트 생성 (pages
디렉터리에서 getStaticProps
함수는 빌드 시간에 페이지를 사전 렌더링하는 데 사용됩니다. 이 함수는 외부 API나 직접 데이터베이스에서 데이터를 가져와 빌드 중 페이지 전체에 이 데이터를 전달하는 데 사용할 수 있습니다.
// `pages` 디렉터리
export async function getStaticProps() {
const res = await fetch(`https://...`)
const projects = await res.json()
return { props: { projects } }
}
export default function Index({ projects }) {
return projects.map((project) => <div>{project.name}</div>)
}
app
디렉터리에서 fetch()
로 데이터를 가져올 때 기본적으로 cache: 'force-cache'
가 되며, 이는 요청 데이터를 수동으로 무효화될 때까지 캐시하게 됩니다. 이것은 pages
디렉터리의 getStaticProps
와 유사합니다.
// `app` 디렉터리
// 이 함수는 어떠한 이름으로든 지정할 수 있습니다.
async function getProjects() {
const res = await fetch(`https://...`)
const projects = await res.json()
return projects
}
export default async function Index() {
const projects = await getProjects()
return projects.map((project) => <div>{project.name}</div>)
}
getStaticPaths
)
동적 경로 (pages
디렉터리에서, getStaticPaths
함수는 빌드 시간에 사전 렌더링되어야 할 동적 경로를 정의하는 데 사용됩니다.
// `pages` 디렉터리
import PostLayout from '@/components/post-layout'
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return { props: { post } }
}
export default function Post({ post }) {
return <PostLayout post={post} />
}
app
디렉터리에서, getStaticPaths
는 generateStaticParams
로 대체됩니다.
generateStaticParams
는 getStaticPaths
와 유사하게 동작하지만, 라우트 매개변수를 반환하기 위한 간소화된 API를 가지고 있으며 레이아웃 내에서 사용될 수 있습니다. generateStaticParams
의 반환 형태는 중첩된 param
객체 배열이나 해결된 경로 문자열 대신 세그먼트 배열입니다.
// `app` 디렉터리
import PostLayout from '@/components/post-layout'
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }]
}
async function getPost(params) {
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
return post
}
export default async function Post({ params }) {
const post = await getPost(params)
return <PostLayout post={post} />
}
generateStaticParams
라는 이름은 app
디렉터리의 새로운 모델에 getStaticPaths
보다 더 적절합니다. get
접두어는 getStaticProps
와 getServerSideProps
가 더 이상 필요하지 않으므로 이제 더 서술적인 generate
로 대체되었습니다. Paths
접미사는 다중 동적 세그먼트로 중첩된 라우팅에 더 적합한 Params
로 대체되었습니다.
fallback
대체하기
pages
디렉터리에서, getStaticPaths
에서 반환되는 fallback
속성은 빌드 시간에 사전 렌더링되지 않은 페이지의 동작을 정의하는 데 사용됩니다. 이 속성은 페이지가 생성되는 동안 대체 페이지를 표시하려면 true
로 설정할 수 있고, 404 페이지를 표시하려면 false
로 설정하거나, 요청 시간에 페이지를 생성하려면 blocking
으로 설정할 수 있습니다.
// `pages` 디렉터리
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking'
};
}
export async function getStaticProps({ params }) {
...
}
export default function Post({ post }) {
return ...
}
app
디렉터리에서, config.dynamicParams
속성은 generateStaticParams
외부의 매개변수가 어떻게 처리되는지를 제어합니다.
true
: (기본값)generateStaticParams
에 포함되지 않은 동적 세그먼트는 요청에 따라 생성됩니다.false
:generateStaticParams
에 포함되지 않은 동적 세그먼트는 404를 반환합니다.
이는 pages
디렉터리의 getStaticPaths
의 fallback: true | false | 'blocking'
옵션을 대체합니다. 스트리밍과의 차이 때문에 dynamicParams
에 fallback: 'blocking'
옵션이 포함되지 않았습니다.
// `app` 디렉터리
export const dynamicParams = true;
export async function generateStaticParams() {
return [...]
}
async function getPost(params) {
...
}
export default async function Post({ params }) {
const post = await getPost(params);
return ...
}
dynamicParams
가 true
로 설정된 경우 (기본값), 생성되지 않은 라우트 세그먼트가 요청되면 서버에서 렌더링되어 캐시됩니다.
getStaticProps
와 revalidate
사용)
증분 정적 재생성 (pages
디렉터리에서, getStaticProps
함수를 사용하면 특정 시간 후 페이지를 자동으로 재생성하기 위해 revalidate
필드를 추가할 수 있습니다.
// `pages` 디렉터리
export async function getStaticProps() {
const res = await fetch(`https://.../posts`)
const posts = await res.json()
return {
props: { posts },
revalidate: 60,
}
}
export default function Index({ posts }) {
return (
<Layout>
<PostList posts={posts} />
</Layout>
)
}
app
디렉터리에서, fetch()
로 데이터를 가져올 때 revalidate
를 사용하여 지정된 초 동안 요청을 캐시할 수 있습니다.
// `app` 디렉터리
async function getPosts() {
const res = await fetch(`https://.../posts`,
{ next: { revalidate: 60 } })
const data = await res.json()
return data.posts
}
export default async function PostList() {
const posts = await getPosts()
return posts.map((post) => <div>{post.name}</div>)
}
API 라우트
API 라우트는 pages/api
디렉터리에서 어떠한 변경도 없이 계속 작동합니다. 그러나 app
디렉터리에서는 Route Handlers에 의해 대체되었습니다.
Route Handlers는 Web Request 및 Response API를 사용하여 주어진 라우트에 대한 사용자 정의 요청 핸들러를 생성할 수 있게 해줍니다.
export async function GET(request: Request) {}
export async function GET(request) {}
클라이언트에서 외부 API를 호출하기 위해 API 라우트를 이전에 사용한 경우, 이제 Server Components를 사용하여 데이터를 안전하게 가져올 수 있습니다. data fetching에 대한 자세한 내용은 여기에서 알아보세요.
단계 7: 스타일링
pages
디렉터리에서는 전역 스타일시트는 pages/_app.js
에만 제한됩니다. 그러나 app
디렉터리에서는 이 제한이 해제되었습니다. 전역 스타일은 어떤 레이아웃, 페이지 또는 컴포넌트에도 추가될 수 있습니다.
Tailwind CSS
Tailwind CSS를 사용하고 있다면, tailwind.config.js
파일에 app
디렉터리를 추가해야 합니다.
module.exports = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}', // <-- 이 줄을 추가합니다
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
}
또한 app/layout.js
파일에서 전역 스타일을 가져와야 합니다.
import '../styles/globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Tailwind CSS로 스타일링하기에 대한 자세한 내용은 여기에서 알아보세요.
Codemods
Next.js는 기능이 폐지될 때 코드베이스를 업그레이드하는 데 도움을 주는 Codemod 변환을 제공합니다. 자세한 정보는 Codemods에서 확인하세요.