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 μμ²μ λ΄μ©μ μ΄ν΄ν μ μλ€.
νμ΄λ‘λ : ν° λ°μ΄ν° μ€μ 'ν₯λ―Έμλ' λ°μ΄ν°λ₯Ό ꡬλ³νλ λ° μ¬μ©.
νλ‘κ·Έλλ°μμ μ£Όλ‘ λ©μΈμ§ νλ‘ν μ½ μ€μ νλ‘ν μ½ μ€λ²ν€λμ μνλ λ°μ΄ν°(μ¦ νμ΄λ‘λ)λ₯Ό ꡬλΆν λ μ¬μ©νλ€
μμ - 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 μμ² λ©μλλ ν΄λΌμ΄μΈνΈκ° μλ²μκ² μμ²μ μ’
λ₯μ λͺ©μ (리μμ€μ λν νμ)μ μ리λ λ°©λ²μ΄λ€.
# 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);
}
};