← 목록으로

API 설계 방법

웹 서비스에서 데이터를 주고받기 위한 API를 어떻게 설계하면 좋은지 기본적인 방법을 정리합니다.

API란 무엇인가

API(Application Programming Interface)는 프론트엔드와 백엔드, 또는 서비스와 서비스가 데이터를 주고받기 위한 약속입니다. 서로 다른 시스템이 표준화된 방식으로 통신할 수 있게 해줍니다.

좋은 API는 예측 가능하고, 일관되며, 이해하기 쉽습니다. 잘못 설계된 API는 팀 전체의 개발 속도를 늦추고 버그의 원인이 됩니다.


RESTful API의 핵심 원칙 6가지

REST(Representational State Transfer)는 가장 보편적인 API 설계 방식입니다.

  1. 클라이언트-서버 분리: 프론트엔드와 백엔드는 독립적으로 동작하고 API로만 통신합니다.

  2. 무상태성(Stateless): 각 요청은 서버가 이전 요청을 기억하지 않아도 독립적으로 처리될 수 있어야 합니다. 인증 정보는 매 요청에 포함해야 합니다.

  3. 캐시 가능: 응답은 캐시 가능 여부를 명시해야 합니다. 캐싱이 가능한 데이터는 Cache-Control 헤더로 표시합니다.

  4. 계층화된 시스템: 클라이언트는 서버에 직접 연결되는지, 중간 프록시를 통하는지 알 필요가 없습니다.

  5. 균일한 인터페이스: URL은 자원(Resource)을 나타내고, HTTP 메서드는 행위를 나타냅니다.

  6. 자원 중심 설계: URL에 동사를 사용하지 않고 명사(자원)를 사용합니다. /getUser 대신 /users/{id}를 사용합니다.


URL 설계 규칙

# 좋은 예시
GET    /posts           # 게시글 목록 조회
GET    /posts/123       # 특정 게시글 조회
POST   /posts           # 게시글 생성
PATCH  /posts/123       # 게시글 일부 수정
DELETE /posts/123       # 게시글 삭제
GET    /users/456/posts # 특정 사용자의 게시글 목록

# 나쁜 예시
GET    /getPosts
POST   /createPost
GET    /deletePost?id=123

URL은 소문자, 하이픈(-) 사용, 복수형 명사를 원칙으로 합니다.


HTTP 상태 코드 완전 정리

HTTP 상태 코드는 요청의 결과를 표준화된 숫자로 나타냅니다.

2xx 성공

  • 200 OK: 요청 성공. GET, PATCH, DELETE 성공 시 사용
  • 201 Created: 자원 생성 성공. POST로 새 데이터를 만들었을 때 사용
  • 204 No Content: 성공했지만 응답 본문이 없음. DELETE 후 사용

4xx 클라이언트 오류

  • 400 Bad Request: 요청 형식이 잘못됨. 필수 필드 누락, 유효하지 않은 값
  • 401 Unauthorized: 인증이 필요함. 로그인이 안 된 상태에서 보호된 API 호출 시
  • 403 Forbidden: 인증은 됐지만 권한이 없음. 다른 사용자의 데이터 수정 시도
  • 404 Not Found: 자원이 존재하지 않음
  • 422 Unprocessable Entity: 형식은 맞지만 비즈니스 규칙 위반 (이미 사용 중인 이메일 등)
  • 429 Too Many Requests: Rate Limit 초과

5xx 서버 오류

  • 500 Internal Server Error: 서버 내부 오류. 예상치 못한 예외 발생 시
  • 503 Service Unavailable: 서버 점검 중 또는 과부하

API 버전 관리 방법

서비스가 성장하면 API를 변경해야 하는 상황이 옵니다. 기존 클라이언트를 깨뜨리지 않으면서 새 API를 배포하려면 버전 관리가 필요합니다.

URL 기반 버전 관리 (가장 일반적)

/v1/posts  ← 기존 API 유지
/v2/posts  ← 새로운 구조의 API

v1 클라이언트는 계속 /v1을 사용하고, 새 클라이언트는 /v2를 사용합니다. 구형 버전은 sunset 날짜를 공지하고 일정 기간 후 deprecated 처리합니다.

헤더 기반 버전 관리

Accept: application/vnd.myapi.v2+json

URL이 깔끔하게 유지되지만, 테스트와 캐싱이 복잡해지는 단점이 있습니다.


응답 형식 표준화

일관된 응답 구조를 만들면 프론트엔드에서 처리하기 쉬워집니다.

// 성공 응답
{
  "success": true,
  "data": {
    "id": 123,
    "title": "첫 번째 게시글",
    "created_at": "2026-03-01T12:00:00Z"
  }
}

// 목록 응답
{
  "success": true,
  "data": [...],
  "pagination": {
    "total": 100,
    "page": 1,
    "per_page": 20,
    "total_pages": 5
  }
}

// 에러 응답
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "이메일 형식이 올바르지 않습니다.",
    "details": [
      { "field": "email", "message": "유효한 이메일 주소를 입력해주세요." }
    ]
  }
}

API 문서 작성 (Swagger/OpenAPI)

OpenAPI는 API를 YAML/JSON으로 표준화하여 문서화하는 방법입니다. Express + Swagger, FastAPI(자동 문서화), NestJS(데코레이터 기반 자동화)를 활용하면 코드에서 문서가 자동으로 생성됩니다.

# openapi.yaml 예시
paths:
  /posts:
    get:
      summary: 게시글 목록 조회
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: 성공

API 보안 기본

인증 처리

모든 보호된 엔드포인트는 인증 미들웨어로 보호합니다. JWT를 사용한다면 Authorization 헤더에서 토큰을 추출하고 유효성을 검증합니다.

Rate Limiting

같은 IP에서 단시간에 과도한 요청을 차단합니다. Express에서는 express-rate-limit 패키지를 사용합니다.

import rateLimit from 'express-rate-limit'

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15분
  max: 100 // 15분에 최대 100회 요청
})

app.use('/api/', limiter)

입력 검증

클라이언트에서 오는 모든 데이터는 신뢰할 수 없습니다. Zod나 Joi를 사용해 스키마 기반 검증을 적용합니다.

import { z } from 'zod'

const createPostSchema = z.object({
  title: z.string().min(1).max(100),
  content: z.string().min(10),
  is_public: z.boolean().default(false)
})

// 요청 처리
const parsed = createPostSchema.safeParse(req.body)
if (!parsed.success) {
  return res.status(400).json({ error: parsed.error })
}

SQL Injection 방지를 위해 raw SQL 대신 ORM(Prisma, Drizzle)이나 파라미터 바인딩을 반드시 사용합니다.