이번에는 단순한 이베트 주도 방식의 채팅 어플리케이션을 만들어 보면서 노드의 실용적인 측면을 살펴보도록 하겠습니다. 이번에 소개하는 상세한 내용 때문에 머리가 아프더라도 너무 걱정할 필요는 없습니다. 목적은 노드 개발에 대한 궁금증을 풀고 노드로 무엇을 할 수 있을지를 미리 본다고 생각하면 됩니다.

웹 어플리케이션을 개발해 본 경험이 있고 HTTP에 대한 기본적인 이해가 있으며 jQuery에 친숙하다는 전제하에 글을 쓰겠습니다.

먼저 진행 내용은 이렇습니다.

○ 어플리케이션의 동작 방식을 살펴본다.

○ 기술적인 요구사항을 살펴보고 어플리케이션 초기 설정을 진행합니다.

○ 어플리케이션에서 사용할 HTML, CSS, 클라이언트 측 자바스크립트를 서비스합니다.

○ Socket.IO를 이용해 채팅과 관련된 메시지를 처리합니다.

○ 어플리케이션 UI에 사용할 자바스크립트를 작성합니다.

 

▷어플리케이션 개요

만들 어플리케이션은 여러 사용자가 대화창에 메시지를 입력해 온라인에서 채팅할 수 있는 프로그램입니다. 한 사용자가 메시지를 입력하면 다른 모든 사용자에게 전송됩니다. 어플리케이션이 시작될 때 각 사용자에게 자동으로 닉네임이 주어지지만, 특정 명령어를 이용해서 닉네임을 변경할 수 있습니다. 닉네임을 변경하기 위한 채팅 명령어는 슬래시(/)로 시작합니다. 비슷한 방식으로 새로운 채팅방을 만들거나 기존 방에 참여할 때 채팅 명령어를 사용할 수 있습니다. 채팅방을 만들거나 참여하면 어플리케이션 상단의 가로 막대 영역에 새롭게 참여한 채팅방의 이름이 표시됩니다. 또한, 대화창 오른쪽에 있는 참여할 수 있는 모든 채팅방 목록에도 현재 채팅방의 정보가 표시 됩니다.

 

이 어플리케이션은 가장 단순한 기능만 있지만, 실시간 어플리케이션을 만드는데 필요한 주요 컴포넌트와 기반 기술을 소개하고 있습니다. 특히, 노드가 어떻게 정적 파일과 같은 일반적인 HTTP 데이터와 대화 내용과 같은 실시간성 데이터를 동시에 서비스 하는지 알 수 있습니다. 또한, 노드 어플리케이션의 구성 방식과 의존성 모듈 관리 방식도 보여줍니다.

 

▷어플리케이션 요구사항과 초기 설정

채팅 어플리케이션을 위해서는 다음과 같은 작업을 수행해야 합니다.

○ 정적 파일 서비스(HTML, CSS와 클라이언트 측 자바스크립트 등)

○ 서버에서 채팅과 관련된 메시지 처리

○ 사용자 브라우저에서 채팅과 관련된 메시지 처리

 

정적 파일을 서비스 하려면 노드에 포함된 HTTP 모듈을 사용해야 합니다. 또한, HTTP를 이용해 파일을 서비스 한다면 파일 내용뿐만 아니라 전송되는 파일의 종류도 포함하여 전송해야 합니다. 이 정보는 HTTP 헤더의 Contet-Type에 설정할 수 있으며 파일의 적절한 마임(MIME) 형식을 헤더에 추가해줍니다. 마임 형식은 mime이라는 서드파티 모듈을 사용해서 둘러 볼 수 있습니다.

mime : 전자 우편을 위한 인터넷 표준 포멧입니다. HTTP와 같은 통신 프로토콜에 사용되고 있습니다. 

