SvelteKit에서는 프론트엔드와 백엔드 API를 같은 프로젝트에서 만들 수 있습니다.

개념 정의

API Routes 는 +server.js 파일에서 HTTP 메서드별 핸들러를 export하여 RESTful API 엔드포인트를 만드는 기능입니다. Next.js의 Route Handlers와 동일한 개념입니다.

기본 사용법

JAVASCRIPT
// src/routes/api/posts/+server.js
import { json, error } from '@sveltejs/kit';

// GET /api/posts
export async function GET({ url }) {
  const page = Number(url.searchParams.get('page') ?? '1');
  const limit = Number(url.searchParams.get('limit') ?? '10');

  const posts = await db.post.findMany({
    skip: (page - 1) * limit,
    take: limit,
  });

  return json(posts);
}

// POST /api/posts
export async function POST({ request }) {
  const body = await request.json();

  if (!body.title || !body.content) {
    error(400, '제목과 내용은 필수입니다');
  }

  const post = await db.post.create({ data: body });
  return json(post, { status: 201 });
}

CRUD API 패턴

JAVASCRIPT
// src/routes/api/posts/[id]/+server.js
import { json, error } from '@sveltejs/kit';

// GET /api/posts/:id
export async function GET({ params }) {
  const post = await db.post.findUnique({
    where: { id: params.id }
  });

  if (!post) {
    error(404, '포스트를 찾을 수 없습니다');
  }

  return json(post);
}

// PUT /api/posts/:id
export async function PUT({ params, request }) {
  const body = await request.json();
  const post = await db.post.update({
    where: { id: params.id },
    data: body,
  });
  return json(post);
}

// DELETE /api/posts/:id
export async function DELETE({ params }) {
  await db.post.delete({ where: { id: params.id } });
  return new Response(null, { status: 204 });
}

응답 헤더 설정

JAVASCRIPT
export async function GET() {
  const data = await fetchData();

  return json(data, {
    headers: {
      'Cache-Control': 'max-age=60',
      'X-Custom-Header': 'value',
    }
  });
}

// 스트리밍 응답
export async function GET() {
  const stream = new ReadableStream({
    start(controller) {
      controller.enqueue('Hello ');
      controller.enqueue('World');
      controller.close();
    }
  });

  return new Response(stream, {
    headers: { 'Content-Type': 'text/plain' }
  });
}

인증 미들웨어 패턴

JAVASCRIPT
// src/routes/api/posts/+server.js
import { json, error } from '@sveltejs/kit';

export async function POST({ request, locals }) {
  // hooks.server.js에서 설정한 인증 정보 확인
  if (!locals.user) {
    error(401, '인증이 필요합니다');
  }

  const body = await request.json();
  const post = await db.post.create({
    data: { ...body, authorId: locals.user.id }
  });

  return json(post, { status: 201 });
}

Webhook 처리

JAVASCRIPT
// src/routes/api/webhook/stripe/+server.js
import { error } from '@sveltejs/kit';
import { STRIPE_WEBHOOK_SECRET } from '$env/static/private';

export async function POST({ request }) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature');

  try {
    const event = stripe.webhooks.constructEvent(
      body, signature, STRIPE_WEBHOOK_SECRET
    );

    switch (event.type) {
      case 'payment_intent.succeeded':
        await handlePaymentSuccess(event.data.object);
        break;
      case 'customer.subscription.deleted':
        await handleSubscriptionCanceled(event.data.object);
        break;
    }

    return new Response('OK', { status: 200 });
  } catch (err) {
    error(400, `Webhook Error: ${err.message}`);
  }
}

면접 포인트

  • "API Routes와 Form Actions의 차이는?": API Routes는 범용 HTTP 엔드포인트로 외부 클라이언트도 사용할 수 있습니다. Form Actions는 SvelteKit 폼에 특화되어 프로그레시브 인핸스먼트, CSRF 보호를 기본 제공합니다.
  • "CORS는 어떻게 처리하나요?": +server.js에서 OPTIONS 핸들러를 추가하고 응답 헤더에 Access-Control-Allow-Origin을 설정하거나, hooks에서 일괄 처리할 수 있습니다.

정리

SvelteKit의 API Routes는 풀스택 개발을 가능하게 하는 핵심 기능입니다. 파일 기반으로 API를 구성하니 구조가 직관적이고, SvelteKit의 타입 시스템과 에러 처리를 그대로 활용할 수 있습니다.

댓글 로딩 중...