๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๊ฐœ๋ฐœ

Fielding์ด ๋งํ•˜๋Š” ์ง„์งœ REST API

REST API๋ฅผ ๋งŒ๋“  ์žฅ๋ณธ์ธ Fielding์€ 2008๋…„์— REST APIs must be hypertext-driven ๋ผ๋Š” ๊ธ€์„ ์ž‘์„ฑํ–ˆ๋‹ค. ํ•ด๋‹น ๊ธ€์˜ ๋‚ด์šฉ์„ ๋ณด๋ฉด Fielding์ด ์„ค๊ณ„ํ•œ REST API๋Š” ๋‹ค์Œ์˜ ๊ทœ์น™์„ ๋”ฐ๋ผ์•ผํ•œ๋‹ค.

  1. ์ž์›์˜ ์‹๋ณ„์ž์™€ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์€ ๊ตฌ๋ถ„๋˜์–ด์•ผ ํ•œ๋‹ค.
  2. ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ์จ๋ผ.
  3. API ๋ฌธ์„œ์— URL ๋ชฉ๋ก์„ ๋‚˜์—ดํ•˜์ง€ ๋งˆ๋ผ. ๋Œ€์‹ , ์‘๋‹ต ํ˜•ํƒœ๋ฅผ ์ž˜ ์ •์˜ํ•ด๋ผ.
  4. ๊ณ ์ •๋œ ์ž์› ์ด๋ฆ„, ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งˆ๋ผ.
  5. ํƒ€์ž…์ด ์ง€์ •๋œ ์ž์›์„ ๊ฐ€์ง€์ง€ ๋งˆ๋ผ.
  6. ์‚ฌ์ „ ์ง€์‹์—†์ด ์ง„์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ผ.

์ด๋Ÿฌํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฅด์ง€ ์•Š์„๊ฑฐ๋ฉด REST API๋ผ ๋ถ€๋ฅด์ง€๋„ ๋ง๊ณ , ๋‹ค๋ฅธ ์œ ํ–‰์–ด๋ฅผ ๊ณจ๋ผ ์“ฐ๋ผ๋Š” ๋ง๊นŒ์ง€ ํ•  ์ •๋„๋กœ REST API์˜ ํ•ต์‹ฌ์ด๋ผ๊ณ  ๊ฐ•์กฐํ•˜๊ณ  ์žˆ๋‹ค. ๋„๋Œ€์ฒด ์–ด๋–ค ๋‚ด์šฉ์ด๊ธธ๋ž˜ Fielding์ด ์ด๋ ‡๊ฒŒ๊นŒ์ง€ ํ™”๊ฐ€ ๋‚ฌ๋Š”์ง€ ์ข€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž.
 

Fielding์˜ REST API

1. ์ž์›์˜ ์‹๋ณ„์ž์™€ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์€ ๊ตฌ๋ถ„๋˜์–ด์•ผ ํ•œ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋ณดํ†ต ์„œ๋ฒ„์˜ REST API๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด HTTP ๋ฉ”์†Œ๋“œ์™€ ์ž์›์— ํ•ด๋‹นํ•˜๋Š” URL์„ ๊ณตํ†ต์ ์œผ๋กœ ์ •์˜ํ•˜์—ฌ ์ ‘๊ทผ ๊ฒฝ๋กœ๋ฅผ ์—ด์–ด๋‘๊ณ  ์žˆ๋‹ค.

