Dev/SW Engineering

Server&Node Sprint - Node.js를 이용한 서버 구축(HTTP 트랜잭션 해부)

HJChung 2020. 7. 21. 12:56

지금까지의 내용을 정리하면, "Client는 요청하는 주체 - API가 이 요청/응답을  정리해서 상호작용 매개, 이때 상호작용은 HTTP라는 규약에 맞게! - Server는 응답하는 주체이다. Client가 Server로부터 요청에 대한 응답(데이터같은..)을 받아서 이를 보여줄때 JavaScript를 사용한 비동기 통신으로 데이터를 주고받는 기술인 AJAX가 있으며, 이때의 Server는 Javascript로 프로그래밍을 하면, 컴퓨터가 알아들을 수 있도록 해주고,  비동기적으로  작동하는 Node.js를 사용해서 구축한다. "

입니다. 

 

node.js를 사용하려고 설치를 하면 이와 함께 번들링 되어있는 여러가지 모듈이 있으므로 require문법을 이용해서 불러서 사용할 수 있습니다. 이 중 대표적인 모듈이 바로 fs(file을 읽고, 쓰고, 지우고 등의 역할을 할 수 있는 file system), http, url, path등이 있습니다.

 

제가 하려고 하는 것은 node.js를 이용하여 웹서버를 구축하는 것이므로 서버를 만드는 모듈인 http를 불러와서 사용하는 것으로 시작합니다. 

1. 서버 생성

서버를 만드는 모듈인 http를 불러와서, http.createServer 로 생성된 서버는 모든 incoming requests를 처리할 것 입니다.

basic-server.js

const http = require('http'); // 서버를 만드는 모듈 불러옴

http.createServer((request, response) => { // 서버 만드는 메소드
  basic-server.js 즉, 서버가 구동되면 할 일 //예) console.log('server start!');
}).listen(포트번호, ip);

기본적으로 위와 같은 형태로 작성이 됩니다. 

  • 포트번호와 ip가 필요합니다. 모든 서버는 요청을 받을 수 있는 포트 번호를 필요로하기 때문입니다.
  • 그리고 저는 '서버가 구동되면 할 일'을 requestHandler.js에 모두 작성하기로 했으므로, 아래와 같은 형태로 서버가 구축됩니다. 
const http = require('http'); 

const port = 3000;
const ip = "127.0.0.1";

const server = http.createServer(requestHandler);
server.listen(port, ip);

console.log("Listening on http://" + ip + ":" + port);

2.Request, Response 

HTTP 요청이 서버에 오면 node가 트랜잭션을 다루려고 request와 response객체를 전달하며 요청 핸들러 함수를 호출한다고 합니다. 전는 이 부분을  requestHandler.js에 모두 작성하기로 하여서

createServer 메소드 콜백의 매개변수인 request response에 대해서 먼저 배운 것을 정리해보겠습니다. 

더보기

실제 어떻게

  • HTTP 요청/응답을 브라우저를 통해 확인하고 해당 내용을 읽을 수 있는지
  • HTTP 다양한 요청 방식과, 응답 코드에 대해 이해해 보도록 하는데 학습목표인데..
  • 아직 블로그에 글로 적을 만큼 이해 및 뇌 속에서 정리된 것이 아니라서.. 앞으로.. 천천히.. 업뎃을.... 할 거 에 요...

Request(HTTP 요청)는 서버로 본내는 요청에 대한 정보가 들어있습니다. 

Request은 header와 (optional)body로 구성되어 있습니다.  

출처: https://youtu.be/pHFWGN-upGM

 

Response는 클라이언트에게 할 응답입니다. 그러므로 응답이 어떤 내용, 데이터를 보내는 것일라면 response객체를 활용하고 이를 어떻게 화면에 출력되게 할까 고민하면 됩니다. Response역시 header와 (optional) body로 구성되어 있습니다. 

 

