nodeJS - REST와 라우팅
서버에 요청을 보낼 때 주소를 통해 요청의 내용 표현. (주소가 /inde.html이면 서버의 index.html을 보내달라는 뜻) 요청의 내용이 주소를 통해 표현되므로 서버가 이해하기 쉬운 주소를 사용해야한다.
REST(REpresentational State Transfer) : 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법.
[Request Method]
GET : 서버의 자원을 가져올 때 사용. request의 본문에 데이터를 넣지 않는다. 데이터를 서버에 보내야한다면 쿼리스트링 사용. 브라우저에서 캐싱(기억)할 수도 있으므로 같은 주소로 GET 요청을 할 때 서버에서 가져오는 것이 아닌 캐시에서 가져 올 수 있다.(성능 향상)
POST : 서버에 자원을 새로 등록 할 때 사용. request의 본문에는 새로 등록할 데이터를 넣어서 보낸다.
PUT : 서버의 자원을 request에 들어 있는 자원으로 치환 할 때 사용. request의 본문에 치환할 데이터를 넣어보낸다.
PATCH : 서버의 자원의 일부만 수정하고자 할 때 사용. request의 본문에 데이터를 넣지 않는다.
OPTIONS : request 하기 전에 통신 옵션을 설명하기 위해 사용
위 메서드로 표현하기 애매한 로그인 같은 동작이 있다면 그냥 POST 사용.
주소 하나가 request 메서드를 여러개 가질 수 있다.
const http = require('http');
const fs = require('fs').promises;
// 데이터 베이스 대용으로 users 객체 선언하여 사용자 정보 저장.
const users = {};
http.createServer(async (req, res) => {
try {
// req.method로 HTTP 요청 메서드 구분.
// 만약 method가 GET이면
if (req.method === 'GET') {
// req.url로 요청 주소를 구분.
// 만약주소가 / 일때
if (req.url === '/') {
// restFront.html 제공
const data = await fs.readFile('./restFront.html');
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
// res.end 앞에 return을 붙이는 이유 :
// res.end를 호출 한다고 해서 함수가 종료되지 않는다. 자바 스크립트에서는 return을 붙이지 않는 한 함수 가 종료되지 않는다.
// 그러므로 반드시 return을 붙여야한다.
return res.end(data);
}
// 주소가 about이면 about.html 파일 제공
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 발생
}
}
// POST /user 요청에서 사용자를 새로 저장
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');
});
}
}
// PUT /user/아이디 요청 : 해당 아이디의 사용자 데이터 수정.
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);
// 받은 데이터는 문자열이므로 JSON으로 만드는 JSON.parse 과정 필요
users[key] = JSON.parse(body).name;
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
return res.end('ok');
});
}
}
// DELETE /user/아이디 요청 : 해당 아이디의 사용자 제거
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');
}
}
// 만약 존재하지 않는 파일을 요청했거나 GET 메서드 요청이 아닌 경우 404 NOT FOUND 에러가 응답으로 전송
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번 포트에서 서버 대기 중입니다');
});