GET /orders/{id}

 
ํ•˜์ง€๋งŒ, Fielding์€ ์ด๋ ‡๊ฒŒ ํ•˜์ง€ ๋ง๋ผ๊ณ  ๋งํ•œ๋‹ค. ์ž์›์„ ์‹๋ณ„ํ•˜๋Š” ๊ฒƒ๊ณผ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ตฌ๋ถ„ํ•˜๋ผ๊ณ  ํ•œ๋‹ค. ์ฆ‰, /orders/{id}์™€ HTTP๋ผ๋Š” ์ ‘๊ทผ ํ”„๋กœํ† ์ฝœ์„ ๊ตฌ๋ถ„ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ž์›์„ ์‹๋ณ„ํ•˜๋Š” ๊ฒฝ๋กœ๋Š” ํ•˜๋‚˜์ง€๋งŒ ์ ‘๊ทผํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ผ ์ˆ˜ ์žˆ๊ณ , ๊ฐ™์€ ์ž์›์ด๋ผ๋„ Accept ํ—ค๋”์— ๋”ฐ๋ผ JSON, XML, HTML ๋“ฑ ๋‹ค๋ฅธ ํ˜•ํƒœ๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

http://api.example.com/orders/42
https://api.example.com/orders/42
ftp://api.example.com/orders/42

 

2. ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ์จ๋ผ.

์–ด๋–ค API๋ฅผ ๋งŒ๋“ค๋•Œ ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์„ ๋ณ€ํ˜•ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ์ด์•ผ๊ธฐ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•˜๋Š” API๋ฅผ ์„ค๊ณ„ํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๊ณ„๋ฅผ ํ–ˆ๋‹ค๊ณ  ํ•˜์ž.

POST /orders/{id}
Custom-Action: CANCEL

 
์ด๊ฑด ์„œ๋ฒ„์—์„œ HTTP ํ—ค๋”์— ์ปค์Šคํ…€ ํ—ค๋”๋ฅผ ์ง€์ •ํ•˜์—ฌ CANCEL์ด๋ผ๋Š” ํ–‰๋™์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์ด๋Š” ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์ด ์•„๋‹Œ ๋ณธ์ธ๋งŒ์˜ ํ”„๋กœํ† ์ฝœ์„ ์ถ”๊ฐ€ํ•œ ๊ผด์ด ๋˜๋ฏ€๋กœ ์ด๋ฅผ ์ดํ•ดํ•œ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋งŒ ํ†ต์‹ ์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ•˜์ง€ ๋ง๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ํ‘œ์ค€์„ ์‚ฌ์šฉํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

DELETE /orders/{id}

 

3. API ๋ฌธ์„œ์— URL ๋ชฉ๋ก์„ ๋‚˜์—ดํ•˜์ง€ ๋งˆ๋ผ. ๋Œ€์‹ , ์‘๋‹ต ํ˜•ํƒœ๋ฅผ ์ž˜ ์ •์˜ํ•ด๋ผ.

 
๋ณดํ†ต ์ด๋ ‡๊ฒŒ API ๋ฌธ์„œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์„œ๋ฒ„-ํด๋ผ์ด์–ธํŠธ ๊ฐ„์— ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉ ํ•ด์•ผํ•˜๋Š”์ง€ ์•Œ๋ ค ์ฃผ์—ˆ์„ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, Fielding์€ API ๋ฌธ์„œ์— ๋ชฉ๋ก์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ ์— ์‘๋‹ต์ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€๋ฅผ ์ •์˜ํ•˜๋Š”๋ฐ ๋…ธ๋ ฅ์„ ์Ÿ์œผ๋ผ๊ณ  ํ•œ๋‹ค.

{
  "orderId": 42,
  "status": "PAID",
  "links": [
    { "rel": "cancel", "href": "/orders/42", "method": "DELETE" },
    { "rel": "refund", "href": "/orders/42/refund", "method": "POST" }
  ]
}

 
์ด๋ ‡๊ฒŒ ์‘๋‹ต ์ž์ฒด์— ๋‹ค์Œ ํ–‰๋™์œผ๋กœ ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ •์˜ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฌธ์„œ ์—†์ด๋„ ์‘๋‹ต๋งŒ์œผ๋กœ ๋‹ค์Œ ํ–‰๋™์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค.
 

4. ๊ณ ์ •๋œ ์ž์› ์ด๋ฆ„, ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งˆ๋ผ.

