핵심 정리
{
"data": {
"id": 101,
"title": "JSON Guide"
},
"meta": {
"requestId": "req_123",
"version": 1
}
}문법
최상위를 object로 두어야 나중에 구조를 깨지 않고 확장할 수 있다
API 응답을 배열로 바로 반환하면 처음에는 단순해 보입니다. 그러나 페이지 정보, 요청 ID, API 버전 같은 메타데이터를 추가해야 하는 순간, 배열 최상위 구조를 object로 바꿔야 하고 이미 배열을 기대하는 소비자 코드가 모두 깨집니다. 최상위를 object로 시작하면 data, meta, paging 같은 필드를 하위 호환성을 유지하면서 추가할 수 있습니다. "현재 필드 수"보다 "앞으로 무엇이 더 붙을 수 있는가"를 기준으로 구조를 잡아야 합니다.
data와 meta를 분리하면 소비자가 필요한 부분만 안정적으로 찾을 수 있다
성공 응답에서 핵심 payload를 data 필드에 담고 부가 정보를 meta에 분리하면, 소비자는 항상 같은 경로(response.data)에서 핵심 데이터를 찾습니다. meta에는 requestId, apiVersion, timestamp, 페이지 정보처럼 payload 자체는 아니지만 처리에 필요한 정보를 넣습니다. 이 구조는 소비자가 meta를 무시하고 data만 읽어도 정상 동작하고, 나중에 meta에 새 필드를 추가해도 소비자 코드를 변경하지 않아도 됩니다.
에러 응답도 구조화해야 프로그래밍 방식 처리가 가능하다
에러를 단순 문자열 하나로 반환하면 소비자가 메시지를 파싱해 에러 종류를 구분해야 합니다. 반면 code, message, details를 가진 구조화된 에러 객체는 소비자가 error.code를 switch 분기에 쓰고, error.details로 필드별 검증 오류를 표시하는 등 자동 처리가 쉬워집니다. 성공 응답과 에러 응답에서 최상위 구조가 다르면 소비자가 두 경우를 항상 분기해야 하므로, 같은 envelope에 data와 error를 나눠 담는 패턴이나 HTTP status로 분기하는 패턴 중 하나를 팀 내 표준으로 고정하는 것이 좋습니다.
{
"error": {
"code": "INVALID_INPUT",
"message": "title is required",
"details": [{ "field": "title", "issue": "missing" }]
}
}응답 구조를 자주 바꾸면 소비자 코드가 빠르게 깨진다 — 최상위 구조는 가능한 일찍 안정화해야 한다
API 응답 구조는 공개 시점부터 계약입니다. 최상위 필드 이름을 바꾸거나 data를 배열에서 object로 변경하면 모든 소비자가 영향을 받습니다. 이후 확장은 기존 필드를 변경하는 것이 아니라 새 필드를 추가하는 방식으로 해야 하위 호환성이 유지됩니다. data 안의 구조 변경도 마찬가지입니다. 버전 관리가 필요하다면 meta.version이나 URL 경로 버전(/v2/)으로 명시적으로 나누는 편이 암묵적 구조 변경보다 훨씬 안전합니다.
{
"data": {
"id": 101,
"title": "JSON Guide"
},
"meta": {
"requestId": "req_123",
"version": 1
}
}선택 기준
| 상황 | 적합한 선택 |
|---|---|
| 목록 응답 시 최상위 타입 | object (data 배열 포함) — 메타데이터 확장 대비 |
| 페이지 정보, requestId 등 부가 정보 | meta 필드로 분리 |
| 에러 응답 포맷 | 구조화된 object (code, message, details) |
| 응답 구조 버전 관리 | meta.version 또는 URL 경로 버전으로 명시 |
| 성공/실패 공통 계약 유지 | envelope 패턴을 팀 기준으로 고정 |
주의할 점
응답 구조를 자주 바꾸면 소비자 코드가 빠르게 깨집니다. 특히 최상위를 배열로 시작했다가 object로 바꾸는 것은 모든 소비자에게 파괴적 변경입니다. 최상위 구조는 가능한 일찍 안정화하고, 이후 확장은 기존 필드 변경이 아닌 새 필드 추가 중심으로 관리해야 합니다.
[
{ "id": 1, "title": "A" },
{ "id": 2, "title": "B" }
]처음엔 단순해 보여도, 나중에 paging이나 requestId가 필요해지는 순간 최상위를 object로 바꿔야 합니다. 공개 API에서 이 변경은 소비자 전부를 깨뜨리는 쪽에 가깝습니다.
비슷하게 성공 응답은 { "data": ... }인데 에러 때는 갑자기 문자열만 내려주면 소비자는 응답 해석 코드를 매번 분기해야 합니다. 최상위 계약은 일찍 고정할수록 좋습니다.
참고 링크
2 sources