🤔 HTTP 요청/응답과 Node.js의 http 모듈에 대해 정리해봅시다.
Chrome 개발자도구의 Network 탭을 사용해 HTTP 요청/응답을 확인할 수 있다.
HTTP 메시지의 는 start-line, HTTP headers, body로 구성되어 있으며, 헤드와 바디 사이에는 한 줄의 공백(empty line)으로 구분되어 있다.
시작 줄(start-line)에는 실행되어야 할 요청, 또은 요청 수행에 대한 성공 또는 실패가 기록되어 있다.
옵션으로 HTTP 헤더 세트가 들어간다. 여기에는 요청에 대한 설명, 혹은 메시지 본문에 대한 설명이 들어간다.
요청에 대한 모든 메타 정보가 전송되었음을 알리는 빈 줄(blank line)이 삽입된다.
요청과 관련된 내용(HTML 폼 콘텐츠 등)이 옵션으로 들어가거나, 응답과 관련된 문서(document)가 들어간다. 본문의 존재 유무 및 크기는 start-line과 HTTP 헤더에 명시된다.
GET, POST, PUT, DELETE, OPTIONS, HEAD, TRACE
서버는 클라이언트의 요청에 따라 적절한 HTTP 상태 코드(HTTP Status Code)를 응답 정보로 설정해서 보내준다. 우리는 상태코드를 확인해서 추가적인 로직을 구현할 수 있다.
주요 상태코드는 200번대부터 500번대까지 다양하지만, 그 중에서도 중요한 코드들을 아래에 정리해보았다.
200번대의 상태코드는 대부분 요청이 성공했음을 의미한다. (서버에서 별도로 설정하지 않으면 응답의 HTTP 상태코드는 항상 200이다.)
300번대의 상태 코드는 대부분 클라이언트가 이전 주소로 데이터를 요청하여 서버에서 새 URL로 리다이렉트를 유도하는 경우에 해당한다.
400번대 상태 코드는 대부분 클라이언트의 코드가 잘못된 경우이다. 유효하지 않은 자원을 요청했거나 요청이나 권한이 잘못된 경우 발생한다.
500번대 상태 코드는 서버 쪽에서 오류가 난 경우이다.
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.statusCode
와 response.setHeader('key', 'value')
를 한번에 작성할 수도 있다.response.write('body내용')
, response.end()
: 응답 바디 전송.response.end('body 내용')
: end
함수에 스트림에 보낼 데이터의 마지막 비트를 선택적으로 전달할 수 있으므로 write
함수를 사용하지 않고 end
함수만으로 한번에 작성할 수도 있다.URL이나 request 객체의 데이터에 기반을 둬서 라우팅을 할 수 있습니다.
아래와 같이 원하는대로 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);
클라이언트로부터 받은 요청에 따라 서버에서 요청에 맞는 작업을 실행할 수 있게 나누어주는 것.
리스너 함수의 바디에서 조건문을 통해 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);