๐Ÿ“Œ ๊น€์˜ํ•œ ๋‹˜์˜ "๋ชจ๋“  ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ HTTP ์›น ๊ธฐ๋ณธ ์ง€์‹" ๊ฐ•์˜ ๋“ฃ๊ณ  ์ •๋ฆฌ

 

๐Ÿ“ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก

ํด๋ผ์ด์–ธํŠธ์—์„œ ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์€ ํฌ๊ฒŒ 2๊ฐ€์ง€ ์ด๋‹ค.

 

1. ์ „๋‹ฌ๋ฐฉ์‹ 

  • ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก
    • GET
    • ์ฃผ๋กœ ์ •๋ ฌ ํ•„ํ„ฐ(๊ฒ€์ƒ‰์–ด)
  • ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก
    • POST, PUT, PATCH
    • ์ฃผ๋กœ ํšŒ์›๊ฐ€์ž…, ์ƒํ’ˆ์ฃผ๋ฌธ, ๋ฆฌ์†Œ์Šค ๋“ฑ๋ก, ๋ฆฌ์†Œ์Šค ๋ณ€๊ฒฝ ๋“ฑ

        

2. ์ƒํ™ฉ

  • ์ •์  ๋ฐ์ดํ„ฐ ์กฐํšŒ : ์ด๋ฏธ์ง€, ์ •์  ํ…์ŠคํŠธ ๋ฌธ์„œ
    • ์กฐํšŒ๋Š” GET ์‚ฌ์šฉ
    • ์ •์  ๋ฐ์ดํ„ฐ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ์—†์ด ๋ฆฌ์†Œ์Šค ๊ฒฝ๋กœ๋งŒ์œผ๋กœ ์กฐํšŒ ๊ฐ€๋Šฅ
  • ๋™์  ๋ฐ์ดํ„ฐ ์กฐํšŒ : ์ฃผ๋กœ ๊ฒ€์ƒ‰, ๊ฒŒ์‹œํŒ ๋ชฉ๋ก์—์„œ ์ •๋ ฌ ํ•„ํ„ฐ(๊ฒ€์ƒ‰์–ด)
    • ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉ, ์ด๋ฅผ ์„œ๋ฒ„์—์„œ key-value๋กœ ๊บผ๋‚ด์–ด ์‚ฌ์šฉ
    • ์กฐํšŒ ์กฐ๊ฑด์„ ์ค„์—ฌ์ฃผ๋Š” ํ•„ํ„ฐ, ์กฐํšŒ ๊ฒฐ๊ณผ๋ฅผ ์ •๋ ฌํ•˜๋Š” ์ •๋ ฌ ์กฐ๊ฑด์— ์ฃผ๋กœ ์‚ฌ์šฉ
    • ์กฐํšŒ๋Š” GET
    • GET์€ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ
  • HTML Form์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก : ํšŒ์›๊ฐ€์ž…, ์ƒํ’ˆ์ฃผ๋ฌธ, ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ
    • HTML์˜ Form ํƒœ๊ทธ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ formํƒœ๊ทธ์˜ submit์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ form์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด HTTP ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. (Type = application/x-www-form-urlencoded ์‚ฌ์šฉ)
    • (์™œ url encoded ์ธ๊ฐ€? url๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธ์ฝ”๋”ฉํ•ด์„œ ๋„˜๊ฒจ์ฃผ๊ธฐ ๋•Œ๋ฌธ)

  
<form action="/save" method="post">
<input type="text" name="username"/>
<input type="text" name="age"/>
<button type="submit">์ „์†ก</button>
</form>
POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

username=kim&age=20
  • form์—์„œ method๋ฅผ get์œผ๋กœ ์ง€์ •ํ•˜๊ฒŒ ๋˜๋ฉด GET์€ ๋ณดํ†ต ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ์— ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ”๊ฟ”์„œ HTTP ๋ฉ”์‹œ์ง€๋ฅผ ๋งŒ๋“ ๋‹ค (GET์˜ ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ณณ๋“ค์ด ์กด์žฌํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  GET์€ ์กฐํšŒ์šฉ)