์šฐ๋ฆฌ๋Š” ๋ณดํ†ต ์„œ๋ฒ„์—์„œ ์–ด๋–ค API๋ฅผ ๋งŒ๋“ค๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ์ด API์˜ URL์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜์—ฌ ์ƒํ˜ธ์ž‘์šฉํ•˜๋„๋ก ๋งŒ๋“ ๋‹ค.

restTemplate.getForObject("/api/v2/orders/42/items", Item.class);

 
ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ ์„œ๋ฒ„๊ฐ€ ์ ‘๊ทผ ๊ฒฝ๋กœ๋ฅผ /api/v3/orders/42/items๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ๋„ ์ด์— ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค. ๊ทธ๋ž˜์„œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ณ ์ •๋œ ์ž์› ์ด๋ฆ„๊ณผ ๊ณ„์ธต์— ๊ฒฐํ•ฉ๋˜์ง€ ์•Š๋„๋ก ์„ค๊ณ„๋ฅผ ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์‘๋‹ต ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

{
  "orderId": 42,
  "links": [
    { "rel": "items", "href": "/api/v2/orders/42/items" }
  ]
}

 
์ด๋ ‡๊ฒŒ ๋˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋Š” "rel"์— ํ•ด๋‹นํ•˜๋Š” items๋งŒ ์ฐธ์กฐํ•˜๋ฉด ๋‹ค์Œ ์ ‘๊ทผ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ณ , ์ด๋Š” ์„œ๋ฒ„๊ฐ€ ์ ‘๊ทผ ๊ฒฝ๋กœ๋ฅผ ๋ณ€๊ฒฝํ•ด๋„ rel์ด ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ์ด์ƒ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.
 

5. ํƒ€์ž…์ด ์ง€์ •๋œ ์ž์›์„ ๊ฐ€์ง€์ง€ ๋งˆ๋ผ.

๋ณดํ†ต ์š”์ฒญ/์‘๋‹ต์˜ ๊ณผ์ •์—์„œ ์šฐ๋ฆฌ๋Š” ์–ด๋–ค ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ• ์ง€ ์ด๋ฏธ ์•Œ๊ณ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‘๋‹ต๊ฐ’์„ ๋ฐ›์„ ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํƒ€์ž…์„ ๋ฏธ๋ฆฌ ์ง€์ •ํ•ด์„œ ์—ญ์ง๋ ฌํ™” ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

Order order = restTemplate.getForObject("/orders/42", Order.class);

 
ํ•˜์ง€๋งŒ, Fielding์€ ์ด๋ ‡๊ฒŒ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋ฉด ์„œ๋ฒ„์˜ ๋„๋ฉ”์ธ ๋ชจ๋ธ์— ์˜์กดํ•˜๊ฒŒ ๋˜๊ณ  ์„œ๋ฒ„์˜ ๋ณ€๊ฒฝ์ด ๊ณง ํด๋ผ์ด์–ธํŠธ์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ด์–ด์ง„๋‹ค๋Š”๊ฑธ ๊ฐ•์กฐํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํƒ€์ž…์˜ ๋ถ„๋ฆฌ๋ฅผ ํ•˜๋ผ๊ณ  ๋งํ•œ๋‹ค.

public class Resource {
    private Map<String, Object> data;
    private List<Link> links;
}
Resource resource = restTemplate.getForObject(someLink, Resource.class);

 
์˜ˆ์‹œ๋กœ Resource๋ผ๋Š” ํƒ€์ž…์„ ์ •์˜ํ•˜๊ณ  ์—ฌ๊ธฐ์—๋Š” ์–ด๋– ํ•œ ์˜์กด ๊ฐ์ฒด๋„ ์—†์ด ๋‹จ์ˆœํžˆ ๋ฐ์ดํ„ฐ์™€ ์–ด๋–ค ๋งํฌ๋งŒ ์กด์žฌํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์„œ๋ฒ„์—์„œ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•ด๋„ Resource์—์„œ ๋ณ€๊ฒฝ๋˜๋Š” ์ง€์ ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์˜ํ–ฅ์„ ๋ผ์น˜์ง€ ์•Š๋Š”๋‹ค.
 
