Quick Reference
{
"data": {
"id": 101,
"title": "JSON Guide"
},
"meta": {
"requestId": "req_123",
"version": 1
}
}목록 자체만 오래 유지할 계약이면 최상위 배열도 올바른 선택입니다. 페이지 정보, 요청 추적값, 공통 오류 형식처럼 payload 밖 정보가 필요할 때만 object envelope를 선택합니다. data, meta, error는 JSON 표준이 아니라 팀이 정하는 필드 이름입니다.
문법
최상위 object는 공통 정보가 있을 때 확장 비용을 줄인다
API 응답을 배열로 바로 반환하는 계약은 단순하고 유효합니다. 다만 페이지 정보, 요청 ID, API 버전 같은 메타데이터가 계약상 필요하면 object envelope가 그 정보를 덧붙일 자리를 제공합니다. 이미 공개한 최상위 배열을 나중에 object로 바꾸면 배열을 기대하는 소비자 코드가 깨집니다. 공통 정보의 필요성과 불필요한 한 단계 중첩을 함께 비교해 처음부터 고정합니다.
data와 meta는 선택한 envelope 안에서 역할을 나눈다
envelope를 쓰기로 했다면 핵심 payload를 data, 부가 정보를 meta에 나누는 방식이 흔합니다. meta에는 requestId, timestamp, 페이지 정보처럼 payload 자체는 아니지만 처리에 필요한 정보를 넣습니다. 다만 이 이름과 구조는 표준이 아닙니다. JSON:API나 조직의 기존 계약이 있다면 그 규칙을 따르고, 새 필드를 추가할 때 소비자가 unknown member를 허용하는지도 확인합니다.
에러 응답도 구조화해야 프로그래밍 방식 처리가 가능하다
에러를 단순 문자열 하나로 반환하면 소비자가 메시지를 파싱해 에러 종류를 구분해야 합니다. 반면 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, 아니면 배열 계약도 가능 |
| 페이지 정보, 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