**Route Handlers를 사용하면 Web Request와 Response API를 사용해서 특정 경로에 대한 사용자 정의 요청 핸들러를 생성할 수 있다!
**
이 Route Handler는 app directory 안에서만 유효하다.
pages directory에 있는 API 라우트랑 같기 때문에 API 라우트와 라우트 핸들러를 동시에 사용할 필요는 없다!
라우트 핸들러는 app 디렉토리 안의 route.js 또는 route.ts 파일 안에 정의되어 있어야 한다.
app/api/route.ts
export const dynamic = 'force-dynamic' // defaults to auto
export async function GET(request: Request) {}
라우트 핸들러는 page.tsx와 layout.tsx처럼 app 디렉토리 내에서 nested 될 수 있지만 page.tsx와 같은 레벨에 있으면 안 된다!!
GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
지원되지 않는 메소드가 호출되면, 405를 반환한다.
native Request와 Response를 지원한다!
NextRequest와 NextResponse로 확장한다.
라우트 핸들러는 기본적으로 캐싱된다. -> response 객체와 GET 메소드를 함께 사용할 때
route.ts
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
Response.json()은 타입스크립트 5.2에서만 유효하다.
만약 더 아래 버전의 타입스크립트를 사용하고 있다면 NextResponse.json()을 대신 사용하면 된다.
캐싱을 선택적으로 해제할 수 있다.
예시
export async function GET(request: 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 Response.json({ product })
}
비슷하게, POST 메소드는 라우트 핸들러가 동적으로 되게 한다.
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 Response.json(data)
}
API 라우터처럼, 라우트 핸들러는 폼 제출에도 사용될 수 있다.

각각의 route.ts와 page.tsx 파일은 해당 라우트에 대한 모든 HTTP 메소드를 인수한다.
export default function Page() {
return <h1>Hello, Next.js!</h1>
}
// ❌ Conflict
// `app/route.js`
export async function POST(request) {}
next.revalidate 옵션을 사용해서 재검증 가능하다.
in route.ts file
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 }, // 60초마다 재검증
})
const data = await res.json()
return Response.json(data)
}
또 revalidate segmnt config option을 사용할 수 있다.
export const revalidate = 60
라우트 핸들러는 cookies와 headers 같은 dynamic function들과 함께 사용될 수 있다.
-> next/headers에서 cookies를 읽고, 설정할 수 있다.
이 서버 함수는 라우트 핸들러에서 직접적으로 호출되거나 다른 함수에 싸여진다.
또 다르게, Set-Cookie header를 사용해서 새로운 Response를 반환할 수 있다.
in route.ts file
import { cookies } from 'next/headers'
export async function GET(request: Request) {
const cookieStore = cookies()
const token = cookieStore.get('token') // cookie 읽기 가능
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token.value}` }, // 새로운 Response 반환
})
}
기본 Web API에서 Request(NextRequest)로부터 쿠키를 읽을 수도 있다.
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token')
}
next/headers에서 headers를 읽을 수 있다.
이 서버 함수는 라우트 핸들러에서 직접적으로 호출되거나 다른 함수에 싸여진다.
headers 인스턴스는 읽을 수만 있다.
headers를 설정하려면, 새로운 headers와 같이 새로운 Response를 반환해야 한다.
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에서 Request(NextRequest)로부터 헤더를 읽을 수도 있다.
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
}
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
redirect('https://nextjs.org/')
}
은 이전에 정리한 포스트를 보자!
params로 가능하다
라우트 핸들러로 보내지는 request 객체는 NextRequest instance이고, 몇 가지 추가적인 편한 메소드들을 가지고 있다.
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
Streaming은 일반적으로 LLM에서 쓰인다.
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
export const runtime = 'edge'
export async function POST(req: Request) {
const { messages } = await req.json()
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages,
})
const stream = OpenAIStream(response)
return new StreamingTextResponse(stream)
}
이 추상화는 웹 API를 사용해서 stream을 생성한다.
-> AI model은 안 쓸 거니 패스
standard Web API 메소드를 써서 Request body를 읽을 수 있다.
export async function POST(request: Request) {
const res = await request.json()
return Response.json({ res })
}
request.formData() 함수로 FromData를 읽을 수 있다.
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
formData 데이터는 모두 string이기에, 원하는 format에서 요청하고 받는 data를 검증하기 위해 zod-form-data를 사용할 수도 있다.
특정 라우트 핸들러를 위해 CORS 헤더를 설정할 수 있다.
export const dynamic = 'force-dynamic' // defaults to auto
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',
},
})
}
여러 개의 라우트 핸들러에 CORS 헤더를 추가하기 위해 Middleware나 next.config.js 파일을 사용할 수 있다.
다른 제 3의 서비스로부터 webhooks를 받기 위해 라우트 핸들러를 사용할 수 있다.
export async function POST(request: Request) {
try {
const text = await request.text()
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
})
}
return new Response('Success!', {
status: 200,
})
}
특정 Page 라우터를 요구하는 API 라우터와는 다르게 bodyParser를 사용할 필요가 없다.
-> 필요할 때 찾아볼 것
라우트 핸들러를 non-UI content를 리턴할 때 사용할 수 있다.
sitemap.xml, robots.txt, app icons, open graph image들을 지원한다.
export const dynamic = 'force-dynamic' // defaults to auto
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>`,
{
headers: {
'Content-Type': 'text/xml',
},
}
)
}
라우트 핸들러는 page와 layout과 동일한 세그먼트 구성을 사용한다.
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'