LHJ

I'm a FE developer.

20.10 웹 서버

04 Jun 2020 » js_lj

20.10 웹 서버

노드는 현재 다양한 애플리케이션에 사용되지만, 원래 목적은 웹 서버를 만드는 것이었으니, 이 장에서 웹 서버에 관한 얘기를 빼놓을 수는 없습니다.

아파치나 IIS, 기타 웹 서버를 설정해 보신 분은 알겠지만, 웹 서버 설정은 생각보다 어렵지 않습니다.
http 모듈(보안 연결을 제공하는 https 모듈 역시)에는 기본적인 웹 서버를 만드는 createServer 메서드가 있습니다.
들어오는 요청을 처리할 콜백 함수만 만들면 됩니다.
서버를 시작할 때는 listen 메서드를 호출하면서 포트를 지정합니다.

const http = require('http');

const server = http.createServer(function(req, res) {
    console.log(`${req.method} ${req.url}`);
    res.end('Hello world');
});

const port = 8080;
server.listen(port, function() {
    // 서버가 시작됐을 때 호출될 콜백을 넘길 수 있습니다.
    console.log(`server started on port ${port}`);
})

NOTE_
대부분의 운영체제에서는 보안상의 이유로 일반 사용자가 기본 HTTP 포트(80 포트)를 사용할 수 없게 막습니다.
사실 1024 미만의 포트를 사용하려면 항상 관리자 권한이 필요하다고 봐도 됩니다.
물론 이런 제약이 문제가 되지는 않습니다.
sudo 명령을 사용할 수 있으면 관리자 권한으로 서버를 실행하고 80 포트를 사용할 수 있습니다.
개발과 테스트 목적으로는 보통 1024 이상의 포트를 사용하곤 합니다.
기억하기 쉬운 3000, 8000, 3030, 8080 등의 포트가 널리 쓰이는 편입니다.

이 프로그램을 실행하고 브라우저에서 http://localhost:8080에 방문하면 Hello world!가 보입니다.
다시 터미널을 보면 모든 요청이 기록되어 있습니다.
요청은 메서드(여기서는 GET. ‘동사(verb)’라고 부르기도 합니다)와 URL 경로로 구성됩니다.
브라우저에서 그 URL에 방문할 때마다 요청 두 개가 기록되는 것을 보고 좀 의아할 수도 있습니다.

GET /
GET /favicon.ico

대부분의 브라우저는 요청을 보낼 때 URL 막대 또는 탭에 표시할 아이콘인 파비콘(favicon)을 요청합니다.

노드 웹 서버의 핵심은 들어오는 요청에 모두 응답하는 콜백 함수입니다.
이 함수는 매개변수로 IncomingMessage 객체(보통 req이라 합니다)ServerResponse 객체(보통 res라 합니다)를 받습니다.

  • IncomingMessage 객체에는 요청받은 URL, 보낸 헤더, 바디에 들어있던 데이터 등 HTTP 요청에 관한 모든 정보가 들어 있습니다.
  • ServerResponse 객체에는 클라이언트(보통 브라우저)에 보낼 응답을 컨트롤하는 프로퍼티와 메서드가 들어 있습니다.

ServerResponse 객체는 쓰기 스트림 인터페이스이며 이를 통해 데이터를 클라이언트에 보냅니다.
ServerResponse 객체가 쓰기 스트림이므로 파일을 보내기도 쉽습니다.
파일 읽기 스트림을 만들어 HTTP 응답에 파이프로 연결하기만 하면 됩니다.
예를 들어 웹사이트에서 favicon.ico 파일을 사용한다면 파비콘 요청을 감지하고 파일을 바로 보낼 수 있습니다.

const http = require('http');

const server = http.createServer(function(req, res) {
    if (req.method === 'GET' && req.url === '/favicon.ico') {
        const fs = require('fs');
        fs.createReadStream('favicon.ico');
        fs.pipe(res);       // end 대신 사용할 수 있습니다.
    } else {
        console.log(`${req.method} ${req.url}`);
        res.end('Hello world');
    }
})

const port = 8080;
server.listen(port, function() {
    // 서버가 시작됐을 때 호출될 콜백을 넘길 수 있습니다.
    console.log(`server started on port ${port}`);
})

(위와 같이 했더니 이런 오류가 뜬다..)

(그래서 아래와 같이 수정했더니..)

const http = require('http');

const server = http.createServer(function(req, res) {
    if (req.method === 'GET' && req.url === '/favicon.ico') {
        const fs = require('fs');
        const rs = fs.createReadStream('favicon.ico');
        rs.pipe(res);       // end 대신 사용할 수 있습니다.
    } else {
        console.log(`${req.method} ${req.url}`);
        res.end('Hello world');
    }
})

const port = 8080;
server.listen(port, function() {
    // 서버가 시작됐을 때 호출될 콜백을 넘길 수 있습니다.
    console.log(`server started on port ${port}`);
})

(이렇게 뜬다)

이 서버는 최소한의 기능만 만들었고 그다지 흥미로운 부분은 없지만, 이 모델을 확장해 들어오는 요청에 응답하는 웹사이트를 만들 수 있습니다.

노드로 웹사이트를 만들고 싶다면 익스프레스(http://expressjs.com/)Koa(http://koajs.com/) 같은 프레임워크가 도움이 됩니다.
이런 프레임워크를 사용하면 웹 서버를 처음부터 만드는 부담을 줄일 수 있습니다.

Koa는 널리 쓰이는 익스프레스 프레임워크를 계승한 것 같은 형태를 취하는데, 두 프레임워크 모두 TJ 할로웨이 척(Holowaychuk)이 만들었으므로 어쩌면 당연한 일입니다.
익스프레스에 이미 익숙하다면 Koa도 쉽게 사용할 수 있습니다.
Koa가 익스프레스에 비해 좀 더 ES6를 중심으로 설계되었을 뿐, 큰 차이는 없습니다.