반응형
12. Route Handler
- Route Hnadler는 Web Request, Response API를 이용해서 custom request handler를 만들 수 있게 도움을 준다.
/api 요청 시 해당 route return 값 전달. - Route Handler는 app directory에서만 유효하며, page directory의 API Route와 동일한 개념이다.
- Route Handler는 route.js | ts로 명명해야 하며, page와 동일한 level에서 함께 존재할 수 없다. (ex. app/settings/page.tsx가 있으면, Route Handler는 있으면 안된다.)
- GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS HTTP Methods 지원하고, 그 외 Method는 405 Method Not Allowed
- Next.js는 Web API인 Request, Respone를 확장한 NextRequest, NextResponse를 지원.
12-1. 동작 방식
- Route Handler는 기본적으로 GET Method Response 시 캐싱을 지원한다.
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 })
}
- 선택적으로 캐싱을 해제하는 방법은 아래와 같다.
1. GET Method를 Request 객체와 함께 사용.
2. GET Method를 제외한 다른 Method 사용.
3. cookies, headers와 같은 동적 함수와 함께 사용.
4. Segment Config Options를 사용해 명시적으로 dynamic mode 사용.
export async function GET(request: Request) { // request 객체와 함께 쓰인 GET.
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 })
}
export async function POST() { // GET 이외의 HTTP Method.
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)
}
12-2. Example
1. 캐싱 데이터 재검증
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 }, // Revalidate every 60 seconds
})
const data = await res.json()
return Response.json(data)
}
// 또는 Segment Config Option
export const revalidate = 60
- Cache가 적용된 Route Handler의 응답값이 60초 마다 최신화하여 캐싱 진행.
2. 동적 함수
1. cookies
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.value}` }, // cookie set.
})
}
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token') // cookie get.
}
- next/headers 모듈 안의 cookies를 이용해 쿠키 정보를 읽거나 설정할 수 있다. (Server Function)
- Response headers에 Set-Cookie를 사용해서 쿠키 정보를 설정할 수도 있다.
2. Headers
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 },
})
}
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers) // NextRequest를 통해 headers 정보 읽기.
}
- next/headers 모듈 안의 headers를 이용해 header 정보를 읽을 수 있다. (Server Function)
- headers 정보는 read only 특징을 가지고 있으므로, 새로운 headers 정보는 Response 객체에 설정.
3. Redirects
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
redirect('https://nextjs.org/')
}
- Server Function인 redirect를 이용할 수 있다.
4. 동적 Segments Route
export async function GET(
request: Request,
{ params }: { params: { slug: string } }
) {
const slug = params.slug // 'a', 'b', or 'c'
}
- Route Handler 안에서 동적 Route를 구성할 수 있다.

5. URL Query Parameters
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
}
- NextRequest에서 제공하는 nextUrl을 활용하면 Query Parameters를 손쉽게 컨트롤 할 수 있다.
6. Streaming
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)
}
- Route Handler 내부에서 Streaming은 일반적으로 Large Language Models(LLMs)와 함께 쓰인다.
// https://developer.mozilla.org/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 를 직접 사용해서 추상화하여 사용할 수도 있다.
7. Request Body
export async function POST(request: Request) {
const res = await request.json()
return Response.json({ res })
}
- Web API를 이용해 Body data를 읽어들일 수 있다.
8. Request Body FormData
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 })
}
- Web API의 formData()를 이용해 FormData를 읽어들일 수 있다.
9. 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',
},
})
}
- Web API를 이용해 CORS header 정보를 설정해줄 수도 있다.
- 여러 Route Handler에 CORS header 정보를 설정하기 위해선 Middleware 또는 next.config.js에서 설정.
10. 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,
})
}
- Route Handler는 외부 서비스의 webhooks를 수신하는 용도로 사용 가능하다.
- page router와 다르게 별도의 bodyParser와 같은 추가 설정이 필요 없다.
11. Edge and Node.js Runtimes
- Route Handler는 Edge Runtime 환경과 Node.js Runtime 환경에서 streaming과 같은 기능을 포함해 동일한 API를 사용할 수 있도록 도와준다.
- Page나 Layout에서 쓸 수 있는 Route Segment Configuration을 지원하고, 정적 재생성 기능을 지원.
export const runtime = 'edge' // 'nodejs' is the default
- runtime option을 이용해 runtime 환경을 설정할 수 있다.
1. Edge Runtime
특징:
분산된 환경: Edge 런타임은 엣지 서버(Edge Servers)에서 실행됩니다. 이는 전 세계 여러 지역에서 실행될 수 있는 분산형 서버 환경을 의미합니다. 엣지 서버는 사용자와 가장 가까운 위치에 배치되어 있어, 지연 시간(latency)을 최소화하고 빠른 응답 속도를 제공합니다.빠른 응답 시간: 엣지 서버에서 처리되기 때문에, 요청에 대한 응답이 사용자와 가까운 위치에서 처리되어 짧은 지연 시간을 자랑합니다. 이 덕분에 글로벌 사용자에게 더 빠른 응답을 제공할 수 있습니다.용도: 주로 캐싱, 정적 콘텐츠 제공, API 요청 처리, 그리고 짧은 시간 내 응답이 필요한 작업들에서 강점을 발휘합니다. 예를 들어, 사용자 인증, 이미지 최적화, A/B 테스트 등을 처리할 때 유용합니다.제한 사항: Edge 런타임은 일부 Node.js API와 호환되지 않을 수 있습니다. 예를 들어, 파일 시스템 접근이나 고급 네트워크 설정 등의 작업은 지원되지 않거나 제한이 있을 수 있습니다. 또한 메모리 및 CPU 제약이 있을 수 있어 복잡한 처리가 필요한 경우에는 적합하지 않을 수 있습니다.
장점:
낮은 지연 시간 (빠른 응답)글로벌 사용자에게 빠른 서비스 제공캐시 효율적 (서버 가까운 위치에서 처리)배포된 엣지 서버에서 처리되어 서버 부하 분산에 유리
단점:
제한된 Node.js 기능 지원메모리와 CPU 제약으로 복잡한 처리가 어려울 수 있음
2. Node.js Runtime
특징:
중앙화된 서버: Node.js 런타임은 전통적인 중앙화된 서버 환경에서 실행됩니다. 즉, 코드가 웹 서버 또는 백엔드 서버에서 실행되며, 일반적으로 클라우드 서버(AWS, Google Cloud, etc.)나 온프레미스 서버에서 실행됩니다.성능: Node.js는 단일 스레드 비동기 처리로 효율적인 요청 처리가 가능하지만, 엣지 서버보다 지리적 거리가 영향을 미치기 때문에 엣지 런타임에 비해 상대적으로 응답 시간이 길어질 수 있습니다.용도: 복잡한 API 요청 처리, 데이터베이스 연결, 대용량 데이터 처리, 복잡한 비즈니스 로직 등을 처리할 때 사용됩니다. 파일 시스템 접근, 환경 변수 설정, 네트워크 요청 처리 등에서 자유롭게 작업할 수 있습니다.확장성: Node.js는 전체 서버 환경에서 실행되므로, 서버 측에서 리소스를 확장할 수 있습니다. 대규모 애플리케이션에 적합합니다.
장점:
풍부한 Node.js API 지원무제한 처리 능력 (서버 리소스를 충분히 활용할 수 있음)복잡한 비즈니스 로직 및 데이터 처리에 적합파일 시스템 접근, 데이터베이스 연결 등 자유로운 작업 가능
단점:
상대적으로 긴 지연 시간 (Edge에 비해)글로벌 사용자에게 빠른 서비스를 제공하는데 비효율적일 수 있음서버에 부하가 집중될 수 있음
2가지 Runtime 환경은 Next.js에서 설정만 진행하고, 실질적인 적용은 배포 환경에서 적용된다고 한다.
Edge Runtime 환경을 제공하지 않는 배포 환경이라면 기본적으로 Node.js Runtime에서 동작시킨다고 한다.
12. Non-UI Response
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',
},
}
)
}
- sitemap.xml, robot.txt, app icons 등과 같은 유형을 Response로 보낼 수도 있다.
처음 뵙겠습니다.
Next.js 사용하면서 처음 본 개념이거나, 이해하기 어려운 부분(?)은 붉은색 볼드처리 해봤다.
- 동적 함수: Next.js에서 요청과 응답을 처리할 때 동적으로 데이터를 추출하고 사용하는 함수들을 의미한다고 한다. cookie, header, query parameter 등?
- Webhooks: reverse API라고도 불리며, 기존 API 요청 -> 응답 과정이 아닌, 서버에서 특정 이벤트에 따라 callbackURL에 특정 데이터 등을 보내는 것.
Preference
Routing: Route Handlers | Next.js
Create custom request handlers for a given route using the Web's Request and Response APIs.
nextjs.org