HTML 코드는 간단하게 작성되어 있어, 이 블로그 글에 첨부하지 않을 예정입니다.
아래 깃허브 주소를 통해 확인해주세요.
https://github.com/GangHyun95/restful-api-practice
GitHub - GangHyun95/restful-api-practice: practice
practice. Contribute to GangHyun95/restful-api-practice development by creating an account on GitHub.
github.com
RESTful Server란?
RESTful Server는 REST 규칙을 잘 준수하는 API 서버를 의미합니다. REST는 자원을 URL로 표현하고, HTTP 메서드(GET, POST, PUT, DELETE 등)를 활용하여 CRUD(Create, Read, Update, Delete) 작업을 수행하는 구조를 따릅니다.
예를 들어
- /users URL에 GET 요청을 보내면 모든 사용자 목록을 조회할 수 있습니다.
- /user/:id URL에 PUT 요청을 보내면 특정 사용자를 수정할 수 있습니다.
서버 코드
- GET 요청: 사용자 목록 제공
- POST 요청: 새 사용자 추가
- PUT 요청: 사용자 정보 수정
- DELETE 요청: 사용자 삭제
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
const users = {}; // 사용자 데이터를 저장할 메모리 객체
const sendResponse = (res, statusCode, contentType, data) => {
res.writeHead(statusCode, { 'Content-Type': `${contentType}; charset=utf-8` });
res.end(data);
};
const handleFileRequest = async (res, filePath, contentType) => {
try {
const data = await fs.readFile(filePath);
sendResponse(res, 200, contentType, data);
} catch (error) {
sendResponse(res, 404, 'text/plain', 'NOT FOUND');
}
};
// GET 요청 처리
const handleGetRequest = async (req, res) => {
if (req.url === '/') {
return handleFileRequest(res, path.join(__dirname, 'restFront.html'), 'text/html');
} else if (req.url === '/users') {
return sendResponse(res, 200, 'application/json', JSON.stringify(users));
}
return handleFileRequest(res, path.join(__dirname, req.url), 'application/octet-stream');
};
// POST 요청 처리
const handlePostRequest = (req, res) => {
if (req.url === '/user') {
let body = '';
req.on('data', (data) => {
body += data;
});
req.on('end', () => {
try {
const { name } = JSON.parse(body);
const id = Date.now();
users[id] = name;
sendResponse(res, 201, 'text/plain', '등록 성공');
} catch (error) {
sendResponse(res, 400, 'text/plain', 'Invalid JSON');
}
});
} else {
sendResponse(res, 404, 'text/plain', 'NOT FOUND');
}
};
// PUT 요청 처리
const handlePutRequest = (req, res) => {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
let body = '';
req.on('data', (data) => {
body += data;
});
req.on('end', () => {
try {
const { name } = JSON.parse(body);
if (users[key]) {
users[key] = name;
sendResponse(res, 200, 'application/json', JSON.stringify(users));
} else {
sendResponse(res, 404, 'text/plain', 'User not found');
}
} catch (error) {
sendResponse(res, 400, 'text/plain', 'Invalid JSON');
}
});
} else {
sendResponse(res, 404, 'text/plain', 'NOT FOUND');
}
};
// DELETE 요청 처리
const handleDeleteRequest = (req, res) => {
if (req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
if (users[key]) {
delete users[key];
sendResponse(res, 200, 'application/json', JSON.stringify(users));
} else {
sendResponse(res, 404, 'text/plain', 'User not found');
}
} else {
sendResponse(res, 404, 'text/plain', 'NOT FOUND');
}
};
http.createServer(async (req, res) => {
try {
console.log(req.method, req.url);
if (req.method === 'GET') {
await handleGetRequest(req, res);
} else if (req.method === 'POST') {
handlePostRequest(req, res);
} else if (req.method === 'PUT') {
handlePutRequest(req, res);
} else if (req.method === 'DELETE') {
handleDeleteRequest(req, res);
} else {
sendResponse(res, 405, 'text/plain', 'Method Not Allowed');
}
} catch (error) {
console.error(error);
sendResponse(res, 500, 'text/plain', 'Internal Server Error');
}
}).listen(8080, () => {
console.log('8080번 포트에서 서버 대기 중입니다.');
req.on('data', ...)
Node.js의 HTTP 서버는 요청 데이터를 스트림(Stream)으로 처리합니다.
클라이언트에서 전송한 데이터가 한 번에 도착하지 않고, 조각(chunk) 단위로 나누어 도착합니다. 이 데이터를 처리하기 위해 req.on('data', ...) 이벤트를 사용합니다.
let body = '';
req.on('data', (data) => {
body += data; // 들어오는 데이터 조각을 누적
});
req.on('end', () => {
console.log('데이터 수신 완료:', body);
});
- 큰 데이터 처리: 요청 데이터가 클 경우, 데이터를 한꺼번에 메모리에 로드하면 비효율적입니다. 스트림 처리를 통해 메모리 사용량을 줄이고 효율적으로 데이터를 처리합니다.
- 비동기 처리: 데이터를 조각 단위로 처리하여, I/O 작업의 효율성을 극대화합니다.
클라이언트 코드
- 사용자 목록 조회 (GET)
- 사용자 추가 (POST)
- 사용자 수정 (PUT)
- 사용자 삭제 (DELETE)
// GET
async function fetchUsers() {
try {
const response = await fetch('/users');
if (!response.ok) {
throw new Error('Failed to fetch users: ' + response.statusText);
}
return await response.json();
} catch (error) {
console.error(error);
alert('사용자 목록을 가져오는 데 실패했습니다.');
return {};
}
}
// PUT
async function updateUser(key, name) {
try {
const response = await fetch(`/user/${key}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
if (!response.ok) {
throw new Error('Failed to update user: ' + response.statusText);
}
} catch (error) {
console.error(error);
alert('사용자 수정에 실패했습니다.');
}
}
// DELETE
async function deleteUser(key) {
try {
const response = await fetch(`/user/${key}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error('Failed to delete user: ' + response.statusText);
}
} catch (error) {
console.error(error);
alert('사용자 삭제에 실패했습니다.');
}
}
// POST
async function addUser(name) {
try {
const response = await fetch('/user', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
if (!response.ok) {
throw new Error('Failed to add user: ' + response.statusText);
}
} catch (error) {
console.error(error);
alert('사용자 추가에 실패했습니다.');
}
}
function createUserElement(key, name) {
const userDiv = document.createElement('div');
userDiv.innerHTML = `
<span>${name}</span>
<button class="edit-button">수정</button>
<button class="delete-button">삭제</button>
`;
const editButton = userDiv.querySelector('.edit-button');
editButton.addEventListener('click', async () => {
const newName = prompt('바꿀 이름을 입력하세요.', name);
if (!newName) {
return alert('이름을 반드시 입력하셔야 합니다.');
}
await updateUser(key, newName);
await renderUserList();
});
const deleteButton = userDiv.querySelector('.delete-button');
deleteButton.addEventListener('click', async () => {
if (confirm('정말로 삭제하시겠습니까?')) {
await deleteUser(key);
await renderUserList();
}
});
return userDiv;
}
async function renderUserList() {
const users = await fetchUsers();
const list = document.querySelector('#list');
list.innerHTML = '';
Object.entries(users).forEach(([key, name]) => {
const userElement = createUserElement(key, name);
list.appendChild(userElement);
});
}
document.querySelector('#form').addEventListener('submit', async (e) => {
e.preventDefault();
const name = e.target.username.value.trim();
if (!name) {
return alert('이름을 입력하세요.');
}
await addUser(name);
await renderUserList();
e.target.username.value = '';
});
document.addEventListener('DOMContentLoaded', renderUserList);
'Node.js > 정리' 카테고리의 다른 글
쿠키와 세션을 활용한 로그인 구현 및 보안 개선 (0) | 2025.01.20 |
---|---|
Node.js로 HTTP 서버 만들어보기 (0) | 2025.01.06 |
Node.js 란? (1) | 2025.01.05 |