GET /members?username=kim&age=20 HTTP/1.1
Host: localhost:8080
  • multipart/form-data : ํŒŒ์ผ ์—…๋กœ๋“œ์™€ ๊ฐ™์€ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ ์ „์†กํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Content-Type

  
<form action="/save" method="post" enctype="multipart/form-data">
<input type="text" name="username"/>
<input type="text" name="age"/>
<input type="file" name="file1" />
<button type="submit">์ „์†ก</button>
</form>

        ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ƒ์„ฑํ•œ HTTP ๋ฉ”์‹œ์ง€ : boundary ๊ธฐ์ค€์œผ๋กœ ์ž๋ฆ„ (๋žœ๋ค์ง€์ •)

        multipart๋ผ๋Š” ์ด๋ฆ„์€ ์ด๋Ÿฐ์‹์œผ๋กœ ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์—ฌ๋Ÿฌ ํŒŒ์ผ๊ณผ form ๋‚ด์šฉ์„ ํ•จ๊ป˜ ์ „์†ก์ด ๊ฐ€๋Šฅํ•ด multipart์ด๋‹ค.

POST /save HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=---XXX

------XXX
Content-Disposition: form-data; name="username"

kim
------XXX
Content-Disposition: form-data; name="age"

20
------XXX
Content-Disposition: form-data; name="file1"; filename="intro.png"
Content-Type: image/png

109238a9o0p3eqwokjasd09ou3oirjwoe9u34ouidf...   <= ์ด๋ฏธ์ง€์— ๋Œ€ํ•œ ๋ฐ”์ดํŠธ ์ •๋ณด
------XXX--

+ ์ฐธ๊ณ : HTML Form์€ GET๊ณผ POST๋งŒ ์ง€์›

 

  • HTTP API๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ์ „์†ก : ์„œ๋ฒ„ to ์„œ๋ฒ„, ์•ฑ ํด๋ผ์ด์–ธํŠธ, ์›น ํด๋ผ์ด์–ธํŠธ(AJAX๋ฐฉ์‹)
    • HTML Form์„ ์“ฐ์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ ๋Œ€๋ถ€๋ถ„
    • POST, PUT, PATCH : ๋ฉ”์‹œ์ง€ ๋ฐ”๋””๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ์ „์†ก
    • GET : ์กฐํšŒ, ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
    • Content-Type: application/json์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ์‚ฌ์‹ค์ƒ ํ‘œ์ค€์ฒ˜๋Ÿผ ์‚ฌ์šฉ

 

๐Ÿ“HTTP API ์„ค๊ณ„ ์˜ˆ์‹œ

 

HTTP API๋ฅผ ํฌ๊ฒŒ ๋‘๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜ํ•˜์—ฌ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ํ•˜๋‚˜๋Š” POST๊ธฐ๋ฐ˜์˜ ๋“ฑ๋ก ๋ฐฉ์‹ "์ปฌ๋ ‰์…˜",  ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” PUT๊ธฐ๋ฐ˜์˜ ๋“ฑ๋ก๋ฐฉ์‹ "์Šคํ† ์–ด"๋‹ค. POST์™€ PUT์„ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด ์ •๋ณด๋ฅผ ์ €์žฅํ•  ๋•Œ ์„œ๋กœ ๋‹ค๋ฅธ ํŠน์ง•์ด ์žˆ๋Š”๋ฐ ์ด๋ฅผ ์ž˜ ์ •๋ฆฌํ•ด์•ผํ•œ๋‹ค.

 

