210315~19_TWIL : HTTP 프로토콜과 Node.js의 http 모듈

seungyeon·2021년 3월 19일
6

TIL

목록 보기
38/64

IM: DAY 22 ~ 26

🤔 HTTP 요청/응답과 Node.js의 http 모듈에 대해 정리해봅시다.


Q. HTTP 요청/응답을 브라우저를 통해 확인할 수 있고, 해당 내용을 읽을 수 있나요?

  • Chrome 개발자도구의 Network 탭을 사용해 HTTP 요청/응답을 확인할 수 있다.

  • HTTP 메시지의 는 start-line, HTTP headers, body로 구성되어 있으며, 헤드와 바디 사이에는 한 줄의 공백(empty line)으로 구분되어 있다.

  • 시작 줄(start-line)에는 실행되어야 할 요청, 또은 요청 수행에 대한 성공 또는 실패가 기록되어 있다.

  • 옵션으로 HTTP 헤더 세트가 들어간다. 여기에는 요청에 대한 설명, 혹은 메시지 본문에 대한 설명이 들어간다.

  • 요청에 대한 모든 메타 정보가 전송되었음을 알리는 빈 줄(blank line)이 삽입된다.

  • 요청과 관련된 내용(HTML 폼 콘텐츠 등)이 옵션으로 들어가거나, 응답과 관련된 문서(document)가 들어간다. 본문의 존재 유무 및 크기는 start-line과 HTTP 헤더에 명시된다.

Q. HTTP 다양한 요청 방식과 응답 코드에 대해 설명해보세요.

HTTP 요청 방식(HTTP 메서드 종류)

GET, POST, PUT, DELETE, OPTIONS, HEAD, TRACE

  • GET: URI 형식으로 웹 서버측 리소스(데이터)를 요청할 때 사용한다. 주소창에 URL을 작성하는게 GET 요청에 해당한다.
  • POST: 데이터를 서버에 제출할 때 사용한다. form 제출같은 것이 POST 요청에 해당한다. Payload(바디)와 같이 보낸다. POST 요청이 성공적으로 처리되면 서버의 내용이 변경된다.
  • PUT: 서버에 있는 resource를 업데이트할 때 사용한다.
  • DELETE: 서버에 있는 resource를 삭제할 때 사용한다.
  • OPTIONS: 서버가 어떤 메서드를 지원하는지 알아볼 때 사용한다. 쉽게 말해서 서버에 실제 요청을 보내기 전에 서버를 테스트해보는 용도라고 보면 된다.
  • PATCH: resource의 일부만 수정할 때 사용한다.
  • HEAD: HTTP 헤더(문서 정보)를 가져올 때 사용한다. GET과 유사한 방식이지만, 실제 문서를 요청하는 것이 아니라, 문서 정보를 요청하는 것이다. 따라서 서버는 HEAD 요청을 받으면 HTTP 응답 메세지에 본문(Body)없이 HTTP 헤더 정보만을 보낸다.
  • TRACE: 서버에 Loopback(루프백) 메시지를 호출하기 위해 사용한다.
  • CONNECT: 양방향 통신을 할 때 사용한다.

HTTP 응답 상태 코드

서버는 클라이언트의 요청에 따라 적절한 HTTP 상태 코드(HTTP Status Code)를 응답 정보로 설정해서 보내준다. 우리는 상태코드를 확인해서 추가적인 로직을 구현할 수 있다.
주요 상태코드는 200번대부터 500번대까지 다양하지만, 그 중에서도 중요한 코드들을 아래에 정리해보았다.

2XX : 요청 성공

200번대의 상태코드는 대부분 요청이 성공했음을 의미한다. (서버에서 별도로 설정하지 않으면 응답의 HTTP 상태코드는 항상 200이다.)

  • 200: GET 요청 성공
  • 204: No Content. 성공했으나 응답 본문에 데이터가 없음
  • 205: Reset Content. 성공했으나 클라이언트의 화면을 새로 고침하도록 권고
  • 206: Partial Conent. 성공했으나 일부 범위의 데이터만 반환

3XX : 리다이렉트

300번대의 상태 코드는 대부분 클라이언트가 이전 주소로 데이터를 요청하여 서버에서 새 URL로 리다이렉트를 유도하는 경우에 해당한다.

  • 301 : Moved Permanently, 요청한 자원이 새 URL에 존재
  • 303 : See Other, 요청한 자원이 임시 주소에 존재
  • 304 : Not Modified, 요청한 자원이 변경되지 않았으므로 클라이언트에서 캐싱된 자원을 사용하도록 권고. ETag와 같은 정보를 활용하여 변경 여부를 확인

4XX : 클라이언트 에러

400번대 상태 코드는 대부분 클라이언트의 코드가 잘못된 경우이다. 유효하지 않은 자원을 요청했거나 요청이나 권한이 잘못된 경우 발생한다.

  • 400 : Bad Request, 잘못된 요청
  • 401 : Unauthorized, 권한 없이 요청. Authorization 헤더가 잘못된 경우
  • 403 : Forbidden, 서버에서 해당 자원에 대해 접근 금지
  • 405 : Method Not Allowed, 허용되지 않은 요청 메서드
  • 409 : Conflict, 최신 자원이 아닌데 업데이트하는 경우. ex) 파일 업로드 시 버전 충돌

