DevToolBox무료
블로그

REST API 설계 모범 사례: 완전 가이드

12분by DevToolBox

훌륭한 REST API 설계는 단순히 JSON을 반환하는 것 이상입니다. 잘 설계된 API는 예측 가능하고, 일관되며, 안전하고, 사용하기 쉬워야 합니다. 이 가이드는 REST API 설계 모범 사례를 다룹니다.

1. 리소스 URI에 명사 사용

REST API는 동작이 아닌 리소스를 모델링합니다.

# Good - nouns representing resources
GET    /api/v1/users          # list users
GET    /api/v1/users/123      # get user 123
POST   /api/v1/users          # create a user
PUT    /api/v1/users/123      # replace user 123
PATCH  /api/v1/users/123      # partially update user 123
DELETE /api/v1/users/123      # delete user 123

# Bad - verbs describing actions
GET    /api/v1/getUsers
POST   /api/v1/createUser
POST   /api/v1/deleteUser/123
GET    /api/v1/getUserById?id=123
# Nested resources (one level deep)
GET    /api/v1/users/123/orders       # orders for user 123
GET    /api/v1/users/123/orders/456   # order 456 for user 123
POST   /api/v1/users/123/orders       # create order for user 123

# For actions that don't map to CRUD, use sub-resources
POST   /api/v1/users/123/activate     # activate user (action)
POST   /api/v1/orders/456/cancel      # cancel order (action)
POST   /api/v1/emails/789/resend      # resend email

2. HTTP 메서드 올바르게 사용

각 HTTP 메서드는 특정 의미를 가집니다.

MethodPurposeIdempotentRequest Body
GETRead a resourceYesNo
POSTCreate a resourceNoYes
PUTFull replacementYesYes
PATCHPartial updateNo*Yes
DELETERemove a resourceYesNo

3. 복수형 리소스 이름 사용

컬렉션에는 항상 복수형 명사를 사용하세요.

# Good - consistent plural nouns
/api/v1/users
/api/v1/users/123
/api/v1/products
/api/v1/products/456/reviews

# Bad - mixing singular and plural
/api/v1/user          # singular
/api/v1/user/123
/api/v1/productList   # avoid "list" suffix

4. HTTP 상태 코드 올바르게 사용

상태 코드는 클라이언트에게 무슨 일이 일어났는지 알려줍니다.

CodeWhen to Use
200 OKSuccessful GET, PUT, PATCH, or DELETE
201 CreatedSuccessful POST that creates a resource
204 No ContentSuccessful DELETE with no response body
400 Bad RequestMalformed request syntax or invalid data
401 UnauthorizedMissing or invalid authentication
403 ForbiddenAuthenticated but not authorized
404 Not FoundResource does not exist
409 ConflictConflicting state (e.g., duplicate email)
422 UnprocessableValidation errors in request body
429 Too Many RequestsRate limit exceeded
500 Internal ErrorUnexpected server error

5. API 버전 관리

API 버전 관리는 호환성 깨지는 변경 시 기존 소비자를 보호합니다.

# Strategy 1: URI versioning (most common)
GET /api/v1/users
GET /api/v2/users

# Strategy 2: Header versioning
GET /api/users
Accept: application/vnd.myapi.v2+json

# Strategy 3: Query parameter versioning
GET /api/users?version=2
StrategyProsCons
URI pathSimple, visible, cacheableURI pollution
HeaderClean URIsHarder to test, less visible
Query paramEasy to addCache-unfriendly, easy to forget

6. 페이지네이션, 필터링, 정렬

컬렉션을 반환하는 모든 엔드포인트는 페이지네이션을 지원해야 합니다.

# Offset-based pagination (simplest)
GET /api/v1/users?page=2&limit=25
GET /api/v1/users?offset=25&limit=25

# Cursor-based pagination (better for large datasets)
GET /api/v1/users?cursor=eyJpZCI6MTAwfQ&limit=25

# Response with pagination metadata
{
  "data": [...],
  "pagination": {
    "total": 1250,
    "page": 2,
    "limit": 25,
    "totalPages": 50,
    "hasNext": true,
    "hasPrev": true
  }
}

# Filtering and sorting
GET /api/v1/products?category=electronics&minPrice=100&maxPrice=500
GET /api/v1/products?sort=price&order=asc
GET /api/v1/products?sort=-created_at  # prefix with - for descending
GET /api/v1/users?fields=id,name,email  # sparse fieldsets