๋‹ค๋งŒ ์ด ๋ฐฉ์‹์—๋„ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ๊ฒฐ๊ตญ data.get("orderId") ์ฒ˜๋Ÿผ ํ‚ค ์ด๋ฆ„์— ๋Œ€ํ•œ ์‚ฌ์ „ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค. Fielding์€ ์ด๋ฅผ Self-descriptive messages(์ž๊ธฐ ์„œ์ˆ ์  ๋ฉ”์‹œ์ง€)๋กœ ํ•ด๊ฒฐํ•˜๋ ค ํ–ˆ๋‹ค.
 
์‘๋‹ต์˜ Content-Type์— application/vnd.example.order+json ๊ฐ™์€ ์ปค์Šคํ…€ ๋ฏธ๋””์–ด ํƒ€์ž…์„ ๋ช…์‹œํ•˜์—ฌ, "์ด ์‘๋‹ต์€ ์ด๋Ÿฐ ๋ช…์„ธ๋ฅผ ๋”ฐ๋ฅธ๋‹ค"๋ฅผ ์‘๋‹ต ์ž์ฒด๊ฐ€ ์„ค๋ช…ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค. application/json์ด RFC 8259๋ผ๋Š” ํ‘œ์ค€ ๋ฌธ์„œ์— "JSON์€ ์ด๋Ÿฐ ํ˜•ํƒœ๋‹ค"๋ผ๊ณ  ์ •์˜๋˜์–ด ์žˆ๋“ฏ์ด, application/vnd.example.order+json๋„ "์ด ๋ฏธ๋””์–ด ํƒ€์ž…์˜ ์‘๋‹ต์€ orderId, status ๊ฐ™์€ ํ•„๋“œ๋ฅผ ํฌํ•จํ•œ๋‹ค"๋Š” ๋ช…์„ธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ณต๊ฐœํ•ด์•ผ ํ•œ๋‹ค.
 

6. ์‚ฌ์ „ ์ง€์‹์—†์ด ์ง„์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ผ.

๋งˆ์ง€๋ง‰ ๊ทœ์น™์€ ์ง€๊ธˆ๊นŒ์ง€ ๋‚˜์˜จ ๋‚ด์šฉ๋“ค์„ ๋ชจ๋‘ ์ข…ํ•ฉํ•œ ๊ฒƒ์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ํ•˜๋Š” ๋ฐฉ์‹์ฒ˜๋Ÿผ API ๋ฌธ์„œ์™€ ๊ฐ™์€๊ฑธ ์ œ๊ณตํ•˜์ง€ ์•Š์•„๋„ ๋‹จ ํ•œ๋ฒˆ์˜ ๋ฃจํŠธ URL ์ ‘๊ทผ์œผ๋กœ ๋ชจ๋“  ์ž์›์˜ ์ ‘๊ทผ ๊ฒฝ๋กœ๋ฅผ ๊ฐ ๊ฒฝ๋กœ๋งˆ๋‹ค์˜ ์‘๋‹ต์œผ๋กœ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.
 
์ฆ‰, ํด๋ผ์ด์–ธํŠธ๊ฐ€ `https://api.example.com`์™€ ๊ฐ™์€ ๊ฒฝ๋กœ๋กœ ์ ‘๊ทผํ•˜๋ฉด ์ดํ›„์˜ ๋ชจ๋“  ๊ฒฝ๋กœ๋Š” ์‘๋‹ต์— ํฌํ•จ๋œ links์—์„œ ์ฐพ์•„ ๋‚˜๋จธ์ง€๋ฅผ ์ ‘๊ทผํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
 