* HTTP API - ์ปฌ๋ ‰์…˜

    - POST ๊ธฐ๋ฐ˜ ๋“ฑ๋ก์œผ๋กœ ์„ค๋ช…

ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ /members GET
ํšŒ์› ๋“ฑ๋ก /members POST
ํšŒ์› ๋‹จ๊ฑด ์กฐํšŒ /members/{id} GET
ํšŒ์› ์ˆ˜์ • /members/{id} PATCH, PUT, POST
ํšŒ์› ์‚ญ์ œ  /members/{id} DELETE

    - ํด๋ผ์ด์–ธํŠธ๋Š” ๋“ฑ๋ก๋  ๋ฆฌ์†Œ์Šค URI๋ฅผ ๋ชจ๋ฅธ๋‹ค > ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ ๋ฐ›์•„ ์ƒˆ๋กœ ๋“ฑ๋ก๋œ ๋ฆฌ์†Œ์Šค URI๋ฅผ ์ƒ์„ฑ (ex. /members/100)

    - ์ด๋Ÿฌํ•œ ํ˜•์‹์„ ์ปฌ๋ ‰์…˜(Collection) ์ด๋ผ๊ณ  ํ•œ๋‹ค. 

      ์„œ๋ฒ„๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งํ•˜๋ฉฐ ์„œ๋ฒ„๊ฐ€ ๋ฆฌ์†Œ์Šค URI๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” /members๊ฐ€ ์ปฌ๋ ‰์…˜!

    - ์ผ๋ฐ˜์ ์œผ๋กœ ์ด ๋ฐฉ์‹์„ ๋งŽ์ด ์‚ฌ์šฉ

 

    

* HTTP API - ์Šคํ† ์–ด

    - PUT ๊ธฐ๋ฐ˜ ๋“ฑ๋ก์œผ๋กœ ์„ค๋ช…

ํŒŒ์ผ ๋ชฉ๋ก ์กฐํšŒ /files GET
ํŒŒ์ผ ๋“ฑ๋ก /files/{filename} PUT (์—…๋กœ๋“œํ•  ํŒŒ์ผ ์ด๋ฆ„์„ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ, ๋™์ผํ•œ ์ด๋ฆ„ ์žˆ์œผ๋ฉด ๋Œ€์ฒด ์—†์œผ๋ฉด ์ƒ์„ฑ)
ํŒŒ์ผ ๋‹จ๊ฑด ์กฐํšŒ /files/{filename} GET
ํŒŒ์ผ ์‚ญ์ œ  /files/{filename} DELETE
ํŒŒ์ผ ๋Œ€๋Ÿ‰ ๋“ฑ๋ก /files POST (๋“ฑ๋ก์— PUT์„ ์‚ฌ์šฉํ–ˆ์œผ๋ฏ€๋กœ POST๋Š” ํ•„์š”์— ๋”ฐ๋ผ ์ž„์˜ ์ง€์ •)

    - ํด๋ผ์ด์–ธํŠธ๋Š” ๋“ฑ๋ก๋  ๋ฆฌ์†Œ์Šค URI๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. > ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง์ ‘ ๋ฆฌ์†Œ์Šค URI๋ฅผ ์ง€์ •ํ•ด ๊ด€๋ฆฌ

    - ์ด๋Ÿฌํ•œ ํ˜•์‹์„ ์Šคํ† ์–ด(Store) ๋ผ๊ณ  ํ•œ๋‹ค. 

      ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ฆฌ์†Œ์Šค ์ €์žฅ์†Œ๋ฅผ ๋งํ•˜๋ฉฐ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฆฌ์†Œ์Šค URI๋ฅผ ์•Œ๊ณ  ๊ด€๋ฆฌ. ์—ฌ๊ธฐ์„œ๋Š” /files๊ฐ€ ์Šคํ† ์–ด!

 

 

