Mini Node Server
- -
들어가기 앞서 참고(트랜젝션 해부)
HTTP 트랜잭션 해부 | Node.js
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
nodejs.org
Node.js가 제공하고 있는 HTTP 트랜잭션 해부(Anatomy of an HTTP Transaction) 공식 가이드 문서를 통해 Mini-Node Server를 완성하는 데 도움을 받을 수 있습니다!
과제 실행에 앞서 깃허브 포크 받고 클론하는 과정은 생략.
서버 실행
node server/basic-server.js
위와 같이 노드 서버를 실행했을때 코드를 수정하고 저장했다면 프로그램을 매번 서버를 Ctrl+C를 눌러 종료했다가 npm start로 다시 시작해야 한다.
어지간히 불편한 일이다. 이러한 번거로움을 없애고자 서버를 매번 실행시키지 않아도 되는 개발도구가 있다.
nodemon을 이용하는 방법이다.
먼저 CLI(command-line interface)에 npm intall nodemon을 입력하여 설치한다.
npm install nodemon
그리고 클론받은 파일의 폴더안에 pakage.json의 "scripts"에 아래 코드를 추가한다.
"start": "nodemon server/basic-server.js"

위 방식으로 nodemon을 설치하고 스크립트에 추가하였다면 CLI에서 바로 입력하면 저장 후 매번 새로 start하지 않아도 적용이 된다.
node server/basic-server.js

만약 위 방법처럼 nodemon 을 따로 설치하지 않고 실행하고자 한다면 터미널에서 바로 실행하면 된다.
npx nodemon server/basic-server.js //npx nodemon으로 server/basic-server.js 파일을 실행합니다.
클라이언트 실행
클론받은 폴더에서 client/index.html를 웹 브라우저에서 실행하면 기본적으로 작성된 버튼과 입력란을 볼 수 있다.


특정 포트로 클라이언트를 실행하고 싶다면, serve를 이용할 수 있습니다.
npx serve -l 포트번호 client/
Bare minimum requirements
이번 과제에서 구현하는 웹 서버의 기능은 매우 단순합니다. 클라이언트의 액션(버튼 클릭)에 따라 각기 다른 HTTP 요청을 서버로 보내고, HTTP 요청에 담아 보낸 단어를 소문자 또는 대문자로 응답을 받아 화면에 보여 줍니다.

과제의 조건
- POST에 문자열을 담아 요청을 보낼 때는 HTTP 메시지의 body(payload)를 이용합니다.
- 서버는 요청에 따른 적절한 응답을 클라이언트로 보내야 합니다.
- CORS 관련 헤더를 OPTIONS 응답에 적용해야 합니다.
- 클라이언트의 preflight request에 대한 응답을 돌려줘야 합니다.
- preflight request에 대한 응답 헤더는 이미 작성되어 있습니다.
클론받은 파일의 basick-server.js 를 열어보면 이러한 상태다.
const http = require('http');
const PORT = 4999;
const ip = 'localhost';
const server = http.createServer((request, response) => {
console.log(
`http request method is ${request.method}, url is ${request.url}`
);
response.writeHead(200, defaultCorsHeader);
response.end('hello mini-server sprints');
});
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
const defaultCorsHeader = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Accept',
'Access-Control-Max-Age': 10
};
특정 도메인에 대한 cors 허용된 부분
const defaultCorsHeader = {
// 이 부분에 특정 origin을 작성해준다.
"Access-Control-Allow-Origin": "http://localhost:5000",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Accept",
"Access-Control-Max-Age": 10,
};
서버 부분에 요청에 대한 기능 구현하기 위해 작성되어야 하는 방식은 이러하다.
if(메소드가 post이고, url이 /upper이면){
대문자로 응답
}else if(메소드가 post이고, url이 /lower이면){
소문자로 응답
}else{
나머지는 에러로 처리 -> bad request
}
위 형식을 지켜서 작성해보면 아래와 같다.
해결 코드
const http = require('http');
const PORT = 4999;
const ip = 'localhost';
// "preflighted" request는 "simple requests” 와는 달리,
// 먼저 OPTIONS 메서드를 통해 다른 도메인의 리소스로 HTTP 요청을 보내
// 실제 요청이 전송하기에 안전한지 확인한다.
const server = http.createServer((request, response) => {
// 메소드가 options, CORS 설정(defaultCorsHeader)을 돌려줘야 한다.
// CORS (body가 필요없기 때문에 headers만 넘어온다)
if(request.method === "OPTIONS"){
response.writeHead(200, defaultCorsHeader);
//writeHead메소드를 이용해 헤더 데이터를 전송한다.(https://nodejs.org/api/http.html#responsewriteheadstatuscode-statusmessage-headers)
response.end(); //end메소드는 모든 응답 헤더와 본문이 전송되었음을 서버에 알린다
}
// 메소드가 POST고 URL이 /upper 이면 대문자 응답을 돌려줘야 한다.
// "on"이라는 메소드는 지정된 이벤트(유저의 버튼 클릭이나 네트워크에 리소스를 요청하는 것 등)
// 처리를 통합하는 것으로, 첫번째 인수에 이벤트 이름을, 두번째 인수에 통합 처리(함수)를 각각 지정한다.
// request.on('data', ) 부분은 request 요청에서 데이터 도착시라고 생각
if(request.method === 'POST' && request.url === "/upper"){
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => { //.on('end',) request 요청에서 데이터가 다 전달되었으면
body = Buffer.concat(body).toString().toUpperCase(); //바로 문자열로 바꾼부분에 대문자로 바꿔주었다.
response.writeHead(200, defaultCorsHeader);
response.end(body);
})
}
// 메소드가 POST, URL이 /lower 이면 소문자로 응답을 돌려줌
else if(request.method === "POST" && request.url === "/lower"){
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString().toLowerCase();
response.writeHead(200, defaultCorsHeader);
response.end(body);
})
}
// 그외는 에러처리, bad request
else{
response.statusCode = 404;
response.end();
}
// 이 부분은 위에서 작성된것이기에 이곳에서 반복되면 오류가 난다. 위에서 작성되니 삭제해야함
// console.log(
// `http request method is ${request.method}, url is ${request.url}`
// );
// response.writeHead(200, defaultCorsHeader);
// response.end('hello mini-server sprints');
});
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
const defaultCorsHeader = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Accept',
'Access-Control-Max-Age': 10
};
위 작성된 코드를 조금 더 보기 좋게 정리하면 아래와 같이 정리된다. 단지 중간에 반복되는 if문을 묶어준것.
const http = require('http');
const PORT = 4999;
const ip = 'localhost';
const server = http.createServer((req, res) => {
if (req.method === 'OPTIONS') {
res.writeHead(200, defaultCorsHeader);
res.end();
}
if (req.method === 'POST') {
let body = [];
req.on('data', chunk => body.push(chunk)).on('end', () => {
body = Buffer.concat(body).toString();
if (req.url === '/upper') body = body.toUpperCase();
if (req.url === '/lower') body = body.toLowerCase();
res.writeHead(200, defaultCorsHeader);
res.end(body);
});
} else {
res.statusCode = 404;
res.end();
}
});
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
const defaultCorsHeader = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Accept',
'Access-Control-Max-Age': 10
};
소중한 공감 감사합니다