5XX : 서버 에러

500번대 상태 코드는 서버 쪽에서 오류가 난 경우이다.

  • 501 : Not Implemented, 요청한 동작에 대해 서버가 수행할 수 없는 경우
  • 503 : Service Unavailable, 서버가 과부하 또는 유지 보수로 내려간 경우

Q. node.js의 내장 http 모듈을 사용하는 방법

Node.js가 내장하고 있는 http 모듈을 사용하면 HTTP 서버를 만들어 node.js 파일을 웹서버로 활용할 수 있다. node.js 파일 상단에서 require() 전역함수를 사용하여 http 모듈을 로드해준다.

const http = require('http'); // http 모듈을 사용합니다.

node.js를 웹서버로 사용하려면 우선 웹서버 객체를 만들어주어야 한다. 웹서버 객체, 즉 HTTP 서버 인스턴스 생성은 http 모듈의 createServer() 함수를 사용한다.
또한, 웹서버 인스턴스의 listen() 함수는 인자로 받아오는 특정 포트에서 HTTP 서버를 시작하게 하고 사용자의 요청을 기다린다.

const http = require('http');

const server = http.createServer();
server.listen(5000);

위의 예제는 인스턴스를 생성하고 시작했을 뿐 클라이언트의 어떤 요청에도 응답하지 않는다.

HTTP 서버는 웹서버 인스턴스 생성시 함수 형태의 리스너가 등록되어야 요청처리를 할 수 있다.

const http = require('http');

const server = http.createServer((request, response) => {
  // ...
});
server.listen(5000);

리스너 함수의 바디에서 작업을 진행하면 된다. 인자로 받아오는 request와 response 객체 안의 프로퍼티를 사용해서 작업을 진행할 수 있다.
Node에서 request 객체에 헤더, URL, 메서드 데이터에 해당하는 프로퍼티를 넣어놨기 때문에 쉽게 가져올 수 있다. (구조분해할당을 사용하면 더욱 쉽게 프로퍼티에 접근할 수 있다.)

const http = require('http');

const server = http.createServer((request, response) => {
  const {headers, method, url} = request; 
});
server.listen(5000);

request 객체에서 바디 데이터를 가져오는 방법은 약간 다르다. request 객체는 ReadableStream 인터페이스를 구현하고 있다. 이 스트림의 'data'와 'end' 이벤트에 이벤트 리스너를 등록해서 데이터를 받아와야 한다.

const http = require('http');

const server = http.createServer((request, response) => {
  const {headers, method, url} = request; 
  
  let body = [];
  request.on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString(); // `body`에 전체 요청 바디가 문자열로 담겨있다.
  });
});
server.listen(5000);

각 'data'이벤트에서 발생시킨 chunk가 Buffer이다.

chunk가 문자열 데이터인 경우 위 예제와 같이 데이터들을 body 배열에 수집한 다음 'end' 이벤트에서 concat한 뒤 문자열로 변환해주는 것이 좋다.

request 객체에서 성공적으로 헤더, URL, 메서드, 바디 데이터를 가져왔다면 이제 response 객체로 헤더, HTTP 상태 코드, 바디 데이터를 보낼 차례다.
response 객체는 ServerResponse의 인스턴스이면서 WritableStream이다. response 객체의 다양한 메서드를 사용해 헤더, HTTP 상태 코드, 바디 데이터를 보낼 수 있다.

  • response.statusCode : HTTP 응답 상태 코드 설정. 따로 설정하지 않으면 자동으로 200이 된다.
  • response.setHeader('key', 'value') : 응답 헤더 설정.
  • response.writeHead(상태코드, 응답헤더객체) : response.statusCoderesponse.setHeader('key', 'value')를 한번에 작성할 수도 있다.
  • response.write('body내용') , response.end() : 응답 바디 전송.
  • response.end('body 내용') : end 함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있으므로 write함수를 사용하지 않고 end 함수만으로 한번에 작성할 수도 있다.

URL이나 request 객체의 데이터에 기반을 둬서 라우팅을 할 수 있습니다.

Q. http 모듈 사용시에 서버에 CORS를 설정하는 방법

아래와 같이 원하는대로 CORS 설정값을 만들어주고

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 설정을 response의 헤더로 보내주면 된다.

const server = http.createServer((request, response) => {
  response.writeHead(200, defaultCorsHeader);
  response.end();
}).listen(8080);

Q. 라우팅(조건에 따른 분기)에 대해 설명하고, 서버 코드에서 구현해보세요.

라우팅이란?

클라이언트로부터 받은 요청에 따라 서버에서 요청에 맞는 작업을 실행할 수 있게 나누어주는 것.

라우팅을 서버코드에서 구현하는 방법

리스너 함수의 바디에서 조건문을 통해 URL을 검사함으로써 라우팅을 할 수 있다.
아래 예제는 요청 메서드가 POST이고, URL이 /echo인 경우에서만 응답을 보내고 그 외의 경우에는 404를 응답하는 코드이다.

const http = require('http');

http.createServer((request, response) => {
  if (request.method === 'POST' && request.url === '/echo') {
    let body = [];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      response.end(body);
    });
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

0개의 댓글