특히 status code는 현재 이 과정이 잘 완료되었지, 그게 아니라면 Client에 문제가 있는지 Server에 문제가 있는지 등을 알려주는 매우 유용한 정보입니다. 

Status code Meaning
200 요청 성공
304 요청에 대한 응답이 수정되지 않음
400 Bad request: 잘못된 문법으로 인ㄴ해 서버가 요청을 이해할 수 없음을 의미
403 Forbidden: 해당 Client가 컨텐츠에 접근할 권ㄴ리를 가지지 않을 때 발생
404 Not Found: Server가 요청받은 리소스를 찾을 수 없을때 , 알려지지 않은 url일 때요청받은 리소스를 사용할 수 없음
500 서버가 처리할 수 없는 요청

등이 있으며 

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드

번역이 완료되지 않았습니다. Please help translate this article from English HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제

developer.mozilla.org

에 여러 Status code와 각 status마다 자세한 설명이 나와있습니다. 

 

이제 진짜! basic-server.js에서 기본적인 서버를 구축했으니까  requestHandler.js에 GET, POST 요청에 대한 각각의 응답을 어떻게 처리해 줄지 정의해보도록 하겠습니다. 

1) GET 요청에 대한 응답

이전에 잘 정의된 서버에서는 앞의 <Interaction With Server>에서 GET요청을 하였을 때[ {"id":0,"username":"정현정","text":"hello","roomname":"담소","date":"2020-06-20T05:21:30.927Z"}, {"id":1,"username":"정현정2","text":"hello2","roomname":"담소","date":"2020-06-20T05:21:30.927Z"}, {"id":2,"username":"정현정3","text":"hello3","roomname":"담소","date":"2020-06-20T05:21:30.927Z"} ]이와 같은 데이터를 잘 반환해 주었습니다. 

 

제가 만든 서버도 이렇게 잘 응답을 해주려면?!

requestHandler.js

const requestHandler = function (request, response) {
  // .writeHead() 메소드의 두번째 인자로는 응답 헤더와 키와 값을 객체 형태로 적어줍니다.
  response.writeHead(200, {"Content-Type": "application/json"});

  if(request.method === 'GET'){ //request가 GET요청이고
    if(request.url === '/messages'){ //올바른 Client주소에서 온 요청이면 
      response.writeHead(200, headers); //올바른 Status code인 200
      response.end(JSON.stringify(results));//모든 messasges 즉, results를 보내는 것으로 응답해주고 
    }
    else{//올바른 Client주소에서 온 요청이 아니면 
      response.writeHead(404, headers);//404 에러 발생하고
      response.end();//응답으로 아무 데이터도 주지 않는다. 
    }
  }
};

2) POST 요청에 대한 응답

{"id":4,"username":"정현정4","text":"hello4","roomname":"담소","date":"2020-06-20T05:21:30.927Z"} 라는 새로운 자원을 생성해달라 그래서  [ {"id":0,"username":"정현정","text":"hello","roomname":"담소","date":"2020-06-20T05:21:30.927Z"}, {"id":1,"username":"정현정2","text":"hello2","roomname":"담소","date":"2020-06-20T05:21:30.927Z"}, {"id":2,"username":"정현정3","text":"hello3","roomname":"담소","date":"2020-06-20T05:21:30.927Z"}, {"id":4,"username":"정현정4","text":"hello4","roomname":"담소","date":"2020-06-20T05:21:30.927Z"} ]

이렇게 만들어 달라는 POST요청은?

requestHandler.js

const requestHandler = function (request, response) {
  response.writeHead(200, {"Content-Type": "application/json"});
  
  if(request.method === 'POST'){ 
    if(request.url === '/messages'){
      response.writeHead(201, headers);
      let message = ''; //새로운 message 객체 하나 받는다.
      request
      .on('data', (chunk) => {
        console.log('data처리 중');
        message += chunk;
      })
      .on('end',() => {//서버에 새로운 message 객체 하나 저장
        var post = JSON.parse(message);//.parse: 정보를 객체화
        results.results.push(post);
        response.end(JSON.stringify(message));
      });
    }
    else{
      response.writeHead(404, headers);
      response.end();
    }
  }

 

3) CORS

