REST (Representational State Transfer)는 웹 서비스를 구축하기 위한 기본적인 아키텍처 스타일을 제공합니다. 하지만 요청 및 응답 형식의 많은 측면을 정의하지 않은 채로 남겨둡니다. 이러한 모호성은 불일치, 개발 오버헤드 증가, API 소비자의 학습 곡선 가파름으로 이어질 수 있습니다. 이때 JSON:API가 등장합니다. JSON:API는 JSON으로 API를 구축하기 위한 표준화되고 컨벤션 기반의 접근 방식을 제공하는 사양입니다.
이 종합 가이드는 JSON:API 사양에 대해 깊이 있게 탐구하며 핵심 개념, 구조 및 강력한 기능을 살펴봅니다. 리소스 가져오기, 생성, 업데이트, 삭제, 관계 관리, 오류 처리 및 데이터 전송 최적화를 위한 메커니즘을 분석하여 강력하고 효율적인 API를 설계하고 사용하는 데 필요한 지식을 갖추도록 지원합니다.
최대 생산성으로 개발 팀이 함께 작업할 수 있는 통합 올인원 플랫폼을 원하십니까?
Apidog는 귀하의 모든 요구를 충족하며 훨씬 저렴한 가격으로 Postman을 대체합니다!
JSON:API를 사용해야 하는 이유: 설명
기술적인 복잡성에 들어가기 전에 JSON:API가 해결하고자 하는 문제를 이해하는 것이 중요합니다. 공유된 컨벤션이 없으면 API 개발자는 종종 상당한 시간을 들여 다음 사항에 대해 논쟁합니다.
- 페이로드 구조: 리소스와 해당 속성은 어떻게 표현되어야 하는가?
- 관계 표현: 서로 다른 리소스 간의 링크는 어떻게 전달되어야 하는가?
- 데이터 가져오기 전략: 클라이언트는 특정 필드를 요청하고, 관련 리소스를 포함하고, 데이터를 정렬, 페이지네이션 및 필터링하는 방법은 무엇인가?
- 오류 보고: 오류 메시지는 어떤 형식을 따라야 하는가?
JSON:API는 요청 및 응답에 대한 명확하고 일관된 형식을 정의함으로써 이러한 문제를 해결합니다. 이러한 표준화는 몇 가지 주요 이점을 제공합니다.
- 불필요한 논쟁 감소 (Reduced Bikeshedding): 일반적인 설계 질문에 대한 답변을 제공함으로써 JSON:API는 팀이 API 설계의 사소한 부분에 대해 논쟁하는 대신 애플리케이션의 핵심 비즈니스 로직에 집중할 수 있도록 합니다.
- 생산성 향상: 표준화된 형식은 API 생산자와 소비자 모두에게 사용자 지정 코드를 줄입니다. 클라이언트 라이브러리는 모든 JSON:API 준수 서비스와 상호 작용하는 데 필요한 많은 상용구 코드를 처리하도록 개발될 수 있습니다.
- 향상된 검색 가능성 및 사용성: 일관된 링크 구조와 리소스 및 관계의 명확한 구분은 API를 더 쉽게 이해하고 탐색할 수 있도록 합니다.
- 최적화된 데이터 전송: 스파스 필드셋(sparse fieldsets) 및 복합 문서(compound documents)와 같은 기능을 통해 클라이언트는 필요한 데이터만 요청하여 페이로드 크기를 최소화하고 HTTP 요청 수를 줄일 수 있습니다.
- 쉬운 캐싱: 이 사양은 표준 HTTP 캐싱 메커니즘 사용을 권장합니다.
- 언어 독립적: JSON 기반이기 때문에 본질적으로 언어 독립적이며 다양한 기술 스택에 걸쳐 광범위한 채택을 용이하게 합니다.
핵심 개념: JSON:API 문서의 구성 요소
JSON:API의 핵심은 리소스(resources) 개념을 중심으로 돌아갑니다. 리소스는 "아티클", "사용자" 또는 "제품"과 같은 특정 유형의 개별 레코드입니다. 요청이든 응답이든 모든 JSON:API 문서는 특정 구조를 따릅니다.
문서 구조: 최상위 멤버
JSON:API 문서는 다음 최상위 멤버 중 하나 이상을 반드시 포함해야 하는 JSON 객체입니다.
data
: 문서의 "기본 데이터"입니다. 다음 중 하나일 수 있습니다.- 단일 리소스 객체(resource object) (예: 특정 아티클을 가져올 때).
- 리소스 객체의 배열 (예: 아티클 컬렉션을 가져올 때).
- 단일 리소스 식별자 객체(resource identifier object) (일대일(to-one) 관계를 나타낼 때).
- 리소스 식별자 객체의 배열 (일대다(to-many) 관계를 나타낼 때).
null
(예: 일대일 관계가 비어 있거나 존재하지 않는 단일 리소스에 대한 요청인 경우).- 빈 배열
[]
(예: 일대다 관계가 비어 있거나 컬렉션 요청 결과가 없는 경우). errors
: 처리 오류에 대한 세부 정보를 제공하는 오류 객체(error object)의 배열입니다.errors
멤버가 있는 경우data
멤버는 없어야 합니다.meta
: 사양의 나머지 부분에 맞지 않는 비표준 메타 정보를 포함하는 메타 객체(meta object)입니다.
또한 문서는 다음 최상위 멤버를 포함할 수도 있습니다.
jsonapi
: 서버 구현을 설명하는 객체입니다.version
(지원되는 최고 JSON:API 버전),ext
(적용된 확장 기능의 URI 배열),profile
(적용된 프로필의 URI 배열)을 포함할 수 있습니다.links
: 기본 데이터와 관련된 링크 객체(links object)입니다. 여기에는 자체 링크(self-links), 관련 리소스 링크 및 페이지네이션 링크가 포함될 수 있습니다.included
: 기본 데이터 및/또는 서로 관련된 리소스 객체의 배열입니다. 관련 리소스를 사이드로딩하여 HTTP 요청 수를 줄이기 위한 "복합 문서(compound documents)"에 사용됩니다.
리소스 객체: 데이터 표현
리소스 객체는 JSON:API의 초석이며 다음을 반드시 포함해야 합니다.
type
: 리소스 유형을 식별하는 문자열입니다 (예:"articles"
,"users"
). 이는 리소스의 네임스페이스를 지정하고 서로 다른 유형 간의 ID 충돌을 방지하는 데 도움이 됩니다.id
: 해당 유형 내에서 리소스를 고유하게 식별하는 문자열입니다.
리소스 객체는 다음을 포함할 수도 있습니다.
attributes
: 리소스에 특정한 데이터를 포함하는 속성 객체(attributes object)입니다.attributes
객체의 키는 리소스의 속성(예: 아티클의"title"
,"body"
)을 나타냅니다. 관계는attributes
객체에 표현되어서는 안 됩니다.relationships
: 리소스와 다른 리소스 간의 연결을 설명하는 관계 객체(relationships object)입니다.links
: 리소스의 정식 URL을 가리키는self
링크와 같이 리소스와 관련된 링크를 포함하는 링크 객체입니다.meta
: 리소스에 대한 비표준 메타 정보를 포함하는 메타 객체입니다.
리소스 객체 예시:JSON
{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API Unveiled",
"body": "A deep dive into the specification...",
"created_at": "2025-05-15T10:00:00Z",
"updated_at": "2025-05-16T14:30:00Z"
},
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "users", "id": "42" }
},
"comments": {
"links": {
"self": "/articles/1/relationships/comments",
"related": "/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
},
"links": {
"self": "/articles/1"
}
}
리소스 식별자 객체
리소스 식별자 객체는 type
과 id
만 포함하는 리소스의 최소 표현입니다. 전체 리소스 객체를 포함하지 않고 다른 리소스로 연결하기 위해 관계 객체 내에서 사용됩니다.
리소스 식별자 객체 예시:JSON
{ "type": "users", "id": "42" }
링크 객체
링크 객체는 API 탐색을 위한 URL을 제공합니다. 일반적인 링크 멤버는 다음과 같습니다.
self
: 리소스 또는 문서 자체를 나타내는 링크입니다.related
: 관련 리소스 또는 컬렉션에 대한 링크입니다. 실제 관련 데이터를 가져오기 위해 관계에서 자주 사용됩니다.- 페이지네이션 링크(Pagination Links): 페이지네이션된 컬렉션을 탐색하기 위한
first
,last
,prev
,next
.
링크는 다음과 같이 표현될 수 있습니다.
- URL을 포함하는 단순 문자열.
href
(문자열 URL)를 반드시 포함해야 하며 선택적으로rel
(관계 유형),describedby
(설명 문서 링크),title
,type
(대상 미디어 유형),hreflang
및meta
객체를 포함할 수 있는 링크 객체.
링크 객체 예시 (관계 내):JSON
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
}
메타 객체
메타 객체는 비표준 메타 정보를 포함할 수 있도록 합니다. 이는 임의의 키-값 쌍이 될 수 있습니다. 예를 들어, meta
객체는 데이터와 관련된 저작권 정보 또는 타임스탬프를 포함할 수 있습니다.
메타 객체 예시:JSON
"meta": {
"copyright": "Copyright 2025 Example Corp.",
"authors": ["John Doe"]
}
콘텐츠 협상: 올바른 언어로 소통하기
JSON:API는 자체 미디어 유형인 application/vnd.api+json
을 정의합니다.
Accept
헤더: 클라이언트는 JSON:API 준수 응답을 기대함을 나타내기 위해application/vnd.api+json
미디어 유형과 함께 이 헤더를 반드시 보내야 합니다. 서버가Accept
헤더의 어떤 미디어 유형도 만족시킬 수 없는 경우406 Not Acceptable
상태로 응답해야 합니다.Content-Type
헤더: 클라이언트와 서버는 본문에 JSON:API 문서를 포함하는 모든 요청 및 응답에 대해application/vnd.api+json
미디어 유형과 함께 이 헤더를 반드시 사용해야 합니다. 요청이application/vnd.api+json
(또는 서버가 지원하는 다른 등록된 미디어 유형) 이외의Content-Type
을 지정하고 본문을 포함하는 경우, 서버는415 Unsupported Media Type
상태로 응답해야 합니다. 요청이Content-Type: application/vnd.api+json
을 지정했지만 본문이 유효한 JSON:API 문서가 아닌 경우, 서버는400 Bad Request
로 응답해야 합니다.
서버는 표준 콘텐츠 협상을 통해 application/vnd.api+json
과 함께 다른 미디어 유형을 지원할 수 있습니다.
데이터 가져오기: 리소스 및 컬렉션 검색
JSON:API는 클라이언트가 필요에 따라 데이터를 정확하게 검색할 수 있는 강력한 메커니즘을 제공합니다.
개별 리소스 가져오기
단일 리소스를 가져오려면 클라이언트는 해당 리소스를 나타내는 엔드포인트에 GET
요청을 보냅니다.
요청:
GET /articles/1
Accept: application/vnd.api+json
성공적인 응답 (200 OK):JSON
{
"links": {
"self": "/articles/1"
},
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API Rocks!"
}
// ... other attributes and relationships
}
}
리소스가 존재하지 않는 경우 서버는 404 Not Found
를 반환해야 합니다. 일대일 관련 리소스 링크를 가져왔는데 관계가 비어 있는 경우 기본 데이터는 null
이 됩니다.
리소스 컬렉션 가져오기
리소스 컬렉션을 가져오려면 클라이언트는 해당 컬렉션을 나타내는 엔드포인트에 GET
요청을 보냅니다.
요청:
GET /articles
Accept: application/vnd.api+json
성공적인 응답 (200 OK):JSON
{
"links": {
"self": "/articles",
"next": "/articles?page[offset]=10",
"last": "/articles?page[offset]=50"
},
"data": [
{
"type": "articles",
"id": "1",
"attributes": { "title": "Article 1" }
// ...
},
{
"type": "articles",
"id": "2",
"attributes": { "title": "Article 2" }
// ...
}
// ... more articles
]
}
컬렉션이 비어 있는 경우 data
멤버는 빈 배열 []
이 됩니다.
관계: 리소스 연결
관계는 대부분의 데이터 모델에서 기본적인 부분입니다. JSON:API는 관계를 정의하고 상호 작용하는 명확한 방법을 제공합니다.
관계 표현
관계는 리소스의 relationships
객체 내에서 정의됩니다. relationships
객체의 각 항목은 고유한 관계("author", "comments" 등)를 나타냅니다.
관계 객체는 다음 중 하나 이상을 반드시 포함해야 합니다.
links
:self
및related
링크를 포함합니다.self
링크 (관계 URL)는 관계 자체를 조작할 수 있도록 합니다 (예: 일대다 관계에서 항목 추가/제거). 가져오면 관련 리소스에 대한 리소스 식별자 객체를 반환합니다.related
링크 (관련 리소스 URL)는 관련 리소스 객체를 직접 가져올 수 있도록 합니다.data
: 리소스 연결(resource linkage) (리소스 식별자 객체)을 포함합니다.- 일대일(to-one) 관계의 경우: 단일 리소스 식별자 객체 또는
null
. - 일대다(to-many) 관계의 경우: 리소스 식별자 객체의 배열 또는 빈 배열
[]
. meta
: 관계에 대한 비표준 정보를 위한 메타 객체입니다.
"author" (일대일) 및 "comments" (일대다) 관계 예시:JSON
"relationships": {
"author": {
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
},
"data": { "type": "users", "id": "42" }
},
"comments": {
"links": {
"self": "/articles/1/relationships/comments",
"related": "/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
관계 가져오기
클라이언트는 제공된 링크를 사용하여 관계 자체 또는 관련 리소스에 대한 정보를 가져올 수 있습니다.
관계 연결 가져오기 (self link):
GET /articles/1/relationships/comments
Accept: application/vnd.api+json
이는 아티클 "1"과 관련된 댓글에 대한 리소스 식별자 객체 컬렉션을 반환합니다.
관련 리소스 가져오기 (related link):
GET /articles/1/comments
Accept: application/vnd.api+json
이는 아티클 "1"과 관련된 전체 댓글 리소스 객체 컬렉션을 반환합니다.
데이터 검색 최적화
JSON:API는 대역폭을 최소화하고 클라이언트 측 성능을 향상시키기 위해 데이터 검색 방법을 최적화하는 여러 기능을 제공합니다.
복합 문서: include
를 사용하여 HTTP 요청 줄이기
관련 리소스를 가져오기 위해 서버에 여러 번 왕복하는 것을 방지하기 위해 JSON:API는 클라이언트가 include
쿼리 매개변수를 사용하여 관련 리소스를 기본 응답에 포함하도록 요청할 수 있도록 합니다. 그러면 서버는 이러한 리소스를 최상위 included
배열에 사이드로딩합니다.
아티클을 가져오고 해당 작성자 및 댓글을 포함하는 요청:
GET /articles/1?include=author,comments
Accept: application/vnd.api+json
응답 (200 OK):JSON
{
"data": {
"type": "articles",
"id": "1",
"attributes": { "title": "..." },
"relationships": {
"author": {
"data": { "type": "users", "id": "42" }
},
"comments": {
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
},
"included": [
{
"type": "users",
"id": "42",
"attributes": { "name": "John Doe" }
},
{
"type": "comments",
"id": "5",
"attributes": { "body": "Great article!" }
},
{
"type": "comments",
"id": "12",
"attributes": { "body": "Very informative." }
}
]
}
include
매개변수는 쉼표로 구분된 관계 경로 목록을 사용합니다.- 점 표기법(예:
include=comments.author
)을 사용하여 중첩된 관계를 포함할 수 있습니다. - 엔드포인트가
include
를 지원하지 않으면400 Bad Request
를 반드시 반환해야 합니다. - 서버는 요청되지 않은 리소스를
included
섹션에 포함해서는 안 됩니다.
스파스 필드셋: 필요한 필드만 가져오기
클라이언트는 fields[TYPE]
쿼리 매개변수를 사용하여 주어진 유형의 리소스에 대해 특정 필드(속성 및 관계)만 반환되도록 요청할 수 있습니다. 이는 페이로드 크기를 줄입니다.
아티클을 가져오되 제목과 작성자 관계만 가져오는 요청:
GET /articles?fields[articles]=title,author
Accept: application/vnd.api+json
응답 (200 OK):JSON
{
"data": [
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Article 1"
},
"relationships": {
"author": {
"data": { "type": "users", "id": "42" }
}
}
}
// ... other articles with only title and author
]
}
id
와type
은 항상 포함됩니다.- 클라이언트가 존재하지 않는 필드를 요청하는 경우 서버는 이를 반드시 무시해야 합니다.
- 클라이언트가 관계인 필드만 요청하는 경우
attributes
멤버는 생략될 수 있습니다.
정렬
클라이언트는 sort
쿼리 매개변수를 사용하여 기본 데이터를 정렬하도록 요청할 수 있습니다.
생성 날짜(내림차순) 및 제목(오름차순)으로 정렬된 아티클을 가져오는 요청:
GET /articles?sort=-created_at,title
Accept: application/vnd.api+json
- 선행 하이픈(
-
)은 내림차순을 나타냅니다. 그렇지 않으면 오름차순입니다. - 서버는 정렬에 사용할 수 있는 속성을 정의합니다. 지원되지 않는 속성에 대한 정렬 요청은
400 Bad Request
를 반환해야 합니다.
페이지네이션
JSON:API는 다양한 페이지네이션 전략을 지원합니다. 사양은 최상위 links
객체에 페이지네이션 링크(first
, prev
, next
, last
)가 어떻게 나타나야 하는지를 정의합니다. 실제 페이지네이션 전략(예: 페이지 기반, 오프셋 기반, 커서 기반)은 page[number]
, page[size]
, page[offset]
, page[limit]
또는 page[cursor]
와 같은 쿼리 매개변수를 사용하여 서버에 의해 결정됩니다.
페이지 기반 페이지네이션 링크 예시:JSON
"links": {
"self": "/articles?page[number]=2&page[size]=10",
"first": "/articles?page[number]=1&page[size]=10",
"prev": "/articles?page[number]=1&page[size]=10",
"next": "/articles?page[number]=3&page[size]=10",
"last": "/articles?page[number]=5&page[size]=10"
}
클라이언트는 자체 페이지네이션 URL을 구성하는 대신 제공된 링크를 사용해야 합니다.
필터링
사양은 데이터 필터링을 위해 filter
쿼리 매개변수를 예약합니다. 하지만 특정 필터링 전략을 의무화하지는 않습니다. 서버는 filter[attribute]=value
와 같은 전략 또는 더 복잡한 표현식 기반 필터링을 구현할 수 있습니다.
예시 (JSON:API에서 권장하지만 의무 사항은 아님):
GET /comments?filter[post]=1 (ID가 1인 게시물의 댓글 가져오기)
GET /comments?filter[post]=1,2&filter[author]=12 (게시물 1 또는 2의 댓글을 작성자 12가 작성한 것으로 가져오기)
클라이언트는 API의 문서를 참조하여 특정 필터링 기능을 이해해야 합니다.
데이터 수정: 리소스 생성, 업데이트 및 삭제
JSON:API는 데이터 조작 작업에 대한 명확한 프로토콜을 정의합니다.
리소스 생성
리소스를 생성하려면 클라이언트는 리소스 컬렉션을 나타내는 URL에 POST
요청을 보냅니다. 요청 본문은 type
과 선택적으로 attributes
및 relationships
를 포함하는 단일 리소스 객체를 반드시 포함해야 합니다. 클라이언트는 새 리소스에 대한 id
를 제공해서는 안 됩니다 (클라이언트 생성 ID가 지원되고 활성화된 경우는 제외).
요청:
POST /articles
Accept: application/vnd.api+json
Content-Type: application/vnd.api+jsonJSON
{
"data": {
"type": "articles",
"attributes": {
"title": "New Article Title",
"body": "Content of the new article."
},
"relationships": {
"author": {
"data": { "type": "users", "id": "42" }
}
}
}
}
성공적인 응답:
201 Created
: 리소스가 성공적으로 생성된 경우. 응답은 새로 생성된 리소스의 URL을 식별하는Location
헤더를 반드시 포함해야 합니다. 응답 본문은 서버 할당id
를 포함하여 새로 생성된 리소스를 포함해야 합니다.202 Accepted
: 생성 요청이 처리를 위해 수락되었지만 처리가 아직 완료되지 않은 경우 (예: 비동기 작업). 응답에는 상태를 모니터링하기 위한 링크가 포함될 수 있습니다.204 No Content
: 리소스가 성공적으로 생성되었지만 서버가 응답 본문에 리소스 표현을 반환하지 않기로 선택한 경우.Location
헤더는 여전히 필요합니다.
이미 존재하는 클라이언트 생성 ID로 리소스를 생성하려는 시도가 있고 서버가 POST
를 통한 업데이트를 지원하지 않는 경우, 서버는 409 Conflict
를 반드시 반환해야 합니다.
리소스 업데이트
리소스는 PATCH
HTTP 메서드를 사용하여 업데이트됩니다. 요청에는 업데이트할 리소스의 id
가 반드시 포함되어야 합니다. 요청 본문에는 type
, id
, 그리고 업데이트할 attributes
및/또는 relationships
를 포함하는 리소스 객체가 포함됩니다.
아티클의 제목과 관계 중 하나를 업데이트하는 요청:
PATCH /articles/1
Accept: application/vnd.api+json
Content-Type: application/vnd.api+jsonJSON
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "Updated Article Title"
},
"relationships": {
"tags": {
"data": [
{ "type": "tags", "id": "3" },
{ "type": "tags", "id": "4" }
]
}
}
}
}
업데이트의 주요 사항:
- 부분 업데이트:
PATCH
요청은 부분적이어야 합니다. 요청에 있는 필드만 업데이트되어야 합니다. 포함되지 않은 필드는 변경되지 않고 유지되어야 합니다. - 관계 업데이트:
- 일대일(To-one): 관계의
data
에 리소스 식별자 객체 또는null
을 제공합니다. - 일대다(To-many): 리소스 식별자 객체의 배열을 제공합니다. 이는 기존 관계 멤버를 완전히 대체합니다. 전체 세트를 대체하지 않고 멤버를 추가하거나 제거하려면 전용 관계 엔드포인트(
/articles/1/relationships/tags
)를POST
(추가),DELETE
(제거) 또는PATCH
(전체 대체)와 함께 사용해야 합니다. - 서버는 요청에 지정되지 않은 속성 또는 관계를 업데이트해서는 안 됩니다.
성공적인 응답:
200 OK
: 업데이트가 성공적이고 서버가 업데이트된 리소스의 표현을 반환하는 경우.202 Accepted
: 업데이트 요청이 처리를 위해 수락되었지만 아직 완료되지 않은 경우.204 No Content
: 업데이트가 성공적이지만 서버가 표현을 반환하지 않기로 선택한 경우.
업데이트할 리소스가 존재하지 않는 경우 서버는 404 Not Found
를 반드시 반환해야 합니다.
관계를 직접 업데이트하기
JSON:API는 기본 리소스의 속성에 영향을 주지 않고 관계를 관리하는 특정 방법을 제공합니다.
- 일대일 관계 업데이트:
PATCH /articles/1/relationships/authorContent-Type: application/vnd.api+json
JSON
{
"data": { "type": "users", "id": "24" } // Assign new author or null to clear
}
- 일대다 관계 업데이트 (완전 대체):
PATCH /articles/1/relationships/commentsContent-Type: application/vnd.api+json
JSON
{
"data": [
{ "type": "comments", "id": "101" },
{ "type": "comments", "id": "102" }
]
}
- 일대다 관계에 추가:
POST /articles/1/relationships/commentsContent-Type: application/vnd.api+json
JSON
{
"data": [
{ "type": "comments", "id": "103" }
]
}
- 일대다 관계에서 제거:
DELETE /articles/1/relationships/commentsContent-Type: application/vnd.api+json
JSON
{
"data": [
{ "type": "comments", "id": "5" }
]
}
관계 업데이트가 성공하면 일반적으로 200 OK
또는 204 No Content
를 반환합니다.
리소스 삭제
리소스를 삭제하려면 클라이언트는 해당 리소스의 엔드포인트에 DELETE
요청을 보냅니다.
요청:
DELETE /articles/1
Accept: application/vnd.api+json
성공적인 응답:
204 No Content
: 삭제가 성공적이고 응답 본문이 반환되지 않은 경우.200 OK
: 서버가 삭제에 대한 메타 정보를 반환하는 경우.202 Accepted
: 삭제 요청이 처리를 위해 수락된 경우.
리소스가 존재하지 않는 경우 서버는 404 Not Found
를 반환해야 합니다. 사양은 삭제 시 관련 리소스 또는 관계가 어떻게 처리되어야 하는지(예: 연쇄 삭제) 지시하지 않습니다. 이는 구현 세부 사항입니다.
오류 처리
오류가 발생하면 서버는 적절한 HTTP 상태 코드(클라이언트 오류의 경우 4xx, 서버 오류의 경우 5xx)를 반드시 사용해야 합니다. 응답 본문은 JSON:API 오류 문서를 포함해야 합니다.
오류 문서는 오류 객체 배열인 최상위 errors
멤버를 포함합니다. 각 오류 객체는 다음을 포함할 수 있습니다.
id
: 이 특정 문제 발생에 대한 고유 식별자입니다.links
: 다음을 포함하는 링크 객체입니다.about
: 이 특정 문제 발생에 대한 추가1 세부 정보로 연결되는 링크입니다.type
: 이 특정 오류가 인스턴스인 오류 유형을 식별하는 링크입니다.status
: 이 문제에 적용 가능한 HTTP 상태 코드이며 문자열로 표현됩니다.code
: 애플리케이션별 오류 코드이며 문자열로2 표현됩니다.title
: 문제에 대한 짧고 사람이 읽을 수 있는 요약입니다.3detail
: 이 특정 문제 발생에 대한 사람이 읽을 수 있는 설명입니다.source
: 요청 문서에서 오류 소스에 대한 참조를 포함하는 객체입니다.4pointer
: 요청 문서의 관련 엔티티에 대한 JSON 포인터 [RFC6901]입니다.parameter
: 오류를 발생시킨 URI 쿼리 매개변수를 나타내는 문자열입니다.header
:5 오류를 발생시킨 단일 요청 헤더의 이름을 나타내는 문자열입니다.meta
: 오류에 대한 비표준 메타 정보를 포함하는 메타 객체입니다.
오류 응답 예시 (422 Unprocessable Entity):JSON
{
"errors": [
{
"status": "422",
"source": { "pointer": "/data/attributes/email" },
"title": "Invalid Attribute",
"detail": "The email address is not valid."
},
{
"status": "422",
"source": { "pointer": "/data/relationships/author" },
"title": "Invalid Relationship Value",
"detail": "Author with ID '999' does not exist."
}
]
}
서버 측 고려 사항 및 모범 사례
핵심 사양은 포괄적이지만 JSON:API는 URL 설계 및 멤버 명명과 같은 측면에 대한 권장 사항도 제공합니다.
URL 설계
- 리소스 컬렉션에는 복수 명사를 사용합니다 (예:
/articles
). - 개별 리소스에는
/articles/{id}
를 사용합니다. - 관계 자체 링크에는
/articles/{id}/relationships/{relationshipName}
를 사용합니다. - 관련 리소스 링크에는
/articles/{id}/{relationshipName}
를 사용합니다.
멤버 명명
- 멤버 이름(키)은 카멜 케이스(예:
firstName
)이거나 단어 구분 기호로 하이픈/밑줄을 사용해야 합니다 (예:first-name
또는first_name
). API 내에서 일관성이 중요합니다. 사양 자체는 자체 쿼리 매개변수에 대해 케밥 케이스(kebab-case)를 사용합니다 (예:page[number]
). - 멤버 이름은 ASCII 영숫자, 하이픈 및 밑줄만 포함해야 합니다.
사양 확장: 확장 기능 및 프로필
JSON:API는 진화하는 요구 사항과 특정 사용 사례를 충족하도록 확장 가능하게 설계되었습니다.
확장 기능
확장 기능은 기본 사양에서 다루지 않는 새로운 기능을 도입할 수 있습니다. 예를 들어, "Atomic Operations" 확장 기능은 단일 원자적 요청으로 여러 작업(생성, 업데이트, 삭제)을 수행할 수 있도록 합니다. 클라이언트와 서버 모두 확장 기능을 사용하려면 이해해야 합니다. 서버가 지원되지 않는 확장 기능을 포함하는 요청을 받으면 적절한 오류로 반드시 응답해야 합니다.
프로필
프로필은 특정 사용 사례(예: 타임스탬프를 처리하는 특정 방법 또는 일반적인 meta
속성 집합)에 대해 기본 사양 위에 일련의 컨벤션을 정의합니다. 확장 기능과 달리 프로필은 한쪽에서 이해하지 못해도 안전하게 무시할 수 있습니다. 이는 핵심 사양을 변경하거나 보편적인 지원을 의무화하지 않고도 일반적인 패턴에 대한 상호 운용성을 촉진하기 위한 것입니다.
서버는 최상위 jsonapi
객체에서 지원되는 확장 기능 및 프로필을 광고할 수 있습니다. 이를 통해 클라이언트는 이러한 기능을 검색하고 그에 따라 요청을 조정할 수 있습니다.
JSON:API의 미래
JSON:API는 커뮤니티의 기여와 새로운 API 설계 과제를 해결해야 하는 필요성에 의해 계속 발전하고 있습니다. 설정보다 컨벤션, 효율성 및 개발자 경험에 대한 초점은 최신 API 구축을 위한 선도적인 표준으로서의 입지를 굳혔습니다. JSON:API를 채택함으로써 개발 팀은 모호성을 크게 줄이고 상호 운용성을 향상시키며 API 개발 및 소비 속도를 가속화할 수 있습니다.
이 상세한 탐구는 JSON:API 사양의 대부분을 다룹니다. 이러한 원칙을 이해하고 구현함으로써 개발자는 기능적일 뿐만 아니라 깔끔하고 일관적이며 작업하기 즐거운 API를 만들 수 있으며, 궁극적으로 보다 생산적이고 협력적인 API 생태계를 조성할 수 있습니다.