* HTML FORM ์‚ฌ์šฉ

    - HTML FORM์€ GET, POST๋งŒ ์ง€์› (AJAX ๊ฐ™์€ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•ด ์ด๋Ÿฐ ์ œ์•ฝ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ)

    - ์—ฌ๊ธฐ์„œ๋Š” ์ˆœ์ˆ˜ HTML + HTML FORM ์ด์•ผ๊ธฐํ•  ๊ฒƒ์œผ๋กœ GET๊ณผ POST๋งŒ ์ง€์›ํ•˜๋ฏ€๋กœ ์ œ์•ฝ์ด ์žˆ๋‹ค. 

ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ /members GET
ํšŒ์› ๋“ฑ๋ก ํผ /members/new GET (๋“ฑ๋ก ํผ์„ ์กฐํšŒ, submit ํ•˜๋ฉด ์•„๋ž˜ ํšŒ์› ๋“ฑ๋ก POST๋กœ)
ํšŒ์› ๋“ฑ๋ก /members
/members/new
POST (๋‘๊ฐ€์ง€ ์„ ํƒ ๊ฐ€๋Šฅ, ์ปฌ๋ ‰์…˜์ฒ˜๋Ÿผ ๋ฆฌ์†Œ์Šค ๊ธฐ์ค€ or ๋“ฑ๋ก ํผ์„ ๋”ฐ๋ฅด๋Š”)
ํšŒ์› ๋‹จ๊ฑด ์กฐํšŒ /members/{id} GET
ํšŒ์› ์ˆ˜์ • ํผ /members/{id}/edit GET (์ˆ˜์ • ํผ ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒ, submit ํ•˜๋ฉด ์•„๋ž˜ ํšŒ์› ์ˆ˜์ • POST๋กœ)
ํšŒ์› ์ˆ˜์ • /members/{id}
/members/{id}/edit
POST
ํšŒ์› ์‚ญ์ œ  /members/{id}/delete POST (/members/{id} ๋กœ POST๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉด ์ปจํŠธ๋กค URI๋ฅผ ์‚ฌ์šฉ)

    - ์ปจํŠธ๋กค URI

      GET, POST๋งŒ ์ง€์›ํ•˜๋ฏ€๋กœ ์ œ์•ฝ์ด ์žˆ์–ด ์ด๊ฑธ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, "๋™์‚ฌ"๋กœ ๋œ ๋ฆฌ์†Œ์Šค ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉ

      POST์˜ /new, /edit, /delete ๊ฐ€ ์ปจํŠธ๋กค URI์— ํ•ด๋‹นํ•œ๋‹ค. 

      ์ œ์•ฝ ํ•ด๊ฒฐ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ HTTP ๋ฉ”์„œ๋“œ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์• ๋งคํ•œ ๊ฒฝ์šฐ์—๋„ ์‚ฌ์šฉํ•œ๋‹ค(HTTP API ํฌํ•จ).
      ์ตœ๋Œ€ํ•œ ๋ฆฌ์†Œ์Šค๊ฐœ๋…์„ ์‚ฌ์šฉํ•˜๋˜ ๊ทธ๊ฒŒ ํž˜๋“ค ๊ฒฝ์šฐ ๋Œ€์ฒด์ œ๋กœ ์‚ฌ์šฉ!

 

๐Ÿ“์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์€ URI ์„ค๊ณ„ ๊ฐœ๋…

* ๋ฌธ์„œ (document)

  • ๋‹จ์ผ ๊ฐœ๋… (ํŒŒ์ผ ํ•˜๋‚˜, ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค row)
  • ex) /members/100, /files.star.jpg

* ์ปฌ๋ ‰์…˜ (collection)

  • ์„œ๋ฒ„๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ
  • ์„œ๋ฒ„๊ฐ€ ๋ฆฌ์†Œ์Šค์˜ URI๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌ
  • ex) /members

* ์Šคํ† ์–ด (store)

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ฆฌ์†Œ์Šค ์ €์žฅ์†Œ
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฆฌ์†Œ์Šค์˜ URI๋ฅผ ์•Œ๊ณ  ๊ด€๋ฆฌ
  • ex) /files

* ์ปจํŠธ๋กค๋Ÿฌ (controller), ์ปจํŠธ๋กค URI

  • ๋ฌธ์„œ, ์ปฌ๋ ‰์…˜, ์Šคํ† ์–ด๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ์ถ”๊ฐ€ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰
  • ๋™์‚ฌ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ
  • ex) /members/{id}/delete
 

REST API - URL Naming Conventions

In REST, having a strong and consistent REST resource naming strategy โ€“ will prove one of the best design decisions in the long term.

restfulapi.net

 

 

+ Recent posts