1. http 서버 만들기
* 포트 연결
- http 요청에 응답하는 노드 서버를 만들어 보자.
const http = require('http');
http.createServer((req, res) => {
// 응답 내용
});
- 위와 같이 http를 불러와서 createServer로 서버를 만들 수 있다.
- res로 응답을 보내는데 write로 응답내용을 적고, end로 끝을 낸다.
// server1.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'content-Type': 'text/html; charset=utf-8' });
res.write('<h1>Hello Node!</h1>');
res.write('<p>Hello server</p>');
res.end('<p>Hello Harry</p>');
})
.listen(8080);
server.on('listening', () => {
console.log('8080번 포트에서 대기 중 입니다.')
});
server.on('error', (error) => {
console.error(error);
})
- writeHead를 통해 헤드를 작성할 수 있다.
- write로 html태그를 작성할 수 있으며, 마지막에는 end로 끝을 냈다.
- listen메서드로 8080번 포트에 연결시켰다. 참고로 포트는 서버 내에서 프로세스를 구분하는 번호이다.
- on 메서드를 통해 이벤트 리스너를 작성할 수 있다. listening과 error 이벤트를 붙였고, 서버가 올바르게 실행되면 콘솔창에 안내 메시지를 띄우도록 하였다.
* 전송
- 실제 서버도 처음에 클라이언트에 html 문서를 보여줘야하는데, 위처럼 하나하나 태그를 작성하는 것은 너무 비효율 적이다.
- 따라서 노드에서 파일을 읽고, 파일을 통째로 클라이언트에 전송하는 것이 낫다.
<!-- server2.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Harry 웹 서버</title>
</head>
<body>
Hello Node Server
</body>
</html>
- 먼저 html 문서를 작성해 주었다.
// server2.js
const http = require('http');
const fs = require('fs').promises
const server = http.createServer(async (req, res) => {
try{
res.writeHead(200, { 'content-Type': 'text/html; charset=utf-8' });
const data = await fs.readFile('./server2.html');
res.end(data)
} catch (error) {
console.error(error);
res.writeHead(200, { 'content-Type': 'text/plain; charset=utf-8'});
res.end(error.message);
}
})
.listen(8080);
server.on('listening', () => {
console.log('8080번 포트에서 대기 중 입니다.')
});
server.on('error', (error) => {
console.error(error);
})
- fs 를 불러와서 data라는 변수에 파일을 읽고 담았다.
- 읽은 데이터를 바로 res.end메서드에 담아서 통째로 전송하도록 하였다.
2. REST API 서버 만들기
* REST API
- 서버에 요청을 보낼 때는 주소를 통해 요청의 내용을 전달한다. 이 때 서버가 이해하기 쉬운 주소를 지정하는 방법이 REST API(Representaltional State Transfer) 이다. /user 이면 사용자 정보와 관련되어 있으며, /pos 라면 게시글과 관련되어 있을 것이다..
- 요청을 보낼 때에는 HTTP 요청 메서드를 사용한다. 이는 GET, POST, PUT, PATCH, DELETE가 있다.
- REST API를 사용한 주소 체계를 이용하는 서버를 RESTful 하다고 하는데, GET /user 는 사용자를 조회한다거나 POST /post 는 게시글을 등록하는 등의 역할을 하도록 지정하곤 한다.
* 서버 만들기
- 이번에는 위에서 만든 서버보다 조금 더 복잡한 구조로 만들어 보자.
- html 문서는 about.html과 restFront.html, test.html 로 구성되어 있다. 각각은 nav태그 내에 a태그로 각각에 접속가능하도록 해두었다. 메인 페이지로 사용할 restFront.html만 먼저 살펴보면 다음과 같다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/test">Test</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="./restFront.js"></script>
</body>
</html>
- js 파일에 axios 를 통해 post('/user/' , { name }) 요청으로 사용자 이름을 등록하고, get('/users') 요청으로 사용자이름을 불러오도록 해두었다. 삭제버튼과 수정버튼도 구현하여 클릭 시 put('/user/' + key, {name}) 또는 delete('/user/' + key) 요청을 보내도록 하였다.
// restServer.js
const http = require('http');
const fs = require('fs').promises;
const users = {};
- 위와 같이 데이터를 저장할 객체를 먼저 선언하였다. (키는 Date.now(), 값은 등록된 유저이름)
* GET
- get요청 부분부터 살펴보자.
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 === '/test') {
const data = await fs.readFile('./test.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));
}
try {
const data = await fs.readFile(`.${req.url}`);
return res.end(data);
} catch (err) {
// 주소에 해당하는 라우트를 못 찾음. 404 에러
}
} else if (req.method === 'POST') {
// ...
}
// ...
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번 포트에서 서버 대기 중입니다');
});
- req.method 를 통해 어떤 http 메서드 요청을 받았는지를 판단한다. GET 에서도 조건문을 통해서 하나하나 나누었다. 링크를 통해 접속하는 것 자체가 GET요청이므로 링크에 대한 조건부터 처리하였다.
- /, /about, /test 는 각각에 맞는 html파일을 읽고 전송시키도록 하였다. return으로 깔끔하게 종료시켜주는 것이 좋다.
- /users에 대한 요청이 왔을 때에는 저장된 유저 객체를 json파일로 전송시켜주었다.
- 이를 통해 링크를 클릭하여 다른 html파일을 보여줄 수 있게 되었고, 유저정보를 보기위해 요청하면 객체를 받을 수 있게 되었다.
* POST
- 이번엔 유저를 등록하는 POST요청을 보자.
http.createServer(async (req, res) => {
try {
if (req.method === 'GET') {
// GET 요청처리
} 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') {
// PUT 요청 처리
} else if (req.method === 'DELETE') {
// DELETE 요청 처리
}
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번 포트에서 서버 대기 중입니다');
});
- POST요청은 (/user, data) 형식으로 받게 된다. 이 때 데이터는 stream 형식으로 받게되므로 'data' 이벤트 리스너를 통해 문자열을 완성시킬 수 있도록 하였다. 그리고 'end' 이벤트 리스너를 통해 데이터를 완전히 받게 되면 객체로 만들어서 이름을 받았다.
- 미리 만들어둔 저장용 객체에 추가하고 성공했다는 응답을 보낸다.
* PUT
- 이번엔 수정을 위한 PUT 요청을 보자.
- js파일에서 서버로 put('/user/' + key, { name }) 으로 요청을 했다. 키값을 링크형태로 추가했고, 새롭게 변경될 이름을 객체로 보냈다.
const http = require('http');
const fs = require('fs').promises;
const users = {}; // 데이터 저장용
http.createServer(async (req, res) => {
try {
if (req.method === 'GET') {
// GET 요청 처리
} else if (req.method === 'POST') {
// POST 요청처리
} 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') {
// DELETE 요청 처리
}
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번 포트에서 서버 대기 중입니다');
});
- url이 /user/ 로 시작하는지를 체크하여 조건을 걸어두었다. split으로 나눈 후 키값만 추출한다.
- data는 POST에서 했던 방식으로 받았다.
- end 이벤트리스너로 데이터를 다 받았을 때, 응답을 하도록 하였다. 먼저 내부에 저장했던 객체를 수정하였고, 성공메세지를 보냈다. 성공이냐 실패냐만 보내는 것에 유의하자.
* DELETE
- 마지막으로 DELETE요청을 살펴보자. 이는 PUT과 거의 비슷하다.
const http = require('http');
const fs = require('fs').promises;
const users = {}; // 데이터 저장용
http.createServer(async (req, res) => {
try {
if (req.method === 'GET') {
// GET 요청 처리
} else if (req.method === 'POST') {
// POST 요청 처리
} else if (req.method === 'PUT') {
// PUT 요청 처리
} 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번 포트에서 서버 대기 중입니다');
});
- PUT 요청때와 마찬가지로 키값을 받았다. 객체에서 그 키를 가진 부분을 삭제하고, 성공메세지를 응답으로 보낸다.
- 지금까지의 요청 조건에 걸리지 못한 요청은 404를 헤드에 작성하고 NOT FOUND를 응답으로 보냈다.
- 중간에 에러가 발생하면 서버측 오류이므로 500 에러를 응답으로 보낸다.
참고
'Server > Node.js' 카테고리의 다른 글
<Node> M1 Mac에서 Rosetta로 노드 패키지 설치하기 (0) | 2022.06.14 |
---|---|
<노드JS> 노드 내장 모듈 (0) | 2021.07.16 |
<노드JS> 노드 내장 객체 (0) | 2021.07.16 |
<노드JS> 노드 JS란 (0) | 2021.07.15 |