Hypermedia APIs vs. Data APIs

Carson Gross

#ํ•˜์ดํผ๋ฏธ๋””์–ด API

ํ•˜์ดํผ๋ฏธ๋””์–ด API๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” API๋กœ, ์ผ๋ฐ˜์ ์œผ๋กœ HTML์„ HTTP๋ฅผ ํ†ตํ•ด ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์Šคํƒ€์ผ์˜ API๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ API์™€ ๊ตฌ๋ณ„๋ฉ๋‹ˆ๋‹ค. ์˜ค๋Š˜๋‚  ์ด ํ›„์ž์˜ ์Šคํƒ€์ผ์€ ์ผ๋ฐ˜์ ์œผ๋กœ JSON API๋กœ ์•Œ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์˜ API๋Š” ๊ฐ๊ฐ ๊ณ ์œ ํ•œ ์„ค๊ณ„ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ์„œ๋กœ ๋‹ค๋ฅธ ์„ค๊ณ„ ์ œ์•ฝ ์กฐ๊ฑด๊ณผ ๋ชฉํ‘œ๋ฅผ ์ฑ„ํƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

#ํ•˜์ดํผ๋ฏธ๋””์–ด API:

#๋ฐ์ดํ„ฐ API:

#์˜ค๋Š˜๋‚ ์˜ API

์˜ค๋Š˜๋‚  API๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ HTTP๋ฅผ ํ†ตํ•œ JSON์œผ๋กœ ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ API๋Š” ๊ฑฐ์˜ ํ•ญ์ƒ ํ•˜์ดํผ๋ฏธ๋””์–ด API๋ณด๋‹ค๋Š” ๋ฐ์ดํ„ฐ ์ง€ํ–ฅ API์ด์ง€๋งŒ, ๋•Œ๋กœ๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด ๊ฐœ๋…์ด ํ†ตํ•ฉ๋˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค(๋Œ€๋ถ€๋ถ„ ์ตœ์ข… ์‚ฌ์šฉ์ž์—๊ฒŒ ํฐ ์ด์ ์„ ์ฃผ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค). ์—…๊ณ„๋Š” REST-ful API์—์„œ ๋ฒ—์–ด๋‚˜ ๋ฐ์ดํ„ฐ API๋ฅผ REST-ful ๋ชจ๋ธ์— ๋งž์ถ”๋Š” ๋ฐ์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ธ์‹ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ข‹์€ ์ผ์ž…๋‹ˆ๋‹ค. ์—…๊ณ„๋Š” ๋ฐ์ดํ„ฐ API ๋ถ„์•ผ์—์„œ REST-ful ์•„์ด๋””์–ด์— ๋Œ€ํ•ด ์˜๋ฌธ์„ ์ œ๊ธฐํ•˜๊ณ , REST๊ฐ€ ์„ค๋ช…ํ•˜๋ ค ํ–ˆ๋˜ ๋„คํŠธ์›Œํฌ ์•„ํ‚คํ…์ฒ˜ ๋Œ€์‹ , ํ•ด๋‹น ํŠน์ • ๋„คํŠธ์›Œํฌ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋” ์ž˜ ์„œ๋น„์Šคํ•œ ์ด์ „์˜ ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ๊ธฐ์ˆ ์„ ์‚ดํŽด๋ณด๊ธฐ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

#ํ•˜์ดํผ๋ฏธ๋””์–ด API ์„ค๊ณ„

ํ•˜์ดํผ๋ฏธ๋””์–ด API๊ฐ€ ๋ฐ์ดํ„ฐ API์™€ ๋‹ค๋ฅด๊ฒŒ ์„ค๊ณ„๋  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด, ์ตœ๊ทผ htmx ๋””์Šค์ฝ”๋“œ์—์„œ ์ œ๊ธฐ๋œ ๋‹ค์Œ ์ƒํ™ฉ์„ ๊ณ ๋ คํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

