μ΅κ·Ό API κ·κ²©λ€μ λν΄μ μμ보λ μ€ JSON APIλΌλ κ²μ λ³΄κ² λμλ€. REST APIκ° μ΄λ―Έ JSON ν¬λ§·μ μ¬μ©νκ³ μλλ° JSONμΌλ‘ μλ΅νλ©΄ κ·Έκ² JSON APIμΈκ° μΆμμ§λ§ μμ보λ κ½€ ν₯λ―Έλ‘μμ μ 리λ₯Ό ν΄λ³Έλ€.
JSON API
μ μ νκΈ°λ JSON:API μΈλ―ν μ΄ νλ‘ν μ½μ μ΄λ¦ κ·Έλλ‘ JSON νμμ μ¬μ©νλ νμ€νλ API κ·κ²©μ΄λ€.
2013λ
μ μ²μ λ°μλλ―ν ν΄λΉ νλ‘ν μ½μ 2015λ
μ v1.0 μ΄ μμ±λκ³ 2022λ
μ v1.1μ΄ μμ±λμλ€κ³ νλ€.
μ΄λ―Έ λλΆλΆμ μΉ APIλ€μ΄ JSONμ μ¬μ©νκ³ λ μμ§λ§ JSON λ°μ΄ν° μ체μ ν¬λ§·μ κ°λ°μλ€λ§λ€ λ€ λ€λ₯΄κ² μ¬μ©νκ³ μλλ° JSON:APIλ κ·Έ ꡬ쑰μ κ·μΉμ λͺ
ννκ² μ μν νμ€μ μ μνλ€.
JSON:APIμ ꡬ쑰
μΌλ°μ μΈ REST APIμμλ μλ²λ§λ€ JSON μλ΅ ννκ° μ κ°κ°μ΄μ§λ§ JSON:APIλ μλ νλͺ©λ€μ νμ€ννλ€.
- data: μ€μ 리μμ€ λ°μ΄ν°
- atrributes: 리μμ€μ μμ±
- relationships: λ€λ₯Έ 리μμ€μμ κ΄κ³
- included: κ΄κ³μ λ°λΌ ν¬ν¨λλ λ€λ₯Έ 리μμ€
- meta, links, errors: λΆκ° μ 보, λ§ν¬, μλ¬ μ 보 λ±
JSON:API μλ΅ μμ
{ "links": { "self": "http://example.com/articles", "next": "http://example.com/articles?page[offset]=2", "last": "http://example.com/articles?page[offset]=10" }, "data": [{ "type": "articles", "id": "1", "attributes": { "title": "JSON:API paints my bikeshed!" }, "relationships": { "author": { "links": { "self": "http://example.com/articles/1/relationships/author", "related": "http://example.com/articles/1/author" }, "data": { "type": "people", "id": "9" } }, "comments": { "links": { "self": "http://example.com/articles/1/relationships/comments", "related": "http://example.com/articles/1/comments" }, "data": [ { "type": "comments", "id": "5" }, { "type": "comments", "id": "12" } ] } }, "links": { "self": "http://example.com/articles/1" } }], "included": [{ "type": "people", "id": "9", "attributes": { "firstName": "Dan", "lastName": "Gebhardt", "twitter": "dgeb" }, "links": { "self": "http://example.com/people/9" } }, { "type": "comments", "id": "5", "attributes": { "body": "First!" }, "relationships": { "author": { "data": { "type": "people", "id": "2" } } }, "links": { "self": "http://example.com/comments/5" } }, { "type": "comments", "id": "12", "attributes": { "body": "I like XML better" }, "relationships": { "author": { "data": { "type": "people", "id": "9" } } }, "links": { "self": "http://example.com/comments/12" } }] }
μ μμλ https://jsonapi.org/ μμ 보μ¬μ£Όλ κ°μμ λΈλ‘κ·Έ μλ΅μΌλ‘ articles 리μμ€μ λν μμ² κ²°κ³Όλ€. κΈ°λ³Έμ μΌλ‘ νμ
κ³Ό idλ₯Ό ν¬ν¨νκ³ , article κ°μ²΄μ λν μμ± μ 보λ attributes νλͺ© μμ ν¬ν¨λμ΄ μλ€.
articlesμ μ°κ΄λ 리μμ€ νμ
μΌλ‘λ authorμ commentsκ° μλ κ² κ°μλ°, ν΄λΉ 리μμ€λ€μ μμ±μ included νλͺ© μλμ ν¬ν¨λμ΄ λ°νλκ³ μλ€.
μ΄λ λ― JSON:APIλ μ°κ΄ λ°μ΄ν°λ€μ μ 보λ₯Ό ν¬ν¨νμ¬ λ°νν¨μΌλ‘μ¨ ν΄λΌμ΄μΈνΈμμ λ°λ³΅μ μΈ μμ²μ 보λ΄μ§ μμλ λλλ‘ νκ³ μμΌλ©° 리μμ€μ λν μ 보 λ° κ΄κ³ ꡬ쑰λ μΌκ΄μ μΌλ‘ μ λ¬νκΈ° λλ¬Έμ ν΄λΌμ΄μΈνΈ μͺ½ ꡬνμ λ¨μν ν΄μ€λ€.
JSON:APIμ μ£Όμ κ·μΉ
JSON:APIλ λ°μ΄ν° ꡬ쑰μ λν μ μ λΏλ§ μλλΌ API κ·κ²©μΌλ‘μ μ§μΌμΌ ν κ·μΉλ€μ λν΄μλ λͺ
μΈνκ³ μλ€.
HTTP λ©μλ
κΈ°λ³Έμ μΌλ‘ REST μμΉμ μ€μνμ¬ μμ²μ λ°λΌ GET, POST, PATCH, DELETE λ©μλλ₯Ό μ¬μ©νλλ‘ νλ©°, μ²λ¦¬ κ²°κ³Όμ λ°λΌ μ¬μ©ν΄μΌ νλ HTTP Status Codeμ λν μ¬νκΉμ§ μ μνκ³ μλ€.
Content-Type
JSON:APIλ μ체 MIME νμ
μ IANAμ λ±λ‘νμ¬ νμ
application/vdn.api+json μ μ¬μ©νλλ‘ νκ³ μλ€.쿼리 νλΌλ―Έν° ν¨λ°λ¦¬
쿼리 νλΌλ―Έν° μ¬μ©μ, κ³΅ν΅ κ΄μ¬μ¬λ₯Ό κ°λ 쿼리 νλΌλ―Έν°λ₯Ό νλμ κ·Έλ£ΉμΌλ‘ νννλ 쿼리 νλΌλ―Έν° ν¨λ°λ¦¬λΌλ κ°λ
μ μ¬μ©νλλ‘ μ μνκ³ μλ€.
μλ₯Όλ€μ΄ νμ΄μ§μ΄ νμν κ²½μ° νμ΄μ§ λ²νΈμ νμ΄μ§ λΉ λ¦¬μμ€ κ°μλ₯Ό λ³λ νλΌλ―Έν°λ‘ λΆλ¦¬νλ λμ μλμ κ°μ κ΅¬μ‘°λ‘ pageλΌλ κ°μ‘± κ°λ
μλμ νμ μμ±λ€μ λνλ΄μ΄ μ¬μ©νλ€.
/?page[offset]=0&page[limit]=10
URL λμμΈ
λ°λμ λ°λΌμΌνλ κ·μΉμ μλμ§λ§ JSON:API μ¬μ©μ URL λμμΈμ μ΄λ»κ² νλ κ²μ΄ μ’μμ§μ λν μ§μΉ¨λ€μ΄ ν¬ν¨λμ΄ μλ€. κΈ°λ³Έμ μΌλ‘λ url λν 리μμ€λ₯Ό μ€μ¬μΌλ‘ μ€κ³νμ¬ κ΄κ³λ₯Ό νννλλ‘ μΆμ²νλ€.
// κΈ°λ³Έ 리μμ€ κ²½λ‘ GET /articles GET /articles/1 // κ΄κ³ λ°μ΄ν° μ§μ μ‘°ν κ²½λ‘ GET /articles/1/relationships/author // ν¬ν¨λ 리μμ€ μ‘°ν GET /articles/1/author // μ€μ²© κ΄κ³ GET /articles/1/comments/2
μλ¬ μλ΅
μμ²μ μ λλ‘ μ²λ¦¬ν μ μμ΄ μλ¬λ₯Ό λ°νν΄μΌ ν κ²½μ°μ λν΄μλ JSON:APIλ κ·μΉμ μ μνκ³ μλ€.
κΈ°λ³Έμ μΌλ‘ μ΅μμμ errors ν€λ₯Ό λκ³ λ°°μ΄λ‘ ꡬ쑰ν λ μλ¬ μ 보λ₯Ό ν¬ν¨μν€λλ‘ νλ€.
{ "errors": [ { "status": "400", "title": "Invalid Attribute", "detail": "First name must contain at least three characters." } ] }
κ° μ€λ₯ κ°μ²΄λ λ€μ νλλ₯Ό μ νμ μΌλ‘ ν¬ν¨ν μ μλ€.
- id: μλ¬ νΈλνΉμ μν μ€λ₯ μλ³μ
- links: μ€λ₯ μ°κ΄ λ¬Έμ λ§ν¬
- status: HTTP μν μ½λ
- code: μλ² λ΄λΆμμ μ μν μλ¬ μ½λ
- title: μ€λ₯μ μ§§μ μμ½
- detail: μ€λ₯μ ꡬ체μ μΈ μ€λͺ
- source: μ€λ₯κ° λ°μν μμΈ μμΉ
- meta: μΆκ°μ μΈ μ»¨ν μ€νΈ λ°μ΄ν°
νμ΄λ€μ΄μ
κ°μ₯ μ²μ 보μ¬μ€ μμμμ λνλλ― νμ΄μ§λ€μ΄μ
μ μ© μ links νλλ₯Ό μ¬μ©ν΄μ 리μμ€ νμ΄μ§λ€μ΄μ
λ§ν¬λ₯Ό μ νμ μΌλ‘ μ 곡ν μ μλ€. νμ΄μ§λ€μ΄μ
λ§ν¬ μ 곡 μμλ λ°λμ self, first, last, prev, next ν€λ₯Ό μ¬μ©ν΄μ μ 곡ν΄μΌ νλ€.
{ "links": { "self": "http://example.com/articles", "next": "http://example.com/articles?page[offset]=2", "last": "http://example.com/articles?page[offset]=10" } }
JSON:APIμ μ₯λ¨μ
μ₯μ
- νμ€νλ μλ΅ κ΅¬μ‘°: λͺ¨λ APIκ° κ°μ ννλ₯Ό κ°μ§λ―λ‘, ν΄λΌμ΄μΈνΈ ꡬνμ΄ μ½λ€.
- λΆνμν μμ² κ°μ: included νλλ‘ κ΄λ ¨ λ°μ΄ν°λ₯Ό ν λ²μ μ λ¬ν μ μλ€.
- κ΄κ³ν λ°μ΄ν° ννμ΄ μ©μ΄: relationshipsλ₯Ό ν΅ν΄ κ΄κ³λ₯Ό λͺ νν νννλ€.
- μλ¬ μ²λ¦¬ μΌκ΄μ±: errors ν¬λ§·μ ν΅ν΄ ꡬ쑰νλ μλ¬λ₯Ό μ λ¬νλ€.
λ¨μ
- κ΅¬μ‘°κ° λ³΅μ‘νλ€: 리μμ€ κ΄κ³κ° 볡μ‘νμ§ μμ κ°λ¨ν APIμλ κ³Όν μ μλ€.
- μ΄κΈ° μ€μ λΆλ΄: μ μνλ νμ€μ λ§μΆ° μλ΅μ ν¬λ§·ν νκΈ°κ° λΆλ΄μ€λ¬μΈ μ μλ€.
- μ μ°μ± μ ν: κ·μΉμ λͺ¨λ μ€μνλ©° 컀μ€ν μλ΅ κ΅¬μ‘°λ₯Ό λ§λ€κΈ°κ° μ΄λ €μΈ μ μλ€.
JSON:API νμ©μ΄ λμμ΄ λ μ μλ μν©
- μ¬λ¬ μ’ λ₯μ ν΄λΌμ΄μΈνΈ(μΉ, λͺ¨λ°μΌ, API ν΄λΌμ΄μΈνΈ)κ° λμΌ APIλ₯Ό μ¬μ©νλ κ²½μ°
- κ΄κ³ν λ°μ΄ν°(μ: μ¬μ©μ β κ²μκΈ β λκΈ)κ° λ§μ κ²½μ°
- νλ‘ νΈμλκ° GraphQLμ²λΌ κ΄κ³ν μμ²μ λ§μ΄ νλ κ²½μ°
Β