루트 핸들러는 웹의 요청/응답 API들을 이용하여 특정 루트에 커스텀 리퀘스트 핸들러를 생성할 수 있도록 도와줍니다.
알아두면 좋은 것: 루트 핸들러는
app
디렉토리 내에서만 사용할 수 있습니다. 루트 핸들러들은pages
디렉토리 내의 API Routes 와 동일하며, 이는 API Routes와 루트 핸들러를 함께 사용하지 않는다는 것을 의미합니다.
루트 핸들러는 app
디렉토리 내의 route.js | ts
파일에 정의합니다:
// app/api/route.ts
export async function GET(request: Request) {}
루트 핸들러는 page.js
나 layout.js
와 비슷하게 app
디렉토리 내에 중첩될 수 있습니다. 하지만 page.js
와 같은 루트 세그먼트 레벨에는 route.js
파일이 올 수 없습니다.
다음과 같은 HTTP 메서드들이 사용 가능합니다: GET
, POST
, PATCH
, DELETE
, HEAD
, 그리고 OPTIONS
. 지원되지 않는 메서드가 호출되면, Next.js는 405 Method Not Allowed
응답을 리턴합니다.
NextRequest
and NextResponse
APIs (확장된 NextRequest
와 NextResponse
API)네이티브 Request와 Response를 지원하는 것에 더불어, Next.js는 이 둘을 NextRequest, 그리고 NextResponse와 함께 확장하여 더 향상된 케이스들을 위해 편리한 도구들을 제공합니다
GET
메서드를 Response
객체와 함께 이용할 경우, 루트 핸들러들은 기본적으로 정적으로 여겨집니다
// app/items/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const res = await fetch('https://data.mangodb-api/com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
}
});
const data = await res.json();
return NextResponse.json({ data })
}
타입스크립트 경고:
Response.json()
은 유효하지만, 네이티브 타입스크립트의 타입들은 현재 에러를 보여줍니다. 타이핑된 응답에 대해서NextResponse.json()
을 대신 사용할 수 있습니다.
정적 페칭은 robots.txt
, rss.xml
, sitemap.xml
등의 루트들을 위한 커스텀 루트 핸들러를 생성하는 데에 유용할 수 있습니다. 예시를 확인하세요.
루트 핸들러는 다음과 같은 경우에 동적이라고 여겨집니다:
Request
객체를 GET
메서드와 함께 사용하는 경우cookies
나 headers
와 같은 동적 함수들을 사용하는 경우예를 들어:
// app/products/api/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
}
});
const product = await res.json();
return NextResponse.json({ product })
}
이와 비슷하게, POST
메서드도 루트 핸들러가 동적으로 여겨지도록 합니다
// app/items/route.ts
import { NextResponse } from 'next/server';
export async function POST() {
const res = await fetch('https://data.mongodb-api.com/...', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
body: JSON.stringify({ time: new Date().toISOString() }),
});
const data = await res.json();
return NextResponse.json(data);
}
노트 : 이전에는, API Routes 양식 제출과 같은 케이스들을 핸들링하는 데에 사용될 수 있었습니다. Route Handlers 는 이러한 케이스에 대한 해결책은 될 수 없습니다. 우리가 준비되면 mutations를 추천할게요.
route
를 가장 낮은 라우팅 원시 레벨로 고려할 수 있습니다.
page
와 같은 클라이언트 사이드 네비게이션이나 레이아웃에 관여하지 않습니다.route.js
파일은 page.js
와 같은 루트에 위치할 수 없습니다.각각의 route.js
와 page.js
는 해당 루트를 위한 모든 HTTP 동사들을 차지합니다.
// `app/page.js`
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// ❌ 충돌이 일어납니다
// `app/route.js`
export async function POST(request) {}
아래의 예시들은 루트 핸들러를 다른 Next.js API 및 기능들과 어떻게 결합할 수 있는지 보여줍니다.
next.revalidate 옵션을 사용해서, 정적 데이터 페칭을 다시 유효화할 수 있습니다.
// app/items/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 } // 매 60초마다 다시 유효화됩니다
});
const data = await res.json();
return NextResponse.json(data)
}
혹은, revalidate
세그먼트 설정 옵션을 사용할 수 있습니다:
export const revalidate = 60;
루트 핸들러는 cookies
나 headers
와 같은 Next.js의 동적 함수들과 함께 이용될 수 있습니다.
next/headers
의 cookies를 통해 쿠키를 읽어올 수 있습니다. 이 서버 함수는 루트 핸들러 안에서, 또는 다른 함수의 내부에서 다이렉트로 호출될 수 있습니다.
이 cookies
인스턴스는 읽기 전용입니다. 쿠키를 세팅하려면, Set-Cookie
헤더를 이용해서 새로운 Response
객체를 리턴해야 합니다.
// app/api/route.ts
import { cookies } from 'next/headers'
export async function GET(request: Request) {
const cookieStore = cookies();
const token = cookieStore.get('token');
return new Response('Hello, Next.js!, {
status: 200,
headers: { 'Set-Cookie': `token=${token}` }
});
}
대안으로는, 기저의 WEB API의 가장 상단 추상화를 이용해서 쿠키를 읽어올 수 있습니다 (NextRequest
):
// app/api/route.ts
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token');
}
next/headers
의 headers를 이용해서 헤더를 읽어올 수 있습니다. 이 서버 함수는 루트 핸들러 안에서, 또는 다른 함수의 내부에서 다이렉트로 호출될 수 있습니다.
이 headers
인스턴스는 읽기 전용입니다. 헤더를 세팅하려면, 새로운 headers
를 통해 Response
객체를 리턴해야 합니다.
// app/api/route.ts
import { headers } from 'next/headers';
export async function GET(request: Request) {
const headersList = headers();
const referer = headersList.get('referer');
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'referer': referer }
});
}
대안으로는, 기저의 WEB API의 가장 상단 추상화를 이용해서 헤더를 읽어올 수 있습니다 (NextRequest
):
// app/api/route.ts
import { type NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
}
// app/api/route.ts
import { redirect } from 'next/navigation';
export async function GET(request: Request) {
redirect('https://nextjs.org/')
}
루트 핸들러는 동적 데이터로부터 리퀘스트 핸들러를 생성할 수 있도록 동적 세그먼트들을 이용할 수 있습니다.
// app/items/[slug]/route.js
export async function GET(request: Request, { params }: {
params: { slug: string }
}) {
const slug = params.slug; // 'a', 'b', 또는 'c'
}
노트: generateStaticParams()는 아직 라우트 핸들러와 사용할 수 없습니다
// app/api/route.ts
// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time: number) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
기존의 Web API 메서드를 이용하여 Request
바디를 읽을 수 있습니다:
// app/items/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const res = await request.json();
return NextResponse.json({ res })
}
기존의 Web API 메서드를 이용하여 Response
에 CORS 헤더를 적용할 수 있습니다.
// app/api/route.ts
export async function GET(request: Request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
});
}
루트 핸들러는 스트리밍 지원을 포함하여 엣지와 Node.js 런타임을 매끄럽게 지원할 수 있는 isomorphic 웹 API를 가지고 있습니다. 루트 핸들러는 페이지와 레이아웃과 같은 루트 세그먼트 설정을 이용하기 때문에, 일반적인 목적의 정적으로 재생성된 루트 핸들러와 같은 오래 기다려온 기능들을 지원합니다.
런타임을 지정하기 위해서는 runtime
세그먼트 설정 옵션을 사용할 수 있습니다:
export const runtime = 'edge' // 'nodejs'가 기본입니다.
UI가 아닌 컨텐츠를 리턴하기 위해 루트 핸들러를 사용할 수 있습니다. sitemap.xml
, robots.txt
, favicon.ico
그리고 오픈그래프 이미지들 모두 빌트인 SEO 서포트를 가지고 있습니다.
// app/rss.xml/route.ts
export async function GET() {
return new Response(`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Next.js Documentation</title>
<link>https://nextjs.org/docs</link>
<description>The React Framework for the Web</description>
</channel>
</rss>`);
}
루트 핸들러는 페이지와 레이아웃과 같은 루트 세그먼트 설정을 사용합니다.
// app/items/route.ts
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
더 많은 정보는 API 레퍼런스를 참조하세요.