7. 오류 응답 형식

일관된 오류 형식은 클라이언트의 프로그래밍적 오류 처리를 돕습니다.

// Consistent error response format
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address",
        "value": "not-an-email"
      },
      {
        "field": "age",
        "message": "Must be at least 18",
        "value": 15
      }
    ],
    "requestId": "req_abc123",
    "timestamp": "2026-01-15T10:30:00Z",
    "docs": "https://api.example.com/docs/errors#VALIDATION_ERROR"
  }
}

// Simple error (non-validation)
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "User with id 999 not found",
    "requestId": "req_def456"
  }
}

8. 인증과 보안

API 보안은 타협할 수 없습니다.

# Bearer token authentication (JWT)
GET /api/v1/users
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

# API key authentication
GET /api/v1/users
X-API-Key: sk_live_abc123def456

# OAuth 2.0 token request
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=read:users write:users
  • Always use HTTPS in production
  • Never put secrets in query parameters (they appear in server logs)
  • Use short-lived access tokens (15-60 min) with refresh tokens
  • Implement CORS properly for browser-based clients
  • Validate and sanitize all input to prevent injection attacks
  • Use rate limiting to prevent brute-force attacks

9. 속도 제한

속도 제한은 API를 남용으로부터 보호합니다.

# Rate limit response headers (standard)
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000        # max requests per window
X-RateLimit-Remaining: 742     # requests remaining
X-RateLimit-Reset: 1706810400  # Unix timestamp when limit resets
Retry-After: 60                # seconds until next request (on 429)

# Rate limit exceeded response
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Limit: 1000/hour",
    "retryAfter": 60
  }
}

10. HATEOAS와 링크

HATEOAS는 API에 발견 가능성을 추가합니다.

// HATEOAS response example
{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "status": "active",
  "_links": {
    "self": { "href": "/api/v1/users/123" },
    "orders": { "href": "/api/v1/users/123/orders" },
    "deactivate": {
      "href": "/api/v1/users/123/deactivate",
      "method": "POST"
    }
  }
}

// Paginated collection with HATEOAS links
{
  "data": [...],
  "_links": {
    "self": { "href": "/api/v1/users?page=2&limit=25" },
    "first": { "href": "/api/v1/users?page=1&limit=25" },
    "prev": { "href": "/api/v1/users?page=1&limit=25" },
    "next": { "href": "/api/v1/users?page=3&limit=25" },
    "last": { "href": "/api/v1/users?page=50&limit=25" }
  }
}

자주 묻는 질문

업데이트에 PUT 또는 PATCH?

PUT은 전체 교체, PATCH는 부분 업데이트에 사용합니다.

URI는 소문자여야 하나요?

네, 소문자 kebab-case를 사용하세요.

중첩된 리소스는 어떻게 처리하나요?

중첩을 한 단계로 제한하세요.

최적의 인증 방법은?

사용자 대상 앱에는 OAuth 2.0과 JWT를 사용합니다.

GraphQL vs REST?

REST는 더 단순하고 캐싱이 우수합니다. GraphQL은 복잡한 데이터 요구에 적합합니다.

TL;DR

  • Use nouns (not verbs) for resource URIs
  • Use the correct HTTP method for each operation
  • Always use plural resource names
  • Return appropriate HTTP status codes
  • Version your API from day one (URI path is simplest)
  • Support pagination, filtering, and sorting for collections
  • Use a consistent error response format
  • Always use HTTPS and proper authentication
  • Implement rate limiting with standard headers
  • Consider HATEOAS for API discoverability

이러한 모범 사례를 처음부터 따르면 리팩토링 시간을 절약할 수 있습니다.

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

{ }JSON Formatter4xxHTTP Status Code ReferenceJWTJWT Decoder

Related Articles

REST API 모범 사례: 2026년 완전 가이드

REST API 설계 모범 사례: 네이밍 규칙, 에러 처리, 인증, 페이지네이션, 보안을 배웁니다.

HTTP 상태 코드: 개발자를 위한 완전 참조 가이드

완전한 HTTP 상태 코드 참조: 1xx~5xx 실용적 설명, API 모범 사례, 디버깅 팁.

API 인증: OAuth 2.0 vs JWT vs API Key

API 인증 방법 비교: OAuth 2.0, JWT Bearer 토큰, API Key. 각 방법의 사용 시나리오, 보안 절충안, 구현 패턴.