์ •๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ REST API ์•„๋ฒ„์ง€ Fielding์ด ์ฃผ์žฅํ•œ ์ง„์งœ REST API์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์•˜๋‹ค. ์ฃผ์žฅํ•˜๋Š” ๊ทœ์น™๋“ค์„ ๋ณด๋‹ค๋ณด๋ฉด ํ•œ ๊ฐ€์ง€ ๊ณตํ†ต์ ์ธ ํŠน์ง•์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ ์ถ”์ƒํ™”์ด๋‹ค.
 
๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ๋ณ€๊ฒฝ์— ์ทจ์•ฝํ•œ ๋ถ€๋ถ„์„ ์ถ”์ƒํ™”์˜ ๋Œ€์ƒ์œผ๋กœ ๋ณด๋“ฏ์ด, Fielding์€ ์„œ๋ฒ„์—์„œ ๋ณ€๊ฒฝ์— ์ทจ์•ฝํ•œ ๋ถ€๋ถ„๋“ค์ธ ๊ตฌ์ฒด์ ์ธ URL ๊ตฌ์กฐ, ๋„๋ฉ”์ธ ํƒ€์ž…, ํ”„๋กœํ† ์ฝœ์— ์˜์กดํ•˜์ง€ ๋ง๊ณ  ์‘๋‹ต์ด ์ œ๊ณตํ•˜๋Š” ๋งํฌ๋ผ๋Š” ์ถ”์ƒํ™”์— ์˜์กดํ•˜๋ผ๊ณ  ๋งํ•˜๊ณ  ์žˆ๋‹ค.
 

ํ˜„๋Œ€์˜ REST API

ํ˜„์‹ค๊ณผ์˜ ๊ดด๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€์˜ REST API ๊ทœ์น™์„ ๋ณด๋ฉด, ํ˜„์žฌ ๋ณดํŽธ์ ์œผ๋กœ ๋งํ•˜๊ณ  ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” REST API์™€ ๊ฑฐ๋ฆฌ๊ฐ€ ๋ฉ€๋‹ค๋Š” ๊ฒƒ์„ ๋А๊ผˆ์„ ๊ฒƒ์ด๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋ฒ„๋Š” API ๋ฌธ์„œ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋Ÿฌํ•œ ์ž์› ์ ‘๊ทผ ๊ฒฝ๋กœ ๋ฐ ๊ณ„์ธต์„ ํ•˜๋“œ์ฝ”๋”ฉํ•ด์„œ ์š”์ฒญ/์‘๋‹ต์˜ ๊ณผ์ •์„ ๊ฑฐ์น˜๊ณ ์žˆ๋‹ค.
 
๊ทธ๋ ‡๋‹ค๋ฉด ์šฐ๋ฆฌ๋Š” ์ž˜๋ชป๋œ REST API๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ผ๊นŒ? Fielding์˜ ๊ทœ์น™์— ๋”ฐ๋ฅด๋ฉด ๊ทธ๋ ‡๋‹ค. ์šฐ๋ฆฌ๋Š” REST API๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด๋‹ค.
 
ํ•˜์ง€๋งŒ ํ˜„์‹ค์—์„œ ๊ทธ ๊ทœ์น™์„ ๋‹ค ์ง€ํ‚ค์ง€ ์•Š๋Š” ๋ฐ๋Š” ์ด์œ ๊ฐ€ ์žˆ๋‹ค.
 

์™œ ์ง€ํ‚ค์ง€ ์•Š์„๊นŒ?

๊ธฐ์กด์˜ ์„ค๊ณ„ ๋ฐฉ์‹๋„ ์ถฉ๋ถ„ํžˆ REST API์˜ ์กฐ๊ฑด์„ ์ผ๋ถ€ ๋งŒ์กฑํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์ด๋ก ์ ์ธ REST๋ฅผ ๋ชจ๋‘ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฒฐ๋ก ์ ์œผ๋กœ๋Š” HATEOAS๋ฅผ ์ง€์ผœ ๊ฐœ๋ฐœํ•ด์•ผ ํ•œ๋‹ค.
 