์ €๋Š” ํผ๊ณผ ํ…Œ์ด๋ธ”์ด ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ํผ์€ ํ…Œ์ด๋ธ”์— ์ƒˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ด๋ฉฐ, ํ…Œ์ด๋ธ”์€ 30์ดˆ๋งˆ๋‹ค ํด๋งํ•˜์—ฌ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ํ‘œ์‹œํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด UI๋ฅผ ๊ธฐ๋ณธ URL /contacts๋กœ ๊ณ ๋ คํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ๋กœ ํ•„์š”ํ•œ ๊ฒƒ์€ ํผ๊ณผ ํ˜„์žฌ ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ”์„ ๊ฐ€์ ธ์˜ค๋Š” ์—”๋“œํฌ์ธํŠธ์ž…๋‹ˆ๋‹ค. ์ด ์—”๋“œํฌ์ธํŠธ๋Š” /contacts์— ์œ„์น˜ํ•˜๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค:

  GET /contacts -> ํผ ๋ฐ ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” ๋ Œ๋”๋ง

๋‹ค์Œ์œผ๋กœ, ์—ฐ๋ฝ์ฒ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋™์ผํ•œ URL๋กœ POST ์š”์ฒญ์„ ํ†ตํ•ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค:

  GET /contacts -> ํผ ๋ฐ ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” ๋ Œ๋”๋ง
  POST /contacts -> ์ƒˆ ์—ฐ๋ฝ์ฒ˜ ์ƒ์„ฑ, GET /contacts๋กœ ๋ฆฌ๋””๋ ‰์…˜

๊ทธ๋ฆฌ๊ณ  HTML์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ๊ฒผ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

<div>
    <form action='/contacts' method="post">
      <!-- ์—ฐ๋ฝ์ฒ˜ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ ํผ -->
    </form>
    <table>
      <!-- ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” -->
    </table>
</div>

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋งค์šฐ ์ „ํ˜•์ ์ธ ์›น 1.0 ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. ์ด ์‹œ์ ๊นŒ์ง€๋Š” ๋ฐ์ดํ„ฐ API์™€ ํ•˜์ดํผ๋ฏธ๋””์–ด API์˜ ์š”๊ตฌ ์‚ฌํ•ญ์ด ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์ง€๋งŒ, ํ•˜์ดํผ๋ฏธ๋””์–ด API๊ฐ€ ์ž๊ธฐ ์„ค๋ช…์ ์ด๋ฉฐ URL์„ ๋ณ€๊ฒฝํ•ด๋„ ํ•˜์ดํผ๋ฏธ๋””์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์†์ƒ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์€ ์ฃผ๋ชฉํ•  ๋งŒํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ htmx๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ๋„๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ด๋ธ” ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•ด ์ฃผ๊ธฐ์ ์œผ๋กœ ์„œ๋ฒ„๋ฅผ ํด๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด /contacts/table์ด๋ผ๋Š” ์ƒˆ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ”๋งŒ ๋ Œ๋”๋งํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค:

  GET /contacts -> ํผ ๋ฐ ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” ๋ Œ๋”๋ง
  POST /contacts -> ์ƒˆ ์—ฐ๋ฝ์ฒ˜ ์ƒ์„ฑ, GET /contacts๋กœ ๋ฆฌ๋””๋ ‰์…˜
  GET /contacts/table -> ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” ๋ Œ๋”๋ง

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ด๋ธ”์— ํด ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค:

<div>
    <form action='/contacts' method="post">
      <!-- ์—ฐ๋ฝ์ฒ˜ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ ํผ -->
    </form>
    <table hx-trigger="every 30s" hx-get="/contacts/table" hx-swap="outerHTML">
      <!-- ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” -->
    </table>
</div>

