...
HTTP 모듈
http 모듈은 노드의 가장 기본적인 모듈이며, http 서버와 클라이언트를 생성하는 것과 관련된 모든 기능을 담당한다.
웹 서버의 생성과 실행
//서버를 생성
var http = require('http');
var server = http.createServer();
//서버를 실행
server.listen(52273, function() { // 포트 설정
console.log('Server Running at http://127.0.0.1:52273'); // 서버실행 하면 콘솔 출력
});
//10초 후 서버 종료
setTimeout(function() {
console.log('서버 종료');
server.close();
},10000);
http 모듈을 사용하기 위해 require()메소드를 호출했고 createServer()메소드를 이용해서 서버를 생성했다.
listen()메소드를 이용해서 서버를 실행했고 close()메소드를 이용해서 서버를 종료했다.
response 객체
위의 예제에서 서버를 실행한 후 http://127.0.0.1:52273 에 접속해도 웹 브라우저에는 아무것도 출력되지 않을 것이다. 요청만 하고 응답을 하지 않았기 때문이다.
클라이언트에 웹 페이지를 제공하려면 응답 메시지를 작성해야 한다.
응답 메시지를 작성할 때는 response 객체를 생성한다.
//서버를 생성
var http = require('http');
http.createServer(function(request, response) {
response.writeHead(200, {'content-type': 'text/html; charset=utf-8'}); // 한글화 하려면 charset=utf-8 필수
response.write('<h1>Hello Node !</h1>'); // 스트림 형식으로 작성
response.end('<p>hello web server with node.js</p>');
}).listen(52273, function() {
console.log('Server Running at http://127.0.0.1:52273');
});
코드를 실행한 후 http://127.0.0.1:52273 에 접속하면 웹 브라우저에 아래와 같이 출력된다.
File System 모듈을 사용한 웹페이지 제공
그러나 자바스크립트 파일 위에서 모든 html 페이지를 문자열로 작성하는 것은 매우 비효율적이다.
그 대신 File System 모듈을 사용하여 서버에 존재하는 html 페이지를 클라이언트에 제공할 수 있다.
<!-- htmlPage.html -->
<h1>This is htmlPage</h1>
<p>Hello world! I'm Jessie</p>
// 모듈 추출
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
// 서버 생성
const server = http
.createServer(async (req, res) => {
let pathname = url.parse(req.url).pathname;
console.log(pathname);
if (pathname == '/') {
pathname = '/index.html';
}
// 파일 읽기
try {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
const data = await fs.readFile('./index.html');
res.end(data);
} catch (err) {
console.err(err);
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); // text/plain은 그냥 글자형태를 의미
res.end(err.message);
}
})
.listen(52273);
// 서버가 실행될 경우 이벤트 발생
server.on('listening', () => {
console.log('52273 포트에서 서버가 실행중 입니다.');
});
// 서버에 에러가 발생할 경우 이벤트 발생
server.on('error', err => {
console.log(err);
});
실행결과
Rest api로 서버 만들기
선행 학습
restfront.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="./restFront.css" /> <!-- 파일을 요청 한다 -->
<style>
a { color: blue; text-decoration: none; }
</style>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/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="./restFront.js"></script>
</body>
</html>
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 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 {
// put 요청을 보냄
await axios.put('/user/' + key, { name });
getUser();
} catch (err) {
console.error(err);
}
});
const remove = document.createElement('button');
remove.textContent = '삭제';
remove.addEventListener('click', async () => {
// 삭제 버튼 클릭
try {
// delete 요청을 보냄
await axios.delete('/user/' + key);
getUser();
} catch (err) {
console.error(err);
}
});
const userDiv = document.createElement('div');
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 {
// ajax요청을 /user에게 name데이터를 post로 보냄
await axios.post('/user', { name });
getUser();
} catch (err) {
console.error(err);
}
e.target.username.value = '';
});
restServer.js
const http = require('http');
const fs = require('fs').promises;
const users = {}; // 데이터 저장용
http
.createServer(async (req, res) => {
try {
// 클라이언트에서 요청을 보내면 request로 crud를 받아올수 있다.
if (req.method === 'GET') {
// 뒤쪽 url 에 따라서 브라우저에 보낼 html 웹페이지가 다르다.
console.log(`req.url : ${req.url}`);
if (req.url === '/') {
const data = await fs.readFile(__dirname + '/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(__dirname + '/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도 아니면 폴더에서 찾도록 함.
예를들어 http://localhost:8082/ 를 요청해서 restFront.html을 불러왔다고 치자
그럼 html에서 설정한 src(상대경로) 폴더,파일을 불러올 것이다.
이 요청도 역시 request에 해당하며 결국 이 분기문으로 들어오게 된다.
하지만 위의 if문에 해당되는것이 없으니 이쪽 마지막 try문으로 오게된다.
그러면 요청한 css나 js파일을 해당 경로 폴더에서 readfFile해주어 response해준다. */
// 결국 서버에서 클라우드에 필요한 파일을 직접 업로드를 해줘야 한다는 말이다.
// 이때까지 웹서버 소프트웨어로 자동으로 res,req를 했다고하면, 노드는 직접 서버를 구축해서
// 하는 것이기 때문에, 일일히 하나하나 다 작업을 해줘야 한다.
try {
console.log(`${__dirname}/${req.url}에서 파일을 불러온다.`);
const data = await fs.readFile(`${__dirname}/${req.url}`);
return res.end(data);
} catch (err) {
// 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
}
// restFront.html에서 axios.post('/user', { name }) 요청을 보낼때 발동
} 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); // { name } 형식으로 보냈으니 구조분해
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번 포트에서 서버 대기 중입니다');
});
Reference
인용한 부분에 있어 만일 누락된 출처가 있다면 반드시 알려주시면 감사하겠습니다
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.