HATEOAS

HATEOAS๋Š” Hypermedia As The Engine Of Application State์˜ ์•ฝ์ž๋กœ ์œ„์—์„œ ์„ค๋ช…ํ–ˆ๋˜ 3, 4, 6๋ฒˆ์„ ๋ชจ๋‘ ํฌํ•จํ•˜๋Š” ๊ฐœ๋…์ด๋‹ค. ์ฆ‰, ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ํ˜„์žฌ ์ƒํƒœ์™€ ํ•จ๊ป˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ „์ด๋  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
 
์•„๊นŒ ์˜ˆ์ œ๋กœ ๋“ค์—ˆ๋˜ ์‘๋‹ต์„ ๋‹ค์‹œ๋ณด๋ฉด ๋ฌด์Šจ ๋ง์ธ์ง€ ๊ฐ์ด ์˜ฌ ๊ฒƒ์ด๋‹ค.

{
  "orderId": 42,
  "status": "PAID",
  "links": [
    { "rel": "cancel", "href": "/orders/42", "method": "DELETE" },
    { "rel": "refund", "href": "/orders/42/refund", "method": "POST" }
  ]
}

 
์—ฌ๊ธฐ์„œ ๋ณด๋ฉด ํ˜„์žฌ, orderId๊ฐ€ 42์ธ ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ์ƒํƒœ๊ฐ€ "PAID"์ž„์„ ์ •์˜ํ•˜๊ณ  ์žˆ๊ณ  ๋‹ค์Œ์œผ๋กœ ์ „์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ๋Š” links์— ํฌํ•จ๋œ cancel, refund๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ํด๋ผ์ด์–ธํŠธ๋Š” ๋” ์ด์ƒ ๊ตฌ์ฒด์ ์ธ URL์ด ์•„๋‹Œ rel์— ํฌํ•จ๋œ ์ถ”์ƒํ™” ๋œ ํ˜•ํƒœ๋กœ ๊ฐ ์ž์›์˜ ์ƒํƒœ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
 
ํ•˜์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ API๋Š” HATEOAS๊นŒ์ง€๋Š” ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
 

  1. ๊ฐœ๋ฐœ ๋น„์šฉ์ด ํฌ๋‹ค.
    ๋ชจ๋“  ์‘๋‹ต์— "์ง€๊ธˆ ์ƒํƒœ์—์„œ ๊ฐ€๋Šฅํ•œ ํ–‰์œ„"๋ฅผ ํฌํ•จ์‹œ์ผœ ์‘๋‹ต์„ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด, ์„œ๋ฒ„๋Š” ๋งค ์‘๋‹ต๋งˆ๋‹ค ํ˜„์žฌ ์ƒํƒœ์— ์ ์ ˆํ•œ ๋งํฌ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์‘๋‹ต ์ž์ฒด์˜ ํฌ๊ธฐ๋„ ์ปค์งˆ ๋ฟ๋”๋Ÿฌ ๋งํฌ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š” ํŠน์„ฑ์ƒ ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š” ๊ณผ์ •์—์„œ ๋ถˆํ•„์š”ํ•œ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ๋น„์šฉ์ด ํ•œ ๋ฒˆ ์ด์ƒ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๋”๊ตฐ๋‹ค๋‚˜ 5๋ฒˆ ํ•ญ๋ชฉ์—์„œ ์ปค์Šคํ…€ ๋ฏธ๋””์–ด ํƒ€์ž…์„ API๋งˆ๋‹ค ์ •์˜ํ•˜๊ณ  ๋ช…์„ธ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฑด ์—„์ฒญ๋‚œ ๋น„์šฉ์ด๋‹ค.

  2. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ™œ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
    ์•„๊นŒ Resource์˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ ํƒ€์ž…์ด ํŠน์ •๋˜์ง€ ์•Š๋Š” ์ˆœ๊ฐ„ ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๋–จ์–ด์ง„๋‹ค. JACKSON ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๊ฐ™์€ ๊ฒƒ์„ ํ†ตํ•ด ๋ฐ”๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋‹ˆ ์‹ค์ˆ˜ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ง„๋‹ค.

  3. ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋น„์Šค์—์„œ๋Š” ๊ณผ์ž‰ ์„ค๊ณ„๋‹ค.
    HATEOAS๋Š” ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ˆ„๊ตฌ์ธ์ง€ ๋ชจ๋ฅด๋Š” ๋ฒ”์šฉ์ ์ธ ์ƒํ™ฉ์—์„œ ๊ฐ€์น˜๊ฐ€ ํฌ๋‹ค. ํ•˜์ง€๋งŒ ํ˜„์‹ค์—์„œ๋Š” ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊ฐ™์€ ํŒ€์ด ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. API ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด ํŒ€ ๋‚ด ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ, ๋ชจ๋“  ์‘๋‹ต์— ๋งํฌ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฑด ๋น„์šฉ ๋Œ€๋น„ ํšจ๊ณผ๊ฐ€ ๋‚ฎ๋‹ค.

 