(자세한 내용은 위키피디아 참고 : https://ko.wikipedia.org/wiki/MIME)

 

채팅과 관련되 메시지를 처리하기 위해 ajax를 이용해 서버로부터 데이터를 가져올 수 있습니다. 하지만 어플리케이션이 가능한 빨리 반응하게 하려면 기존의 ajax 방식을 사용해 메시지를 전송하는 방법을 피해야 합니다. ajax가 전송 체계로 사용하는 HTTP는 실시간 통신을 위해 설계된 것이 아닙니다. HTTP를 이용해 메시지를 전송하면 매번 새로운 TCP/IP 연결을 사용해야 합니다. 연결을 시작하고 종료하는데 시간이 소요되며 모든 요청마다 HTTP 헤더 정보를 보내야 하므로 전송해야 할 데이터 양도 많아지게 됩니다. 그러므로 HTTP보다는 실시간 통신을 지원하기 위해 설계된 양방향 경량 통신 프로토콜인 웹소켓을 이용하는 편이 더 좋습니다.

하지만 웹소켓을 사용하지 못하는 브라우저가 있을 때 다양한 대체 기능을 제공하는 인기 라이브러리인 Socket.IO를 활용해서 어플리케이션을 만들도록 하겠습니다. Socket.IO는 추가 코드나 설정 없이 대체 기능을 투명하게 처리합니다.

 

▷HTTP와 웹소켓 서비스

본격적으로 시작하기 전에 HTTP와 웹소켓을 동시에 처리하는 방법을 조금 알아보겠습니다. 이는 노드가 실시간 어플리케이션에 적절한 선택이 될 수 있는 이유이기도 합니다. 어플리케이션에서 채팅 메시지를 주고받을 때 ajax를 사용하지 않기로 했지만, 사용자의 브라우저에 그릴 화면을 보여줄 때 필요한 HTML, CSS, 클라이언트 측의 자바스크립트를 전송하려면 여전히 HTTP를 사용해야 합니다. 노드는 하나의 TCP/IP 포트를 이용해 HTTP와 웹소켓을 동시에 쉽게 서비스합니다. 노드 내부에는 HTTP 서비스를 제공하는 모듈이 있는데 익스프레스와 같이 노드의 내장 기능을 이용해 웹 서비스를 조금 더 쉽게 할 수 있도록 도와주는 서드파티 모듈이 많이 있습니다.

 

▷어플리케이션의 파일 구조 생성

어플리케이션을 제작하기에 앞서 프로젝트 디렉토리를 생성합니다. 주요 어플리케이션 파일이 디렉토리에 바로 저장됩니다.

 

 

 

lib : 서버 측 로직을 구현한 파일을 저장

public : 클라이언트 측과 관련된 파일을 저장

 ├ javascripts

 └ stylesheets

 

구조를 이렇게 만들었지만 노드는 특정 디렉터리 구조만을 고집하지 않습니다.

사용자가 생각하는 어떤방식으로도 어플리케이션 파일을 관리할수 있습니다.

디렉터리를 모두 생성했으면 어플리케이션의 의존성 모듈을 명시하는 방법을 자세히 알아보겠습니다.

 

 

▷의존성 모듈 명시

어플리케이션 의존성 모듈이란 어플리케이션에 필요한 기능을 사용하기 위해 설치해야 하는 모듈을 말합니다. 예를들어, MySQL 데이터베이스에 데이터를 저장하는 어플리케이션을 만들었다고 가정해보겠습니다. 노드 내부에는 MySQL에 접근할 수 있는 내장 모듈이 없으므로 이를 위한 서드파티 모듈을 따로 설치해야 하는데 이를 의존성 모듈로 볼 수 있습니다. 물론 의존성 모듈을 형식에 맞게 명시하지 않더라도 노드 어플리케이션을 만들 수 있지만, 의존성 모듈을 명시하는 것은 좋은 습관입니다. 의존성 모듈을 명시함으로써 다른 사용자가 어플리케이션을 사용하거나 여러 곳에서 프로그램을 실행해야 할 때 좀더 수월하게 사용 환경을 설정할 수 있습니다.

 

어플리케이션 의존성 모듈은 package.json 파일에 명시합니다. 이 파일은 항상 어플리케이션의 최상위 디렉터리에 있어야 합니다.

package.json 파일은 CommonJS 패키지 기술어 표준을 따르는 JSON 표현식을 사용하며 어플리케이션에 대한 설명을 담고 있습니다.

 

package.json

1
2
3
4
5
6
7
8
9
{
  "name" : "chatrooms",
  "version" : "0.0.1",
  "description" : "Minimalist multiroom chat server",
  "dependencies" : {
    "socket.io" : "~0.9.6",
    "mime" : "~1.2.7"
  }
}
cs

 

이 파일의 내용이 혼란스럽더라도 걱정할 필요는 없습니다. 그냥 단순히 어플리케이션의 정보를 담고 있는 파일이라고 생각하시면 됩니다.

name : 어플리케이션의 이름입니다.

version : 어플리케이션의 버전정보를 말해주는데 첫 버전이니 0.0.1로 설정했습니다.

description : 영어만 봐도 알 수 있듯이 어플리케이션의 대한 설명입니다.

dependencies : 의존성 모듈을 나타내는데요. 이 곳에 적힌 모듈을 확인해서 필요한 모듈을 설치할 수 있습니다. 반대로 필요한 모듈을 설치 받았을 때에 자동으로 이곳에 작성되기도 합니다.

 

그럼 이제 cmd창을 열어 우리가 만든 최상위 폴더로 이동해줍니다. cmd창에서 폴더를 이동하는 명령어는 cd 명령어 입니다.

최상위 폴더로 갔다면 npm install 이라는 명령어를 실행해봅니다.

 

디렉토리 구조를 만들고 의존성 모듈을 설치했으니 이제 어플리케이션 로직에 살을 붙일 준비가 되었습니다.

 

 

▷HTML, CSS, 클라이언트 측 자바스크립트 서비스

앞서 말했듯이 3가지 기능이 필요합니다.

○ 정적 파일 서비스(HTML, CSS와 클라이언트 측 자바스크립트 등)

○ 서버에서 채팅과 관련된 메시지 처리

○ 사용자 브라우저에서 채팅과 관련된 메시지 처리

 

먼저 정적파일을 서비스하기 위한 로직을 만들어 보겠습니다. 그리고 정적 HTML과 CSS파일을 추가하겠습니다.

정적 파일 서버를 생성하려면 노드에 내장된 몇가지 기능과 함께 파일의 MIME 타입을 판단할 수 있는 서드파티 mime모듈을 이용합니다.

가장 먼저 할 일은 프로젝트 최상위 폴더에 server.js 파일을 생성합니다.

이 파일은 노드의 HTTP 관련 기능과 파일시스템 연동, 파일 경로와 관련된 기능, 그리고 MIME 타입 판단 기능을 사용할 수 있게끔 합니다.

cache 변수는 캐시 파일 데이터를 다루는데 사용합니다.

server.js

1
2
3
4
5
var http = require('http'); 
var fs = require('fs');
var path = require('path');
var mime = require('mime');
var cache = {};
cs

 

1행 : HTTP 서버와 클라이언트 기능을 제공하는 내장 http 모듈

2행 : 파일시스템 관련 기능을 제공하는 fs 모듈 

3행 : 파일시스템 경로 관련 기능을 제공하는 내장 path모듈

4행 : 파일명 확장자 기반의 MIME 타입 추론 기능을 제공하는 외부 mime 모듈

5행 : 캐시된 파일의 내용이 저장되는 캐시 객체

 

다음으로 할 일은 정적 HTTP 파일 서비스에 필요한 세개의 헬퍼 함수를 작성하는 것입니다.

첫번째는 요청한 팡리이 존재하지 않을 때 404 오류를 전송하는 부분입니다. server.js 에 해당 함수를 추가해 보겠습니다.

1
2
3
4
5
function send404(response) {
  response.writeHead(404, {'Content-Type':'text/plain'});
  response.write('Error 404: resource not found.');
  response.end();
}
cs

 

두번째는 파일 데이터를 서비스하는 함수입니다. 먼저 적절한 HTTP 헤더를 작성한 다음 파일의 내용을 전송합니다. server.js에 작성합니다.

1
2
3
4
function sendFile(response, filePath, fileContents) {
  response.writeHead(200, {'Content-Type' : mime.lookup(path.basename(filePath))});
  response.end();
}
cs

 

파일시스템보다는 메모리 저장 장치(RAM)의 접근 속도가 더 빠릅니다. 이런 이유로 노드 어플리케이션에서는 최근에 사용한 데이터를 일반적으로 메모리에 캐싱합니다. 이번 채팅 어플리케이션에서는 정적 파일을 처음 디스크에서 읽을 때를 제외하고는 메모리에 캐시해 사용할 것입니다.

다음 함수는 캐시에 파일이 존재하는지 판단하여 캐시에 있다면 바로 서비스하고 캐시에 없다면 디스크에서 읽어와서 서비스합니다.

만약 어디에도 파일이 존재하지 않으면 응답으로 HTTP 404 오류를 반환합니다. 함수는 server.js에 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function serveStatic(response, cache, absPath) {
  if (cache[absPath]) {
    sendFile(response, absPath, cache[absPath]);
  } else {
    fs.exists(absPath, function(exists) {
      if(exists) {
        fs.readFile(absPath, function(err, data) {
          if(err) {
            send404(response);
          } else {
            cache[absPath] = data;
            sendFile(response, absPath, data);
          }
        });
      } else {
        send404(response);
      }
    });
  }
}
cs

2행 : 파일이 메모리에 캐시돼 있는지 확인

3행 : 캐시에 존재하는 파일이면 바로 서비스

5행 : 파일 존재 여부 검사

7행 : 디스크에서 파일 읽기

12행 : 디스크에 저장된 파일 서비스

16행 : HTTP 404 오류 응답

 

출처 : Node.js 인 액션

2부에서 계속..

 

 

 

 


'Language > Node' 카테고리의 다른 글

2.다중 채팅방 만들기 (2부)  (0) 2017.09.19
1.Node(노드)란? (2부)  (0) 2017.08.29
1.Node(노드)란?  (0) 2017.08.28

+ Recent posts