44์ฅ REST API
REST API๋?
REST๋ ๋ฌด์์ธ๊ฐ?
REpresentational State Transfer์ ์ค์๋ง.
HTTP/1.0 ๊ณผ 1.1 ์คํ ์์ฑ ๋ฐ ์ํ์น HTTP ์๋ฒ ํ๋ก์ ํธ์ ๊ณต๋ ์ค๋ฆฝ์ ๋ก์ด ํ๋ฉ์ ๋ ผ๋ฌธ์์ ์ฒ์ ์๊ฐ๋์๋ค.
HTTP ํ๋กํ ์ฝ์ ์ฅ์ ์ ์ต๋ํ ํ์ฉํ ์ ์๋ ์ํคํ ์ณ๋ก ์๊ฐ๋์ด HTTP ํ๋กํ ์ฝ์ ์๋์ ๋ง๊ฒ ๋์์ธํ๋๋ก ์ ๋ํ๊ณ ์๋ค.
์๊ฒฉํ ์๋ฏธ vs ๊ฐ๋จํ ์๋ฏธ
์๊ฒฉํ ์๋ฏธ ๋คํธ์ํฌ ์ํคํ ์ณ ์๋ฆฌ์ ๋ชจ์, ์ฆ ์์์ ์ ์ํ๊ณ ์์์ ๋ํ ์ฃผ์๋ฅผ ์ง์ ํ๋ ๋ฐฉ๋ฒ ์ ๋ฐ์ ์๋ฏธํ๋ค.
๊ฐ๋จํ ์๋ฏธ ์น ์์ ์๋ฃ๋ฅผ HTTP ์์์ ๋ณ๋์ ์ ์ก๊ณ์ธต ์์ด ์ ์กํ๊ธฐ ์ํ ์์ฃผ ๊ฐ๋จํ ์ธํฐํ์ด์ค
SOAP(Simple Object Access Protocol : HTTP, HTTPS ๋ฑ์ ํตํด XML ๊ธฐ๋ฐ ๋ฉ์ธ์ง๋ฅผ ๊ตํํ๋ ํ๋กํ ์ฝ) ์ด๋ ์ฟ ํค๋ฅผ ํตํ ์ธ์ ํธ๋ํน ๊ฐ์ ์ ์ก๊ณ์ธต ๊ฒ๋ค ์์ด ํต์
RESTFul๊ณผ REST API๋ ๋ฌด์์ธ๊ฐ?
REST์ ๊ธฐ๋ณธ ์์น์ ์ฑ์คํ ์งํจ ์๋น์ค ๋ค์ง์ธ์ RESTfulํ๋ค๊ณ ํํํ๋ค.
REST๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋น์ค API๋ฅผ ๊ตฌํํ ๊ฒ์ ์๋ฏธํ๋ค.
REST๋ HTTP๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๊ท์ ํ ์ํคํ ์ณ๊ณ , REST API๋ REST๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋น์ค API๋ฅผ ๊ตฌํํ ๊ฒ์ ์๋ฏธํ๋ค.
44.1 REST API์ ๊ตฌ์ฑ
REST API๋ ์์(resource), ํ์(verb), ํํ(representation)์ ์ธ๊ฐ์ง ์์๋ก ๊ตฌ์ฑ๋๋ค. REST๋ ์์ฒด ํํ ๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋์ด REST API ๋ง์ผ๋ก HTTP ์์ฒญ์ ๋ด์ฉ์ ์ดํดํ ์ ์๋ค.
์์
์์
URI(์๋ํฌ์ธํธ)
ํ์
์์์ ๋ํ ํ์
HTTP ์์ฒญ ๋ฉ์๋
ํํ
์์์ ๋ํ ํ์์ ๊ตฌ์ฒด์ ๋ด์ฉ
ํ์ด๋ก๋
ํ์ด๋ก๋ : ํฐ ๋ฐ์ดํฐ ์ค์ 'ํฅ๋ฏธ์๋' ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ณํ๋ ๋ฐ ์ฌ์ฉ.
ํ๋ก๊ทธ๋๋ฐ์์ ์ฃผ๋ก ๋ฉ์ธ์ง ํ๋กํ ์ฝ ์ค์ ํ๋กํ ์ฝ ์ค๋ฒํค๋์ ์ํ๋ ๋ฐ์ดํฐ(์ฆ ํ์ด๋ก๋)๋ฅผ ๊ตฌ๋ถํ ๋ ์ฌ์ฉํ๋ค
์์ - JSON ํ์์ ์น ์๋น์ค ์๋ต
{ "status": "OK", "data": { "message": "Hello, world!" } }
์ฌ๊ธฐ์์ "Hello, world!"๊ฐ ํด๋ผ์ด์ธํธ๊ฐ ๊ด์ฌ์ ๊ฐ์ง๋ ํ์ด๋ก๋์ด๊ณ , ๋๋จธ์ง ๋ถ๋ถ์ ์ค์ํ๊ธด ํ์ง๋ง ํ๋กํ ์ฝ ์ค๋ฒํค๋์ด๋ค. ์ถ์ฒ : [wiki]https://ko.wikipedia.org/wiki/%ED%8E%98%EC%9D%B4%EB%A1%9C%EB%93%9C_(%EC%BB%B4%ED%93%A8%ED%8C%85)
44.2 REST API ์ค๊ณ ์์น
REST ์์ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ณธ์ง์ธ ์์น์ ๋๊ฐ์ง์ด๋ค!!
1. URI๋ ๋ฆฌ์์ค๋ฅผ ํํํ๋๋ฐ ์ง์คํ๋ค.
2. HTTP ์์ฒญ ๋ฉ์๋๋ฅผ ํตํด ํ์๋ฅผ ์ ์ํ๋ค.
URI๋ ๋ฆฌ์์ค๋ฅผ ํํํด์ผ ํ๋ค.
#bad
GET /getTodos/1 - (1)๋ฒ ์์
GET /todos/show/1 - (2)๋ฒ ์์
# good
GET /todos/1
URI๋ ๋ฆฌ์์ค๋ฅผ ํํํ๋๋ฐ์ ์ค์ ์ ๋์ด์ผ ํ๋ค.
(1)๋ฒ ์์ -> ์์์ฒ๋ผ get ๊ฐ์ ํ์์ ๋ํ ํํ์ด ๋ค์ด๊ฐ์๋ ์๋๋ค
(2)๋ฒ ์์ -> ๋ฆฌ์์ค๋ฅผ ์๋ณํ ์ ์๋ ์ด๋ฆ์ ๋์ฌ๋ณด๋ค๋ ๋ช ์ฌ๋ฅผ ์ฌ์ฉํ๋ค.
๋ฆฌ์์ค์ ๋ํ ํ์๋ HTTP ์์ฒญ ๋ฉ์๋๋ก ํํํ๋ค.
HTTP ์์ฒญ ๋ฉ์๋๋ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์๊ฒ ์์ฒญ์ ์ข ๋ฅ์ ๋ชฉ์ (๋ฆฌ์์ค์ ๋ํ ํ์)์ ์๋ฆฌ๋ ๋ฐฉ๋ฒ์ด๋ค.
GET
index / retrieve
๋ชจ๋ ํน์ ๋ฆฌ์์ค ์ทจ๋
X
POST
create
๋ฆฌ์์ค ์์ฑ
O
PUT
replace
๋ฆฌ์์ค์ ์ ์ฒด ๊ต์ฒด
O
PATCH
modify
๋ฆฌ์์ค์ ์ผ๋ถ ์์
O
DELETE
delete
๋ชจ๋ / ํน์ ๋ฆฌ์์ค ์ญ์
X
# bad
GET /todos/delete/1 - (1)๋ฒ ์์
# good
DELETE /todos/1
๋ฆฌ์์ค์ ๋ํ ํ์๋ HTTP ์์ฒญ ๋ฉ์๋๋ฅผ ํตํด ํํํ๋ฉฐ, URI์ ํํํ์ง ์๋๋ค.
(1)๋ฒ ์์ - GET์ ๋ฆฌ์์ค๋ฅผ ์ทจ๋ํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋์ด๋ค. ์ญ์ ์์ฒญ์ ํ๋ ค๋ฉด DELETE ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
44.3 REST API ์ค์ต
44.3.1 ~ 44.3.3 JSON Serverํ๊ฒฝ ์ธํ
(์๋ต)
๋ค์ JSON ํ์ผ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก todo์ ๋ํ ๋ฐ์ดํฐ๋ฅผ CRUD ํ ์ ์๋ ์๋ฒ๋ผ๊ณ ์๊ฐํด๋ณด์.
{
"todos": [
{
"id": 1,
"content": "JS",
"completed": true
},
{
"id": 2,
"content": "React",
"completed": false
},
{
"id": 1,
"content": "FrontEnd",
"completed": false
}
]
}
44.3.4 GET ์์ฒญ
๋ชจ๋ todos ๋ฆฌ์์ค๋ฅผ ์ทจ๋ ํ ๋
const xhr = new XMLHttpRequest(); /**G * 1. ํ์๋ ๋ฉ์๋๋ก GET ์์ฒญ์ผ๋ก ์ทจ๋ํ๊ฒ ๋ค * 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, todos ๋ฆฌ์์ค๋ฅผ ์ทจ๋ํ๊ฒ ๋ค(index) */ xhr.open("GET", "/todos"); xhr.send(); xhr.onload = () => { if (xhr.status === 200) { console.log(xhr.response); } else { console.error("Error", xhr.status, xhr.statusText); } };
response
{ "todos": [ { "id": 1, "content": "JS", "completed": true }, { "id": 2, "content": "React", "completed": false }, { "id": 3, "content": "FrontEnd", "completed": false } ] }
ํน์ todos ๋ฆฌ์์ค๋ฅผ ์ทจ๋
const xhr = new XMLHttpRequest(); /** * 1. ํ์๋ ๋ฉ์๋๋ก GET ์์ฒญ์ผ๋ก ์ทจ๋ํ๊ฒ ๋ค * 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, todos/1 ๋ก id:1์ธ todo ๋ฆฌ์์ค๋ฅผ ์ทจ๋ํ๊ฒ ๋ค * * todos ๋ฆฌ์์ค์์ ํน์ todo๋ฅผ ์ทจ๋(retrieve) */ xhr.open("GET", "/todos/1"); xhr.send(); xhr.onload = () => { if (xhr.status === 200) { console.log(xhr.response); } else { console.error("Error", xhr.status, xhr.statusText); } };
response
{ "todos": [ { "id": 1, "content": "JS", "completed": true } ] }
44.3.5 POST ์์ฒญ
const xhr = new XMLHttpRequest();
/**
* 1. ํ์๋ ๋ฉ์๋๋ก POST ์์ฒญ์ผ๋ก ์์ฑํ๊ฒ ๋ค
* 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, ์๋ก์ด todo๋ฅผ ์์ฑํ๊ฒ ๋ค
*/
xhr.open("POST", "/todos");
// ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ๋ณด๋ผ ํ์ด๋ก๋์ MIME ํ์
์ง์
xhr.setRequestHeader("Content-Type", "application/json");
// ํ์ด๋ก๋๋ฅผ ๋ด์์ ์๋ฒ๋ก ์ ์ก
xhr.send(JSON.stringify({ id: "4", content: "Angular", completed: false }));
xhr.onload = () => {
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.error("Error", xhr.status, xhr.statusText);
}
};
response
{ "todos": [ { "id": 1, "content": "JS", "completed": true }, { "id": 2, "content": "React", "completed": false }, { "id": 3, "content": "FrontEnd", "completed": false }, { "id": 4, "content": "Angular", "completed": false } ] }
44.3.6 PUT ์์ฒญ
const xhr = new XMLHttpRequest();
/**
* 1. ํ์๋ ๋ฉ์๋๋ก PUT ์์ฒญ์ผ๋ก ๋ฆฌ์์ค์ ์ฒด๋ฅผ ๊ต์ฒดํ๊ฒ ๋ค
* 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, id๋ก todo๋ฅผ ํน์ ํ์ฌ id๋ฅผ ์ ์ธํ ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒด
*/
xhr.open("PUT", "/todos/4");
// ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ๋ณด๋ผ ํ์ด๋ก๋์ MIME ํ์
์ง์
xhr.setRequestHeader("Content-Type", "application/json");
// ํ์ด๋ก๋๋ฅผ ๋ด์์ ์๋ฒ๋ก ์ ์ก
xhr.send(JSON.stringify({ id: "4", content: "Svelte", completed: false }));
xhr.onload = () => {
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.error("Error", xhr.status, xhr.statusText);
}
};
response
{ "todos": [ { "id": 4, "content": "Svelte", "completed": false } ] }
44.3.7 PATCH ์์ฒญ
const xhr = new XMLHttpRequest();
/**
* 1. ํ์๋ ๋ฉ์๋๋ก, PATCH ์์ฒญ์ผ๋ก ๋ฆฌ์์ค์ ์ฒด๋ฅผ ๊ต์ฒดํ๊ฒ ๋ค
* 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, id๋ก todo๋ฅผ ํน์ ํ์ฌ ์ํ๋ ๋ถ๋ถ๋ง ๊ต์ฒด
*/
xhr.open("PATCH", "/todos/2");
// ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ๋ณด๋ผ ํ์ด๋ก๋์ MIME ํ์
์ง์
xhr.setRequestHeader("Content-Type", "application/json");
// ํ์ด๋ก๋๋ฅผ ๋ด์์ ์๋ฒ๋ก ์ ์ก
xhr.send(JSON.stringify({ completed: true }));
xhr.onload = () => {
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.error("Error", xhr.status, xhr.statusText);
}
};
response
{ "todos": [ { "id": 2, "content": "React", "completed": true } ] }
44.3.8 DELETE ์์ฒญ
/**
* 1. ํ์๋ ๋ฉ์๋๋ก DELETE ์์ฒญ์ผ๋ก ๋ฆฌ์์ค๋ฅผ ์ญ์ ํ๊ฒ ๋ค
* 2. ๋ฆฌ์์ค๋ URI๋ก ํํ, id๋ก todo๋ฅผ ํน์ ํ์ฌ ํด๋น todo ์ญ์
*/
xhr.open("PATCH", "/todos/4");
// ํ์ด๋ก๋๋ฅผ ๋ด์์ ์๋ฒ๋ก ์ ์ก
xhr.send(JSON.stringify({ completed: true }));
xhr.onload = () => {
if (xhr.status === 200) {
console.log(xhr.response);
} else {
console.error("Error", xhr.status, xhr.statusText);
}
};
response
{}
Last updated