๋งˆ๋ฌด๋ฆฌ

Fielding์˜ ๊ธฐ์ค€์— ๋”ฐ๋ฅด๋ฉด ์šฐ๋ฆฌ๋Š” REST API๊ฐ€ ์•„๋‹ˆ๋ผ HTTP API๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Š” ์ž˜๋ชป๋œ ์„ ํƒ์ด๋ผ๊ธฐ ๋ณด๋‹ค๋Š” ํ”ํžˆ ๋งํ•˜๋“ฏ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„์˜ ๊ณผ์ •์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. ๊ณต๊ฐœ API์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ๋ฅผ ํ†ต์ œํ•  ์ˆ˜ ์—†๋Š” ํ™˜๊ฒฝ์—์„œ๋Š” Fielding์˜ ์›์น™์ด ์—ฌ์ „ํžˆ ์œ ํšจํ•œ ์„ค๊ณ„ ์ง€์นจ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.
 
ํ•˜์ง€๋งŒ, ํ˜„์‹ค์˜ ๋งŽ์€ ์„œ๋น„์Šค์—์„œ๋Š” API ๊ฒฝ๋กœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๋นˆ๋„๊ฐ€ ๋†’์ง€ ์•Š๊ณ , ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊ฐ™์€ ์กฐ์ง์ด ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
 
์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ๋ณ€๊ฒฝ์˜ ์—ฌํŒŒ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด HATEOAS๋ฅผ ๋„์ž…ํ•˜๋Š”๊ฑด ์–ด์ฉŒ๋ฉด ์˜ค๋ฒ„ ์—”์ง€๋‹ˆ์–ด๋ง ์ผ ์ˆ˜๋„ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์‹ค์ œ๋กœ ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋น„์Šค๋Š” Open API๋”๋ผ๋„ HATEOAS๋ฅผ ํฌ๊ธฐํ•˜๊ณ , API ๋ฒ„์ €๋‹์„ ํ†ตํ•ด ๋ณ€๊ฒฝ์˜ ์—ฌํŒŒ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งŽ์ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
 
์‹ ๊ธฐํ•œ ์ ์€ REST API๋„ ๊ฒฐ๊ตญ ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ์ค‘์š”ํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๋Š” โ€˜๋ณ€๊ฒฝ์— ์ทจ์•ฝํ•œ ๋ถ€๋ถ„์„ ์˜ˆ๋ฐฉํ•œ๋‹ค.โ€™๋Š” ์‚ฌ๊ณ ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ๋„ ์ถ”์ƒํ™”๋ฅผ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„์˜ ๊ณผ์ •์œผ๋กœ ๋ณด๋Š”๋งŒํผ REST API๋„ ๊ฐ์ž์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๊ฒŒ ์ž˜ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„ ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.
 

์ฐธ๊ณ