alltools.one
API
2025-06-05
7 min
alltools.one Team
APIRESTJSONResponse FormatBest Practices

API 응답 포맷: 일관된 API를 위한 모범 사례

일관성 없는 API 응답은 프론트엔드 개발자들이 가장 많이 불만을 제기하는 사항입니다. 모든 엔드포인트가 서로 다른 형태로 데이터를 반환하면 클라이언트 코드에 특수 처리가 난무하게 됩니다. 일관된 응답 포맷은 개발자 경험을 개선하고, 버그를 줄이며, API를 자체 문서화합니다.

응답 엔벨로프

모든 응답을 일관된 구조로 감싸세요:

성공 (단일 리소스)

{
  "data": {
    "id": "user_123",
    "name": "Alice",
    "email": "alice@example.com",
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "meta": {
    "requestId": "req_abc123"
  }
}

성공 (컬렉션)

{
  "data": [
    { "id": "user_123", "name": "Alice" },
    { "id": "user_456", "name": "Bob" }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "perPage": 20,
    "requestId": "req_def456"
  }
}

오류

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format" }
    ]
  },
  "meta": {
    "requestId": "req_ghi789"
  }
}

핵심 원칙: 클라이언트는 항상 최상위 레벨에서 data 또는 error를 확인합니다. 같은 응답에 성공 데이터와 오류 정보를 절대 혼합하지 마세요.

응답 포맷을 JSON 유효성 검사기로 확인하세요.

HTTP 상태 코드

상태 코드를 올바르게 사용하세요 — 클라이언트가 가장 먼저 확인하는 항목입니다:

성공 코드

코드사용 시점
200 OKGET 성공, PUT/PATCH 성공
201 Created리소스를 생성한 POST
204 No ContentDELETE 성공 (본문 없음)

클라이언트 오류 코드

코드사용 시점
400 Bad Request잘못된 요청 구문
401 Unauthorized인증 누락 또는 유효하지 않음
403 Forbidden인증되었으나 권한 없음
404 Not Found리소스가 존재하지 않음
409 Conflict리소스 상태 충돌 (중복)
422 Unprocessable올바른 구문이나 의미론적 오류
429 Too Many Requests요청 제한 초과

서버 오류 코드

코드사용 시점
500 Internal Server Error예상치 못한 서버 장애
502 Bad Gateway업스트림 서비스 장애
503 Service Unavailable일시적 과부하 또는 유지보수

오류 응답 설계

좋은 오류 응답은 개발자가 빠르게 디버깅할 수 있도록 도와줍니다:

{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "User with ID 'user_999' not found",
    "details": [],
    "documentationUrl": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
  },
  "meta": {
    "requestId": "req_xyz789",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}

규칙:

  • code는 기계가 읽을 수 있는 값입니다 (상수 문자열, HTTP 상태가 아님)
  • message는 사람이 읽을 수 있는 값입니다 (클라이언트를 손상시키지 않고 변경 가능)
  • details는 유효성 검사 실패에 대한 필드 수준 오류를 제공합니다
  • requestId는 지원팀이 로그에서 요청을 추적할 수 있게 합니다

페이지네이션 패턴

오프셋 기반

간단하며 임의의 페이지로 이동을 지원합니다:

{
  "data": ["..."],
  "meta": {
    "page": 2,
    "perPage": 20,
    "total": 142,
    "totalPages": 8
  },
  "links": {
    "first": "/api/users?page=1&per_page=20",
    "prev": "/api/users?page=1&per_page=20",
    "next": "/api/users?page=3&per_page=20",
    "last": "/api/users?page=8&per_page=20"
  }
}

커서 기반

실시간 데이터와 대규모 데이터셋에 더 적합합니다:

{
  "data": ["..."],
  "meta": {
    "hasNext": true,
    "hasPrev": true
  },
  "links": {
    "next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
    "prev": "/api/users?cursor=eyJpZCI6MTIzfQ&limit=20&direction=prev"
  }
}

날짜와 시간

항상 시간대 정보와 함께 ISO 8601을 사용하세요:

{
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T14:22:33+00:00",
  "expiresAt": "2024-12-31T23:59:59Z"
}

절대 사용하지 마세요:

  • Unix 타임스탬프 (모호함: 초인지 밀리초인지?)
  • 시간대가 없는 로컬 날짜
  • MM/DD/YYYY와 같은 사용자 정의 포맷

타임스탬프 처리에 대한 자세한 내용은 Unix 타임스탬프 가이드를 참조하세요.

Null 대 부재 필드

두 가지 일반적인 접근 방식:

null을 포함 (명시적):

{ "name": "Alice", "avatar": null, "bio": null }

부재 필드 생략 (희소):

{ "name": "Alice" }

권장 사항: API 내에서 일관성을 유지하세요. 명시적 null은 타입이 있는 언어에 적합합니다 (클라이언트가 필드의 존재를 알 수 있음). 희소 방식은 대역폭이 제한된 환경에 적합합니다.

응답 버전 관리

응답 포맷이 변경될 때, API의 버전을 관리하세요:

GET /api/v2/users/123

버전 관리가 필요한 호환성을 깨는 변경사항:

  • 필드 제거
  • 필드 타입 변경
  • 필드 이름 변경
  • 응답 엔벨로프 구조 변경

호환성을 유지하는 변경사항 (버전 관리 없이 안전):

  • 새 필드 추가
  • 새 엔드포인트 추가
  • 새 enum 값 추가

자주 묻는 질문

성공 응답을 data 키로 감싸야 하나요, 아니면 리소스를 직접 반환해야 하나요?

data 래퍼를 사용하면 모든 응답에 일관된 구조를 제공하고, 데이터와 함께 메타데이터, 페이지네이션, 링크를 위한 공간을 확보합니다. 리소스를 직접 반환하는 것은 단일 리소스 엔드포인트에 더 간단합니다. 대부분의 현대 API는 일관성을 위해 래퍼 방식을 사용합니다.

배치 작업에서 부분 실패를 어떻게 처리해야 하나요?

data 객체에 성공과 실패를 모두 포함하는 응답과 함께 200을 반환하세요. 207 Multi-Status (WebDAV) 사용도 또 다른 옵션이지만 REST API에서는 덜 일반적으로 사용됩니다.

관련 리소스

Published on 2025-06-05
API Response Formats: Best Practices for Consistent APIs | alltools.one