Files
nuxt-deep/docs/curriculum/03-server-routes.md

4.7 KiB

3. 서버 라우트·엔드포인트 (server/api/* & server/routes/*)

Nitro 엔진

Nuxt의 서버는 Nitro로 구동된다. server/ 디렉토리에 파일을 만들면 자동으로 API 엔드포인트가 생성된다.

server/
├── api/          # /api/ 접두사가 붙음
│   ├── hello.ts          → GET /api/hello
│   ├── users/
│   │   ├── index.ts      → GET /api/users
│   │   └── [id].ts       → GET /api/users/:id
│   └── products.post.ts  → POST /api/products (메서드 지정)
└── routes/       # 접두사 없음
    └── sitemap.xml.ts    → GET /sitemap.xml

기본 사용법

eventHandler

// server/api/hello.ts
export default defineEventHandler((event) => {
  return { message: 'Hello, Nuxt!' }
})

getQuery — 쿼리 파라미터 읽기

// server/api/products.ts
// GET /api/products?category=tent&limit=10
export default defineEventHandler((event) => {
  const query = getQuery(event)
  // query.category → 'tent'
  // query.limit → '10'

  return {
    category: query.category,
    limit: Number(query.limit) || 20
  }
})

readBody — 요청 본문(Body) 읽기

// server/api/products.post.ts
// POST /api/products
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  // body = { name: '텐트', price: 150000 }

  // DB에 저장하는 로직
  const product = await db.create(body)
  return product
})

라우트 파라미터

// server/api/users/[id].ts
// GET /api/users/123
export default defineEventHandler((event) => {
  const id = getRouterParam(event, 'id')
  // id → '123'

  return { userId: id }
})

HTTP 메서드 지정

파일명에 메서드를 붙이면 해당 메서드만 처리한다.

server/api/products.get.ts    → GET만 처리
server/api/products.post.ts   → POST만 처리
server/api/products.put.ts    → PUT만 처리
server/api/products.delete.ts → DELETE만 처리

메서드 제한 없이 처리하려면 내부에서 분기한다:

// server/api/products.ts
export default defineEventHandler(async (event) => {
  const method = getMethod(event)

  if (method === 'GET') {
    return await getProducts()
  }

  if (method === 'POST') {
    const body = await readBody(event)
    return await createProduct(body)
  }
})

유틸리티 함수 모음

import {
  defineEventHandler,  // 핸들러 정의
  getQuery,            // ?key=value 읽기
  readBody,            // POST body 읽기
  getRouterParam,      // URL 파라미터 읽기 (/users/:id)
  getMethod,           // HTTP 메서드 확인
  getHeaders,          // 요청 헤더 읽기
  getCookie,           // 쿠키 읽기
  setCookie,           // 쿠키 설정
  setResponseStatus,   // 응답 상태코드 설정
  sendRedirect,        // 리다이렉트
  createError,         // 에러 생성
} from 'h3'            // Nuxt가 내부적으로 h3를 사용

에러 처리

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')
  const user = await db.findUser(id)

  if (!user) {
    // 404 에러를 던지면 Nuxt가 적절한 응답을 보냄
    throw createError({
      statusCode: 404,
      statusMessage: '사용자를 찾을 수 없습니다'
    })
  }

  return user
})

미들웨어 (서버 전용)

// server/middleware/auth.ts
// 모든 API 요청에 자동으로 실행됨
export default defineEventHandler((event) => {
  const token = getHeader(event, 'authorization')

  if (!token) {
    throw createError({ statusCode: 401, statusMessage: '인증 필요' })
  }
})

server/routes/ vs server/api/

// server/api/data.ts → /api/data
// server/routes/data.ts → /data (접두사 없음)

// sitemap, robots.txt 같은 특수 엔드포인트에 유용
// server/routes/robots.txt.ts
export default defineEventHandler(() => {
  return `User-agent: *\nDisallow: /admin`
})

실전 예시: CRUD API

// server/api/purchases/index.get.ts
export default defineEventHandler(async (event) => {
  const query = getQuery(event)
  const purchases = await db
    .from('purchases')
    .select('*')
    .limit(Number(query.limit) || 20)
  return purchases
})

// server/api/purchases/index.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const purchase = await db.from('purchases').insert(body)
  setResponseStatus(event, 201)
  return purchase
})

// server/api/purchases/[id].delete.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')
  await db.from('purchases').delete().eq('id', id)
  return { success: true }
})