웹은 API등으로 클라이언트가 여러 서버와 통신을 합니다. 다른 여러 서버의 자원을 요청하게 되는 것입니다. 

이런 것을 cross origin으로 요청을 한다 라고 하며

CORS(Cross Origin Resourse Sharing)은 'Cross origin 에서 리소스를 요청하여 사용한다'라는 의미입니다. 

그런데 브라우저들은 보안 상의 이유로 스크립트 내에서 초기화되는 cross-origin HTTP 요청을 제한합니다. 

 

이를 아무런 제약없이 놔두면 서버에 어떤 리소스를 생성하고 가져갈지 확인할 수 없기때문이고,  그래서 서버가 허용한 범위 내에서만 cross origin요청이 가능합니다. 

그럼 서버에서 얼마나 허용할 것인지를 적어주어야합니다. 그래서! 아래처럼 허용할 것들을 다 설정해주고, 이를 header에 넣습니다. 

requestHandler.js

const defaultCorsHeaders = {
	'access-control-allow-origin': '*', //모든 도메인(*)을 허용한다. 
    'access-control-allow-methods': 'GET, POST, PUT, DELETE, OPTIONS', //메소드는 GET, POST, PUT, DELETE, OPTIONS만 허용한다. 
    'access-control-allow-headers': 'content-type, accept', //헤더에는 content-type과 accept만 쓸 수 있다. 
    'access-control-max-age': 10, //preflight request는 10초 까지 허용된다. 
 }
 
 const headrs = defaultCorsHeaders
 
 

4) Option

그래서 서버에 어떤 요청을 보내기 전에 OPTIONS라는 CORS 에 대한 정보를 확인하고 이 단계에서 cross origin 허가가 나면 그때서야 POST 요청을 보내는 순서로 되어있다. 즉, OPTIONS라는 것은 preflight request라 하고, 이는 서버에서 Allow하는 조건들을 다 맞추고 있는지 사전에 서버에 확인하는 요청입니다. 

requestHandler.js

const requestHandler = function (request, response) {
  response.writeHead(200, {"Content-Type": "application/json"});
  
  if(request.method === 'OPTIONS'){ 
      response.writeHead(200, headers);
      response.end();
  }

OPTIONS는 POST와 같은 요청의 사전에 검사되는 것이기 때문에 지금까지의 내용을 순서에 맞게 합쳐서 코딩한다면(즉, 순서에 맞게 각 요청에 따라 다른 처리를 해주어야 하는 것을 구현한다면, )

if (request.method === 'OPTIONS') {
    response.writeHead(200, headers)
    response.end()
  } else if (request.method === 'POST') {
    if (request.url === '/messages') {
      response.writeHead(201, headers)
      let message = ''
      request
        .on('data', (chunk) => {
          message += chunk
        })
        .on('end', () => {
          var post = JSON.parse(message) 
          results.results.push(post)
          response.end(JSON.stringify(message))
        })
    } else {
      response.writeHead(404, headers)
      response.end()
    }
  } else if (request.method === 'GET') {
    if (request.url === '/messages') {
      fs.readFile('data', (err, data) => {
        if (err) {
          console.log(err)
          response.writeHead(404, headers)
        }
        response.end(JSON.stringify(data))
        response.writeHead(200, headers)
      })
    }
  } else {
    response.writeHead(404, headers)
    response.end()
  }
}

이렇게 됩니다.!

Reference

https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/

https://www.zerocho.com/category/NodeJS/post/57774a8eacbd2e9803de0195

 

(NodeJS) 서버 만들기

안녕하세요! 이번 시간에는 서버를 만들어보겠습니다! 서버를 직접 만든다니 새롭지 않나요? 다른 서버들은 서버를 다운로드받은 후 설정... 설정... 설정... 끝에야 돌릴 수 있잖아요. Node.js는 코

www.zerocho.com