REST๋ HTTP๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ๋ฆฌ์์ค์ ์ ๊ทผํ๋ ๋ฐฉ์์ ๊ท์ ํ ์ํคํ
์ฒ๊ณ , REST API๋ REST๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋น์ค API๋ฅผ ๊ตฌํํ ๊ฒ์ ์๋ฏธํ๋ค.
REST API๋ ์์, ํ์, ํํ์ 3๊ฐ์ง ์์๋ก ๊ตฌ์ฑ๋๋ค. REST๋ ์์ฒด ํํ ๊ตฌ์กฐ๋ก ๊ตฌ์ฑ๋์ด REST API๋ง์ผ๋ก HTTP ์์ฒญ์ ๋ด์ฉ์ ์ดํดํ ์ ์๋ค.
๊ตฌ์ฑ์์ | ๋ด์ฉ | ํํ ๋ฐฉ๋ฒ |
---|---|---|
์์ | ์์ | URI(์๋ํฌ์ธํธ) |
ํ์ | ์์์ ๋ํ ํ์ | HTTP ์์ฒญ ๋ฉ์๋ |
ํํ | ์ง์์ ๋ํ ํ์์ ๊ตฌ์ฒด์ ๋ด์ฉ | ํ์ด๋ก๋ |
REST์์ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ณธ์ ์ธ ์์น์ ๋ ๊ฐ์ง๋ค. URI๋ ๋ฆฌ์์ค๋ฅผ ํํํ๋ ๋ฐ ์ง์คํ๊ณ ํ์์ ๋ํ ์ ์๋ HTTP ์์ฒญ ๋ฉ์๋๋ฅผ ํตํด ํ๋ ๊ฒ์ด RESTful API๋ฅผ ์ค๊ณํ๋ ์ค์ฌ ๊ท์น์ด๋ค.
URI๋ ๋ฆฌ์์ค๋ฅผ ํํํ๋ ๋ฐ ์ค์
์ ๋์ด์ผ ํ๋ค. ๋ฆฌ์์ค๋ฅผ ์๋ณํ ์ ์๋ ์ด๋ฆ์ ๋์ฌ๋ณด๋ค๋ ๋ช
์ฌ๋ฅผ ์ฌ์ฉ
ํ๋ค. ๋ฐ๋ผ์ ์ด๋ฆ์ get ๊ฐ์ ํ์์ ๋ํ ํํ์ด ๋ค์ด๊ฐ์๋ ์ ๋๋ค.
#bad
GET /getTodos/1
GET /todos/show/1
#good
GET/todos/1
HTTP ์์ฒญ ๋ฉ์๋๋ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์๊ฒ ์์ฒญ์ ์ข ๋ฅ์ ๋ชฉ์ ์ ์๋ฆฌ๋ ๋ฐฉ๋ฒ์ด๋ค. ์ฃผ๋ก 5๊ฐ์ง ์์ฒญ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ CRUD๋ฅผ ๊ตฌํํ๋ค.
HTTP ์์ฒญ ๋ฉ์๋ | ์ข ๋ฅ | ๋ชฉ์ | ํ์ด๋ก๋ |
---|---|---|---|
GET | index/retrieve | ๋ชจ๋ /ํน์ ๋ฆฌ์์ค ์ทจ๋ | X |
POST | create | ๋ฆฌ์์ค ์์ฑ | O |
PUT | replace | ๋ฆฌ์์ค์ ์ ์ฒด ๊ต์ฒด | O |
PATCH | modify | ๋ฆฌ์์ค์ ์ผ๋ถ ์์ | O |
DELETE | delete | ๋ชจ๋ /ํน์ ๋ฆฌ์์ค ์ญ์ | X |
๋ฆฌ์์ค์ ๋ํ ํ์๋ HTTP ์์ฒญ ๋ฉ์๋๋ฅผ ํตํด ํํํ๋ฉฐ URI์ ํํํ์ง ์๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฆฌ์์ค๋ฅผ ์ทจ๋ํ๋ ๊ฒฝ์ฐ์๋ GET, ๋ฆฌ์์ค๋ฅผ ์ญ์ ํ๋ ๊ฒฝ์ฐ์๋ DELETE๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌ์์ค์ ๋ํ ํ์๋ฅผ ๋ช ํํ ํํํ๋ค.
HTTP ์์ฒญ์ ์ ์กํ๊ณ ์๋ต์ ๋ฐ์ผ๋ ค๋ฉด ์๋ฒ๊ฐ ํ์ํ๋ค. JSON Server๋ฅผ ์ฌ์ฉํด ๊ฐ์ REST API ์๋ฒ๋ฅผ ๊ตฌ์ถํ์ฌ HTTP ์์ฒญ์ ์ ์กํ๊ณ ์๋ต์ ๋ฐ๋ ์ค์ต์ ํด๋ณด์.
JSON Server๋ json ํ์ผ์ ์ฌ์ฉํ์ฌ ๊ฐ์ REST API ์๋ฒ๋ฅผ ๊ตฌ์ถํ ์ ์๋ ํด์ด๋ค. ์ฌ์ฉ๋ฒ์ ๋งค์ฐ ๊ฐ๋จํ๋ค. ๋จผ์ npm์ ์ฌ์ฉํ์ฌ JSON Server๋ฅผ ์ค์นํ์.
ํฐ๋ฏธ๋์์ ๋ค์๊ณผ ๊ฐ์ด ๋ช
๋ น์ด๋ฅผ ์
๋ ฅํ์ฌ JSON Server๋ฅผ ์ค์นํ๋ค.
$ mkdir json-server-exam && cd json-server-exam
$ npm init -y
$ npm install json-server --save-dev
ํ๋ก์ ํธ ๋ฃจํธ ํด๋(/json-server-exam)์ ๋ค์๊ณผ ๊ฐ์ด db.json ํ์ผ์ ์์ฑํ๋ค. db.json ํ์ผ์ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ญํ ์ ํ๋ค.
{
"todos": [
{
"id":1,
"content": "HTML",
"completed": true
},
{
"id":2,
"content": "CSS",
"completed": false
},
{
"id":3,
"content": "JavaScript",
"completed": true
},
]
}
ํฐ๋ฏธ๋์์ ๋ค์๊ณผ ๊ฐ์ด ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ฌ JSON Server๋ฅผ ์คํํ๋ค. JSON Server๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ญํ ์ ํ๋ db.json ํ์ผ์ ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ฒ ํ๋ ค๋ฉด watch ์ต์ ์ ์ถ๊ฐํ๋ค.
##๊ธฐ๋ณธ ํฌํธ(3000) ์ฌ์ฉ / watch ์ต์
์ ์ฉ
$ json-server --watch db.json
todos ๋ฆฌ์์ค์์ ๋ชจ๋ todo๋ฅผ ์ทจ๋(index)ํ๋ค.
JSON Server์ ๋ฃจํธ ํด๋(/json-server-exam)์ public ํด๋๋ฅผ ์์ฑํ๊ณ JSON Server๋ฅผ ์ค๋จํ ํ ์ฌ์คํํ๋ค. ๊ทธ๋ฆฌ๊ณ public ํด๋์ ๋ค์ get_index.html์ ์ถ๊ฐํ๊ณ ๋ธ๋ผ์ฐ์ ์์ http://localhost:3000/get_index.html๋ก ์ ์ํ๋ค
<!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๋ก ์ ์ํ๋ค
<!DOCTYPE html>
<html>
<body>
<pre></pre>
<script>
const xhr = new XMLHttpRequest();
xhr.open("GET", "/todos/1");
xhr.send();
xhr.onload = () => {
if (xhr.status === 200) {
document.querySelector('pre').textContent = xhr.response;
} else {
console.error("Error", xhr.status, xhr.statusText);
}
};
</script>
</body>
</html>
todos ๋ฆฌ์์ค์ ์๋ก์ด todo๋ฅผ ์์ฑํ๋ค. POST ์์ฒญ ์์๋ setRequestHeader ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ์ ์กํ ํ์ด๋ก๋์ MIME ํ์
์ ์ง์ ํด์ผ ํ๋ค.
public ํด๋์ ๋ค์ post.html์ ์ถ๊ฐํ๊ณ http://localhost:3000/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>
PUT์ ํน์ ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒดํ ๋ ์ฌ์ฉํ๋ค. ๋ค์ ์์ ์์๋ todos ๋ฆฌ์์ค์์ id๋ก todo๋ฅผ ํน์ ํ์ฌ id๋ฅผ ์ ์ธํ ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ๊ต์ฒดํ๋ค. PUT ์์ฒญ ์์๋ setRequestHeader ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ์ ์กํ ํ์ด๋ก๋์ MIME ํ์
์ ์ง์ ํด์ผ ํ๋ค.
public ํด๋์ ๋ค์ put.html์ ์ถ๊ฐํ๊ณ ๋ธ๋ผ์ฐ์ ์์ http://localhost:3000/put.html๋ก ์ ์ํ๋ค
<!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>
PATCH๋ ํน์ ๋ฆฌ์์ค์ ์ผ๋ถ๋ฅผ ์์ ํ ๋ ์ฌ์ฉํ๋ค. ๋ค์ ์์ ์์๋ todos ๋ฆฌ์์ค์ id๋ก todo๋ฅผ ํน์ ํ์ฌ completed๋ง ์์ ํ๋ค. PATCH ์์ฒญ ์์๋ setRequestHeader ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ ๋ชธ์ฒด์ ๋ด์ ์๋ฒ๋ก ์ ์กํ ํ์ด๋ก๋์ MIME ํ์
์ ์ง์ ํด์ผ ํ๋ค.
public ํด๋์ ๋ค์ patch.html์ ์ถ๊ฐํ๊ณ ๋ธ๋ผ์ฐ์ ์์ http://localhost:3000/patch.html๋ก ์ ์ํ๋ค.
<!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>
todos ๋ฆฌ์์ค์์ id๋ฅผ ์ฌ์ฉํ์ฌ todo๋ฅผ ์ญ์ ํ๋ค. public ํด๋์ ๋ค์ delete.html์ ์ถ๊ฐํ๊ณ ๋ธ๋ผ์ฐ์ ์์ http://localhost:3000/delete.html๋ก ์ ์ํ๋ค
<!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>