본문 바로가기

개발 공부/React

Next.js Route Handler

Next.js 13+ App Router가 도입되면서, 기존 pages/api 방식 대신 Route Handler라는 새로운 서버 API 작성 방식이 등장했습니다

 

 


 

Route Handler

  • Route Handler는 app/api/**/route.ts 파일 안에서 GET, POST, DELETE, PUT 등 HTTP 메서드를 함수로 export하여 API를 만드는 방식입니다.
  • Next.js 서버가 이 파일을 읽고, 각 메서드를 API endpoint로 자동 매핑해줍니다.

 

 

기존 방식과 비교

방식 파일 위치 특징
pages API pages/api/* Next.js 12 스타일, 페이지 라우터에 의존
Route Handler app/api/*/route.ts App Router 기반, 서버 컴포넌트 친화적, 최신 공식 방식

 

 

 


Route Handler가 왜 필요한가?

1) App Router와 완전 통합

  • 서버 컴포넌트와 자연스럽게 연동됨
  • fetch 시 자동 캐싱/리밸리데이션 기능 활용 가능
  • 서버 액션과 조합 가능

2) 파일 기반 API

  • 파일 생성만으로 API 라우트 자동 생성
  • API URL = 폴더 구조 그대로

3) Edge Runtime 지원

  • 글로벌 초고속 API 구성 가능

 

 


예제: /api/tasks CRUD

  • GET: 전체 tasks 조회
  • POST: 새로운 task 생성
  • DELETE: 특정 task 삭제

폴더 구조

app/
  api/
    tasks/
      route.ts
  tasks/
    page.tsx

 

 


/api/tasks/route.ts 구현

타입 정의

interface Task {
  id: number;
  title: string;
  completed: boolean;
}

interface CreateTaskRequest {
  title: string;
}

임시 데이터

let tasks: Task[] = [
  { id: 1, title: "Learn Next.js", completed: false },
  { id: 2, title: "Build a project", completed: false },
];

GET — 전체 조회

export async function GET() {
  return Response.json(tasks);
}

 

POST — 새로운 task 생성

export async function POST(req: Request) {
  const body: CreateTaskRequest = await req.json();
  if (!body.title) {
    return new Response("Title is required", { status: 400 });
  }

  const newTask = {
    id: tasks.length + 1,
    title: body.title,
    completed: false,
  };

  tasks.push(newTask);
  return new Response(JSON.stringify(newTask), { status: 201 });
}

 

 DELETE — task 삭제

export async function DELETE(req: Request) {
  const { searchParams } = new URL(req.url);
  const id = Number(searchParams.get("id"));

  if (!id) {
    return new Response("ID is required", { status: 400 });
  }

  const index = tasks.findIndex((t) => t.id === id);
  if (index === -1) {
    return new Response("Task not found", { status: 404 });
  }

  tasks.splice(index, 1);
  return new Response("Task deleted", { status: 200 });
}

 

 


API를 사용하는 페이지 /tasks/page.tsx

export default async function TasksPage() {
  const res = await fetch("http://localhost:3000/api/tasks", {
    cache: "no-store",
  });

  const tasks = await res.json();

  return (
    <div>
      <h1>Tasks</h1>
      <pre>{JSON.stringify(tasks, null, 2)}</pre>
    </div>
  );
}

 

cache: "no-store"?

  • Route Handler는 기본적으로 fetch 결과가 캐싱됩니다.
  • SSR에서 최신 데이터를 원한다면 반드시 no-store가 필요합니다.