iBetter Books
수정

Ch 02. API 라우트 작성하기

이 장의 예제 코드는 Prisma를 사용합니다. Prisma 설치와 usePrisma() 유틸리티 작성은 Ch 04에서 다룹니다. 지금은 API 라우트 구조와 패턴에 집중하고, Ch 04를 마친 뒤 예제를 직접 실행해보세요.

Nitro의 API 라우트는 파일 이름이 곧 URL 주소입니다. 파일을 어디에 어떤 이름으로 만드는지에 따라 HTTP 메서드와 경로가 자동으로 결정됩니다. 별도의 라우터 설정이 필요 없습니다.

파일 이름 = URL 패턴

server/api/ 아래에 만드는 파일의 이름이 그대로 API 경로가 됩니다. 파일 이름에 HTTP 메서드를 붙이면 해당 메서드만 처리하는 엔드포인트가 됩니다.

파일 경로 엔드포인트
server/api/posts.ts GET /api/posts
server/api/posts.post.ts POST /api/posts
server/api/posts/[id].ts GET /api/posts/:id
server/api/posts/[id].delete.ts DELETE /api/posts/:id
server/api/posts/[id].put.ts PUT /api/posts/:id

메서드 접미사(.get, .post, .put, .delete, .patch)를 붙이지 않으면 모든 HTTP 메서드를 처리합니다.

목록 조회와 생성 API

게시글 목록을 가져오는 GET과 새 게시글을 만드는 POST는 같은 경로(/api/posts)를 사용하지만 파일을 분리해서 작성합니다.

// server/api/posts/index.get.tsexport default defineEventHandler(async (event) => {  const posts = await usePrisma().post.findMany()  return posts})
// server/api/posts/index.post.tsexport default defineEventHandler(async (event) => {  const body = await readBody(event)  const post = await usePrisma().post.create({ data: body })  return post})

index.get.tsindex.post.ts는 각각 GET /api/posts, POST /api/posts에 매핑됩니다. index 키워드가 상위 경로를 가리킵니다.

동적 파라미터 처리

URL 경로에 변하는 값이 들어오는 경우 대괄호([id])로 파일 이름을 만듭니다. getRouterParam 함수로 그 값을 꺼냅니다.

// server/api/posts/[id].get.tsexport default defineEventHandler(async (event) => {  const id = Number(getRouterParam(event, 'id'))  const post = await usePrisma().post.findUnique({ where: { id } })  return post})

getRouterParam의 반환값은 항상 문자열이므로, 숫자 ID가 필요하다면 Number()로 변환해야 합니다.

쿼리스트링 파싱

목록 조회 시 페이지 번호나 검색어를 쿼리스트링으로 전달받는 경우 getQuery를 사용합니다.

// server/api/posts/index.get.tsexport default defineEventHandler(async (event) => {  const query = getQuery(event)  const page = Number(query.page ?? 1)  const limit = Number(query.limit ?? 10)  const skip = (page - 1) * limit  const posts = await usePrisma().post.findMany({    skip,    take: limit,    orderBy: { createdAt: 'desc' }  })  return { posts, page, limit }})

/api/posts?page=2&limit=5처럼 요청하면 두 번째 페이지의 5개 게시글을 반환합니다.

삭제 API

특정 게시글을 삭제하는 엔드포인트는 파일 이름에 .delete를 붙입니다.

// server/api/posts/[id].delete.tsexport default defineEventHandler(async (event) => {  const id = Number(getRouterParam(event, 'id'))  await usePrisma().post.delete({ where: { id } })  return { success: true }})

파일 이름 하나로 메서드와 경로를 모두 표현할 수 있다는 점이 Nitro 파일 기반 라우팅의 핵심입니다. 폴더 구조가 그대로 API 설계서 역할을 해 주기 때문에 프로젝트가 커져도 구조를 파악하기 쉽습니다.