서버 측 소스를 다시 살펴보겠습니다.
//restServer.js
const http = require('http');
const fs = require('fs').promises;
const users = {}; // 데이터 저장용
http.createServer(async (req, res) => {
try {
if (req.method === 'GET') {
if (req.url === '/') {
const data = await fs.readFile('./restFront.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(data);
} else if (req.url === '/about') {
const data = await fs.readFile('./about.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
return res.end(data);
} else if (req.url === '/users') {
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
return res.end(JSON.stringify(users));
}
// /도 /about도 /users도 아니면
try {
const data = await fs.readFile(`.${req.url}`);
return res.end(data);
} catch (err) {
// 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
}
} else if (req.method === 'POST') {
if (req.url === '/user') {
let body = '';
// 요청의 body를 stream 형식으로 받음
req.on('data', (data) => {
body += data;
});
// 요청의 body를 다 받은 후 실행됨
return req.on('end', () => {
console.log('POST 본문(Body):', body);
const { name } = JSON.parse(body);
const id = Date.now();
users(id) = name;
res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('ok');
});
}
} else if (req.method === 'PUT') {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')(2);
let body = '';
req.on('data', (data) => {
body += data;
});
return req.on('end', () => {
console.log('PUT 본문(Body):', body);
users(key) = JSON.parse(body).name;
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('ok');
});
}
} else if (req.method === 'DELETE') {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')(2);
delete users(key);
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('ok');
}
}
res.writeHead(404);
return res.end('NOT FOUND');
} catch (err) {
console.error(err);
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(err.message);
}
})
.listen(8082, () => {
console.log('8082번 포트에서 서버 대기 중입니다');
});
/about 요청은 주소에 직접 입력해도 되지만 기본적으로 a 태그는 GET 요청이므로 간단히
localhost:8082 페이지에서 Home About a tag를 클릭하면 /about 부분이 실행됩니다.
*css나 js 파일을 불러오는 부분이 있었습니다.
//restFront.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="http://kmryu807.m/./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="http://kmryu807.about">About</a>
</nav>
<div>
<form id="form">
<input type="text" id="username">
<button type="submit">등록</button>
</form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="http://kmryu807.m/./restFront.js"></script>
</body>
</html>
이러한 모든 부분은 서버에 GET 요청을 보냅니다. 따라서 이러한 부분은 /about 또는 /users에는 적용되지 않습니다.
GET의 시도 부분이 실행됩니다. 여기에 해당하지 않으면 catch가 실행됩니다.
다음은 등록 버튼을 눌렀을 때의 모습입니다.
입력란에 123123을 입력하고 Register를 눌러 restFront.html의 양식 부분을 실행합니다.
//restFront.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="http://kmryu807.m/./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="http://kmryu807.about">About</a>
</nav>
<div>
<form id="form">
<input type="text" id="username">
<button type="submit">등록</button>
</form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="http://kmryu807.m/./restFront.js"></script>
</body>
</html>
이 양식 부분은 restFront.js와 연결됩니다.
//restFront.js
async function getUser() { // 로딩 시 사용자 가져오는 함수
try {
const res = await axios.get('/users');
const users = res.data;
const list = document.getElementById('list');
list.innerHTML = '';
// 사용자마다 반복적으로 화면 표시 및 이벤트 연결
Object.keys(users).map(function (key) {
const userDiv = document.createElement('div');
const span = document.createElement('span');
span.textContent = users(key);
const edit = document.createElement('button');
edit.textContent="수정";
edit.addEventListener('click', async () => { // 수정 버튼 클릭
const name = prompt('바꿀 이름을 입력하세요');
if (!name) {
return alert('이름을 반드시 입력하셔야 합니다');
}
try {
await axios.put('/user/' + key, { name });
getUser();
} catch (err) {
console.error(err);
}
});
const remove = document.createElement('button');
remove.textContent="삭제";
remove.addEventListener('click', async () => { // 삭제 버튼 클릭
try {
await axios.delete('/user/' + key);
getUser();
} catch (err) {
console.error(err);
}
});
userDiv.appendChild(span);
userDiv.appendChild(edit);
userDiv.appendChild(remove);
list.appendChild(userDiv);
console.log(res.data);
});
} catch (err) {
console.error(err);
}
}
window.onload = getUser; // 화면 로딩 시 getUser 호출
// 폼 제출(submit) 시 실행
document.getElementById('form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = e.target.username.value;
if (!name) {
return alert('이름을 입력하세요');
}
try {
await axios.post('/user', { name });
getUser();
} catch (err) {
console.error(err);
}
e.target.username.value="";
});
document.getElementById(‘form’).addEventListener(‘submit’, async (3) at the end => {
restFront.html의 폼 부분이 실행되면 위의 코드 부분이 실행됩니다.
try 문을 보면 post 방식을 사용하고 url이 /user인 것을 알 수 있습니다.
그런 다음 서버는 게시 및 사용자 요청에 적합한 응답도 구현해야 합니다.
POST 및 /user 요청에 응답하는 코드는 restServer.js에서 찾을 수 있습니다.
그러나 POST 요청에는 데이터가 포함되어 있으므로 데이터를 처리해야 합니다.
코드를 보면, 이것이 데이터를 얻는 방법이라고 생각할 수 있습니다. 형식만 기억하시면 됩니다.
더 설명하면 청크 단위로 데이터를 받아 마지막에 바디에 마지막으로 받은 데이터를 처리한다.
*요청 처리 여부는 웹브라우저(크롬 기준)의 개발자 도구에서 네트워크 탭으로 들어가시면 확인하실 수 있습니다.

POST 메서드의 요청이 201 상태, 즉 잘 처리되었음을 알 수 있습니다.

Body에서 {key : value} 형태로 데이터가 전송되는 것을 확인할 수 있다.
데이터 수정인 PUT과 데이터 삭제인 DELETE도 이 흐름에서 동작한다.
데이터를 수신, 수정 및 삭제하는 방법의 형식과 구문을 실제로 시도하고 숙지하는 것이 중요합니다.
적어도 서버 측과 전면 측에서는 이런 방식으로 데이터를 교환하는 흐름을 유지하는 것이 좋습니다.
또한 200, 201, 404 등을 http 상태 코드라고 합니다.
http 상태 코드에 대해서는 아래의 공식 문서를 확인하는 것이 좋습니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Status
