REST API μ νμ
REST λ 2000λ
λ°ν λΉμμ μΉμ΄ HTTP λ₯Ό μ λλ‘ μ¬μ©νμ§ λͺ»νκ³ μλ μν©μ λ³΄κ³ HTTP μ μ₯μ μ μ΅λν νμ©ν μ μλ μν€ν
μ²λ‘μ μκ°λμλ€.
REST λ HTTP νλ‘ν μ½μ μλμ λ§κ² λμμΈνλλ‘ μ λνκ³ μλ€.
REST μ κΈ°λ³Έ μμΉμ μ±μ€ν μ§ν¨ μλΉμ€ λμμΈμ "RESTfulπ΄" μ΄λΌκ³ νννλ€.
REST λ REpresentational State Transfer (μν μ μ΄ νν) μ μ½μλ‘ μΉ μλΉμ€ κ°μ ν΅μ μ μν μννΈμ¨μ΄ μν€ν μ² μ€νμΌ μ€ νλμ΄λ€. μμμ νννκ³ μν μ μ΄λ₯Ό ν΅ν΄ λ°μ΄ν°λ₯Ό μ£Όκ³ λ°λ λ°©μμ μλ―Ένλ€. λ€μν ν΄λΌμ΄μΈνΈ μ ν리μΌμ΄μ κ³Ό μλ² κ°μ λ°μ΄ν° ν΅μ μ κ°λ₯νκ² νλ€.
API λ Application Programming Interface μ μ½μλ‘, μμ© νλ‘κ·Έλ¨ κ°μ μνΈμμ©μ μν μΈν°νμ΄μ€λ₯Ό μλ―Ένλ€. λ€λ₯Έ μμ© νλ‘κ·Έλ¨μ΄λ μλΉμ€μ κΈ°λ₯κ³Ό λ°μ΄ν°μ μ κ·Όν μ μλλ‘ μ μλ λ©μλ, κ·μ½, λꡬ λ±μ μ§ν©μ΄λ€. API λ λ€μν ννλ‘ μ 곡λ μ μλλ°, REST API λ μΉ μλΉμ€μμ μ¬μ©λλ κ² μ€ νλλ‘, μν μ μ΄ νν ( REST ) μ μμΉμ λ°λ₯΄λ API μ΄λ€.
π’π REST λ HTTP λ₯Ό κΈ°λ°μΌλ‘ ν΄λΌμ΄μΈνΈκ° μλ²μ 리μμ€μ μ κ·Όνλ λ°©μμ κ·μ ν μν€ν μ²μ΄κ³ , REST API λ REST λ₯Ό κΈ°λ°μΌλ‘ μλΉμ€ API λ₯Ό ꡬνν κ²μ μλ―Ένλ€.
REST API λ μμ resource, νμ verb, νν representations μ 3κ°μ§ μμλ‘ κ΅¬μ±λλ€. μ체 νν κ΅¬μ‘°λ‘ κ΅¬μ±λμ΄ REST API λ§μΌλ‘ HTTP μμ²μ λ΄μ©μ μ΄ν΄ν μ μλ€.
| κ΅¬μ± μμ | λ΄μ© | νν λ°©λ² | μμ |
|---|---|---|---|
| μμ Resource |
μμ | URI(μλν¬μΈνΈ) | μ¬μ©μ μ 보λ₯Ό λνλ΄λ μμμ μ κ·ΌνκΈ° μν¨/users: μ¬μ©μ μ 보μ μ κ·Ό/users/1: IDκ° 1μΈ μ¬μ©μ μ 보μ μ κ·Ό |
| νμ Verb |
μμμ λν νμ | HTTP μμ² λ©μλ | GET,POST,PUT,DELETE |
| νν Representation |
μμμ λν νμμ ꡬ체μ λ΄μ© | νμ΄λ‘λ | μμμ λν νμμ ꡬ체μ μΈ λ΄μ©μ λ΄λ λ°μ΄ν° |
RESTfulπ΄ νκΈ° μν κ·μΉ π
- URI λ 리μμ€λ₯Ό νννλλ° μ§μ€νκ³
- νμμ λν μ μλ HTTP μμ² λ©μλλ₯Ό ν΅ν΄ νλ€.
π 1. URI λ 리μμ€λ₯Ό ννν΄μΌ νλ€.
리μμ€λ₯Ό μλ³ν μ μλ μ΄λ¦μ λμ¬λ³΄λ€λ λͺ
μ¬λ₯Ό μ¬μ©νλ€.
μ΄λ¦μ get κ°μ νμμ λν ννμ΄ λ€μ΄κ°μλ μ λλ€.
# BAD ππ
GET / getTodos/1
# GOOD ππ
GET / todos/1
π 2. 리μμ€μ λν νμλ HTTP μμ² λ©μλλ‘ νννλ€.
HTTP μμ² λ©μλλ ν΄λΌμ΄μΈνΈκ° μλ²μκ² μμ²μ μ’ λ₯μ λͺ©μ μ μ리λ λ°©λ²μ΄λ€. μ£Όλ‘ 5κ°μ§ μμ² λ©μλλ₯Ό μ¬μ©νμ¬ CRUD λ₯Ό ꡬννλ€.
| HTTP μμ² λ©μλ | μ’ λ₯ | λͺ©μ | νμ΄λ‘λ |
|---|---|---|---|
GET |
index/retrieve | λͺ¨λ /νΉμ 리μμ€ μ·¨λ | X |
POST |
create | 리μμ€ μμ± | O |
PUT |
replace | 리μμ€μ μ 체 κ΅μ²΄ | O |
PATCH |
modify | 리μμ€μ μΌλΆ μμ | O |
DELETE |
delete | λͺ¨λ /νΉμ 리μμ€ μμ | X |
retrieve : νΉμ 리μμ€λ₯Ό κ°μ Έμ€λ κ² νμ΄λ‘λ : μμ²μ λ³Έλ¬Έμ λ΄κΈ°λ λ°μ΄ν°
리μμ€μ λν νμλ HTTP μμ² λ©μλλ₯Ό ν΅ν΄ νννλ©° URI μ νννμ§ μλλ€.
# BAD ππ
GET /todos/delete/1
# GOOD ππ
DELETE /todos/1
JSON Serverλ₯Ό μ¬μ©ν΄ κ°μ REST API μλ²λ₯Ό ꡬμΆνμ¬ HTTP μμ²μ μ μ‘νκ³ μλ΅μ λ°λ μ€μ΅μ μ§νν΄λ³΄μ!
mksir json-server-exam && json-server-exam
pnpm init-y
pnpm install json-server--save-dev
νλ‘μ νΈ λ£¨νΈ ν΄λμ db.json νμΌμ μμ±νλ€.
리μμ€λ₯Ό μ 곡νλ λ°μ΄ν°λ² μ΄μ€ μν μ νλ€.
44-01
{
"todos": [
{
"id": 1,
"content": "HTML",
"completed": true
},
{
"id": 2,
"content": "CSS",
"completed": false
},
{
"id": 3,
"content": "Javascript",
"completed": true
}
]
}$ json-server--watch db.json
db.json νμΌμ λ³κ²½μ κ°μ§νκΈ° μν΄ watch μ΅μ
μ μΆκ°νλ€.
$ json-server--watch db.json --port 5000
κΈ°λ³Έ ν¬νΈ (3000) λ³κ²½μ μν΄ port μ΅μ
μ μΆκ°νλ€.
λ§€λ² λͺ
λ Ήμ΄λ₯Ό μ
λ ₯νλ λ²κ±°λ‘μμ λκΈ° μν΄ package.json νμΌμ scripts λ₯Ό μλμ κ°μ΄ μμ νλ€.
44-02
{
"name": "json-server-exam",
"version": "1.0.0",
"scripts": {
"start": "json-server --watch db.json"
},
"devDependencies": {
"json-server": "^0.16.1"
}
}κ·Έ ν ν°λ―Έλμμ pnpm start λͺ
λ Ήμ΄λ₯Ό μ
λ ₯ν΄ JSON Server λ₯Ό μ€ννλ€.
todos 리μμ€μμ λͺ¨λ todo λ₯Ό μ·¨λ(index) νλ€.
JSON Server μ λ£¨νΈ ν΄μ μ public ν΄λλ₯Ό μμ±νκ³ JSON Server λ₯Ό μ€λ¨ν ν μ¬μ€ννλ€. κ·Έλ¦¬κ³ public ν΄λμ μλ get_index.html μ μΆκ°νκ³ λΈλΌμ°μ μμ http:/localhost:3000/get_index.html λ‘ μ μνλ€.
44-03
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μμ λͺ¨λ todoλ₯Ό μ·¨λ(index)
xhr.open('GET', '/todos');
// HTTP μμ² μ μ‘
xhr.send();
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>todos 리μμ€μμ id λ₯Ό μ¬μ©νμ¬ νΉμ todo λ₯Ό μ·¨λ(retrieve)νλ€. public ν΄λμ μλ get_retrieve.html μ μΆκ°νκ³ λΈλΌμ°μ μμ http://localhost:3000?get_retrieve.html λ‘ μ μνλ€.
44-04
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μμ idλ₯Ό μ¬μ©νμ¬ νΉμ todoλ₯Ό μ·¨λ(retrieve)
xhr.open('GET', '/todos/1');
// HTTP μμ² μ μ‘
xhr.send();
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>{
"id": 1,
"content": "HTML",
"completed": true
}μμ μ λ³΄κ° λ³΄μ΄κ² λλ€.
todos 리μ€νΈμ μλ‘μ΄ todo λ₯Ό μμ±νλ€.
setRequestHeader λ©μλλ₯Ό μ¬μ©νμ¬ μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ ν΄μΌ νλ€.
public ν΄λμ μλ post.html μ μΆκ°νκ³ λΈλΌμ°μ μμ http://localhost:3000/post.html λ‘ μ μνλ€.
44-05 : post.html
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μ μλ‘μ΄ todoλ₯Ό μμ±
xhr.open('POST', '/todos');
// μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ
xhr.setRequestHeader('content-type', 'application/json');
// HTTP μμ² μ μ‘
// μλ‘μ΄ todoλ₯Ό μμ±νκΈ° μν΄ νμ΄λ‘λλ₯Ό μλ²μ μ μ‘ν΄μΌ νλ€.
xhr.send(JSON.stringify({ id: 4, content: 'Angular', completed: false }));
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200(OK) λλ 201(Created)μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200 || xhr.status === 201) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>{
"id": 4,
"content": "Angular",
"completed": false
}μμ κ°μ΄ μ λ³΄κ° λ€μ΄κ°κ² λλ€.
PUT μ νΉμ 리μμ€ μ 체λ₯Ό κ΅μ²΄ν λ μ¬μ©νλ€. todos 리μμ€μμ id λ‘ todo λ₯Ό νΉμ νμ¬ id λ₯Ό μ μΈν 리μμ€ μ 체λ₯Ό κ΅μ²΄νλ€. PUT μμ²μμλ setRequestHeader λ©μλλ₯Ό μ¬μ©νμ¬ μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ ν΄μΌ νλ€.
public ν΄λμ μλ put.html μ μΆκ°νκ³ λΈλΌμ°μ μμ http://localhost:3000/put.htmlλ‘ μ μνλ€.
44-06
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μμ idλ‘ todoλ₯Ό νΉμ νμ¬ idλ₯Ό μ μΈν 리μμ€ μ 체λ₯Ό κ΅μ²΄
xhr.open('PUT', '/todos/4');
// μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ
xhr.setRequestHeader('content-type', 'application/json');
// HTTP μμ² μ μ‘
// 리μμ€ μ 체λ₯Ό κ΅μ²΄νκΈ° μν΄ νμ΄λ‘λλ₯Ό μλ²μ μ μ‘ν΄μΌ νλ€.
xhr.send(JSON.stringify({ id: 4, content: 'React', completed: true }));
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>{
"id": 4,
"content": "React",
"completed": true
}μμ κ°μ μ 보λ₯Ό νμΈν μ μλ€.
PATCH λ νΉμ 리μμ€μ μΌλΆλ₯Ό μμ ν λ μ¬μ©νλ€. λ€μ μμ μμλ todos 리μμ€μ idλ‘ todo λ₯Ό νΉμ νμ¬ completed λ§ μμ νλ€. PATCH μμ² μμλ setRequestHeader λ©μλλ₯Ό μ¬μ©νμ¬ μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ ν΄μΌ νλ€.
public ν΄λμ μλ patch.htmlμ μΆκ°νκ³ λΈλΌμ°μ μμ http://localhost:3000/patch.htmlλ‘ μ μνλ€.
44-07
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μ idλ‘ todoλ₯Ό νΉμ νμ¬ completedλ§ μμ
xhr.open('PATCH', '/todos/4');
// μμ² λͺΈμ²΄μ λ΄μ μλ²λ‘ μ μ‘ν νμ΄λ‘λμ MIME νμ
μ μ§μ
xhr.setRequestHeader('content-type', 'application/json');
// HTTP μμ² μ μ‘
// 리μμ€λ₯Ό μμ νκΈ° μν΄ νμ΄λ‘λλ₯Ό μλ²μ μ μ‘ν΄μΌ νλ€.
xhr.send(JSON.stringify({ completed: false }));
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>{
"id": 4,
"content": "React",
"completed": false
}μμ κ°μ΄ completed μ μ λ³΄κ° false λ‘ λ°λ κ²μ νμΈν μ μλ€.
todos 리μμ€μμ id λ₯Ό μ¬μ©νμ¬ todo λ₯Ό μμ νλ€.
public ν΄λμ λ€μ delete.htmlμ μΆκ°νκ³ λΈλΌμ°μ μμ http://localhost:3000/delete.htmlλ‘ μ μνλ€.
44-08
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
// XMLHttpRequest κ°μ²΄ μμ±
const xhr = new XMLHttpRequest();
// HTTP μμ² μ΄κΈ°ν
// todos 리μμ€μμ idλ₯Ό μ¬μ©νμ¬ todoλ₯Ό μμ νλ€.
xhr.open('DELETE', '/todos/4');
// HTTP μμ² μ μ‘
xhr.send();
// load μ΄λ²€νΈλ μμ²μ΄ μ±κ³΅μ μΌλ‘ μλ£λ κ²½μ° λ°μνλ€.
xhr.onload = () => {
// status νλ‘νΌν° κ°μ΄ 200μ΄λ©΄ μ μμ μΌλ‘ μλ΅λ μνλ€.
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error('Error', xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>{}
μμ κ°μ΄ μ λ³΄κ° μμ λμλ€.