์—ฌ๊ธฐ์„œ ํ•˜์ดํผ๋ฏธ๋””์–ด API์™€ ๋ฐ์ดํ„ฐ API์˜ ์ฐจ์ด๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒˆ ์—”๋“œํฌ์ธํŠธ๋Š” ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์˜ ํ•„์š”๊ฐ€ ์•„๋‹ˆ๋ผ ํ•˜์ดํผ๋ฏธ๋””์–ด์˜ ํ•„์š”์— ์˜ํ•ด ์™„์ „ํžˆ ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค. ์ด ์—”๋“œํฌ์ธํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•˜์ดํผ๋ฏธ๋””์–ด ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ทธ ํ˜•ํƒœ๊ฐ€ ํฌ๊ฒŒ ๋ฐ”๋€” ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ์Šคํ…œ์ด ์ž๊ธฐ ์„ค๋ช…์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ „์ ์œผ๋กœ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.

HTML์„ htmx๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋งํ•˜๋„๋ก ์—…๋ฐ์ดํŠธํ–ˆ์œผ๋ฏ€๋กœ, ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ์œ„ํ•ด ํผ์—์„œ๋„ htmx๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค:

<div>
    <form action='/contacts' method="post" hx-boost="true">
      <!-- ์—ฐ๋ฝ์ฒ˜ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•œ ํผ -->
    </form>
    <table hx-trigger="every 30s" hx-get="/contacts/table" hx-swap="outerHTML">
      <!-- ์—ฐ๋ฝ์ฒ˜ ํ…Œ์ด๋ธ” -->
    </table>
</div>

ํ•„์š”์— ๋”ฐ๋ผ ์ž…๋ ฅ์˜ ์„œ๋ฒ„ ์ธก ๊ฒ€์ฆ, ๋™์  ํผ ๋“ฑ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์—”๋“œํฌ์ธํŠธ๋Š” ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๊ณ ๋ ค ์‚ฌํ•ญ์ด ์•„๋‹Œ ํ•˜์ดํผ๋ฏธ๋””์–ด์˜ ํ•„์š”์— ๋”ฐ๋ผ ๊ตฌ๋™๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ฌ์„ฑํ•˜๋ ค๋Š” ๋ชฉํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

#API ๋ณ€ํ™”

์ด ์งง์€ ๊ธ€์˜ ํ•ต์‹ฌ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: ํ•˜์ดํผ๋ฏธ๋””์–ด ์‹œ์Šคํ…œ์—์„œ๋Š” ํ•˜์ดํผ๋ฏธ๋””์–ด ์‹œ์Šคํ…œ์˜ ๋ฉ”์‹œ์ง€๊ฐ€ ์ž๊ธฐ ์„ค๋ช…์ ์ด๊ธฐ ๋•Œ๋ฌธ์— API ๋ณ€๊ฒฝ์ด ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. API๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณ€๊ฒฝํ•ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊นจ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ธ๊ฐ„ ์‚ฌ์šฉ์ž๋Š” ์ƒˆ๋กœ์šด ํ•˜์ดํผ๋ฏธ๋””์–ด(HTML)๋ฅผ ๋ณด๊ณ  ์›ํ•˜๋Š” ์ž‘์—…์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปดํ“จํ„ฐ์™€ ๋น„๊ตํ•  ๋•Œ ์ธ๊ฐ„์€ ๋ฌด์—‡์„ ํ•ด์•ผ ํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐ ๋›ฐ์–ด๋‚˜๋ฉฐ, ๋ณ€ํ™”์— ๋น„๊ต์  ์ž˜ ์ ์‘ํ•ฉ๋‹ˆ๋‹ค.

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

#๊ฒฐ๋ก 

ํ•˜์ดํผ๋ฏธ๋””์–ด API๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ API๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ์™€ ๋‹ค๋ฅธ ์‚ฌ๊ณ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ์ด ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ข‹์€ ํ•˜์ดํผ๋ฏธ๋””์–ด ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ฃผ์š” ๋ชฉํ‘œ๊ฐ€ ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

</>