Node.js & MongoDB

Node.js에서 HTTP 클라이언트와 HTTP 서버를 구현

정적 파일 제공

정적 파일을 제공하려면 우선 HTTP 서버를 시작하고 포트를 수신한다. 요청 핸들러에서 fs 모듈을 통해 파일을 지역적으로 열고 응답에 파일 내용을 쓴다.

다음은 정적 파일 서버의 기본 구현을 보여준다.

var fs = require('fs');
var http = require('http');
var url = require('url');
var ROOT_DIR = "html/";
http.createServer(function (req, res) {
  var urlObj = url.parse(req.url, true, false);
  fs.readFile(ROOT_DIR + urlObj.pathname, function (err,data) {
    if (err) {
      res.writeHead(404);
      res.end(JSON.stringify(err));
      return;
    }
    res.writeHead(200);
    res.end(data);
  });
}).listen(8080);

정적 파일은 fs.readFile()을 사용해 열기와 읽기가 가능하다. readFile() 콜백에서는 res.end(data)를 사용하여 파일 내용을 응답 개체에 쓴다.

다음은 서버에 GET 요청을 보내고 파일 내용을 가져오는 기본 HTTP 클라이언트의 구현을 보여준다.

var http = require('http');
var options = {
    hostname: 'localhost',
    port: '8080',
    path: '/hello.html'
  };
function handleResponse(response) {
  var serverData = '';
  response.on('data', function (chunk) {
    serverData += chunk;
  });
  response.on('end', function () {
    console.log(serverData);
  });
}
http.request(options, function(response){
  handleResponse(response);
}).end();

GET 요청이 완료되면 콜백 함수는 on('data') 핸들러를 사용해 서버로부터 받은 응답 내용을 읽고, on('end') 핸들러에서 파일 내용을 콘솔에 출력한다.

동적 GET 서버 구현

동적으로 GET 요청을 처리하려면 클라이언트의 요청에 대한 동적 데이터 구성에 필요한 요청 핸들러를 구현하고 응답 데이터를 작성한다. 다음으로 end() 함수를 호출해 요청을 마무리하고 Writable 스트림을 비운다.

다음은 간단한 HTTP 파일을 동적으로 생성해 응답하는 기본적 형태의 동적 웹 서버의 구현 내용을 보여준다.

var http = require('http');
var messages = [
  'Hello World',
  'From a basic Node.js server',
  'Take Luck'];
http.createServer(function (req, res) {
  res.setHeader("Content-Type", "text/html");
  res.writeHead(200);
  res.write('<html><head><title>Simple HTTP Server</title></head>');
  res.write('<body>');
  for (var idx in  messages){
    res.write('\n<h1>' + messages[idx] + '</h1>');
  }
  res.end('\n</body></html>');
}).listen(8080);

CreateServer()를 사용해 서버를 생성하고 요청 이벤트 핸들러에서는 for 구문을 사용해 매 반복 구문마다 write() 호출을 통해 클라이언트에 보낼 응답을 스트림하고 end() 함수를 호출해 응답을 마무리한다.

다음은 위에서 작성한 서버의 응답을 읽기 위한 기본 HTTP 클라언트 구현 내용이다.

var options = {
  hostname: 'localhost',
  port: '8080',
};
function handleResponse(response) {
  var serverData = '';
  response.on('data', function (chunk) {
    serverData += chunk;
  });
  response.on('end', function () {
    console.log("Response Status:", response.statusCode);
    console.log("Response Headers:", response.headers);
    console.log(serverData);
  });
}
http.request(options, function(response){
  handleResponse(response);
}).end();

POST 서버 구현

POST 요청을 처리하려면 POST 본문 내용을 읽고 내용을 처리하는 요청 핸들러 코드를 구현해야 한다. 데이터 처리를 마치면 클라이언트에 보낼 데이터를 동적으로 만들어 응답으로 작성한 후 call()을 호출해 응답을 마무리하고 Writable 스트림을 비운다.

아래는 POST 요청을 처리하는 기본 HTTP 서버 구현 모습이다.

var http = require('http');
http.createServer(function (req, res) {
  var jsonData = "";
  req.on('data', function (chunk) {
    jsonData += chunk;
  });
  req.on('end', function () {
    var reqObj = JSON.parse(jsonData);
    var resObj = {
      message: "Hello " + reqObj.name,
      question: "Are you a good " + reqObj.occupation + "?"
    };
    res.writeHead(200);
    res.end(JSON.stringify(resObj));
  });
}).listen(8080);

그리고 아래는 POST 방식으로 JSON 데이터를 서버에 보내고 JSON 응답을 처리하는 HTTP 클라이언트의 기본 구현 모습이다.

var http = require('http');
var options = {
  host: '127.0.0.1',
  path: '/',
  port: '8080',
  method: 'POST'
};
function readJSONResponse(response) {
  var responseData = '';
  response.on('data', function (chunk) {
    responseData += chunk;
  });
  response.on('end', function () {
    var dataObj = JSON.parse(responseData);
    console.log("Raw Response: " +responseData);
    console.log("Message: " + dataObj.message);
    console.log("Question: " + dataObj.question);
  });
}
var req = http.request(options, readJSONResponse);
req.write('{"name":"Bilbo", "occupation":"Burglar"}');
req.end();

먼저 클라이언트에서 JSON 문자열이 요청 스트림에 기록되고 end() 호출로 요청이 종료되면 웹서버는 클라이언트에서 name과 occupation 속성을 가진 JSON 문자열 받는다.

var req = http.request(options, readJSONResponse);
req.write('{"name":"Bilbo", "occupation":"Burglar"}');
req.end();

요청 스트림에서 데이터를 읽은 후

http.createServer(function (req, res) {
  ...
  req.on('data', function (chunk) {
    jsonData += chunk;
  });

이벤트 핸들러에서 데이터를 객체로 변환하고 message와 question 속성을 가진 새로운 객체를 만든다.

var reqObj = JSON.parse(jsonData);
var resObj = {
  message: "Hello " + reqObj.name,
  question: "Are you a good " + reqObj.occupation + "?"
};

그리고 새로운 객체를 JSON 문자열로 변환하여 응답개체에 쓴다.

res.writeHead(200);
res.end(JSON.stringify(resObj));

서버에서 응답을 받으면 on('data') 핸들러가 JSON 응답을 읽고

function readJSONResponse(response) {
  ...
  response.on('data', function (chunk) {
    responseData += chunk;
  });

on('end') 핸들러가 응답을 JSON 객체로 변환하고 원시 응답과 메시지, 질문 형태로 출력한다.

function readJSONResponse(response) {
  ...
  response.on('end', function () {
    var dataObj = JSON.parse(responseData);
    console.log("Raw Response: " +responseData);
    console.log("Message: " + dataObj.message);
    console.log("Question: " + dataObj.question);
  });