본문 바로가기

IT/Node.js

Node.js 실습 및 예제

 Part 1. 소개

Node.js 개발 환경구축

우분투 설치
$ sudo apt-get install -y build-essential
$ curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
$ sudo apt-get install -y nodejs

참고

  • curl이 설치되어 있지 않은 경우 : '$ sudo apt-get install curl' 명령 실행
  • 아카이브 주소와 관련된 오류가 발생한 경우 : '$ sudo apt-get update' 명령 실행

Node.js 실행

$ node

node 명령어를 실행하면 다음과 같이 Node.js 코드를 입력할 수 있는 REPL 화면이 나타납니다. 이 화면에서 한 줄씩 코드를 입력해 실행해볼 수 있습니다.

Ctrl + C 키를 2번 누르거나 .exit 명령어를 사용하면 REPL을 빠져나올 수 있습니다.

첫번째 Node.js 애플리케이션

Node.js 파일을 생성하고 실행해봅니다.

# node.basic.js 파일 생성
$ sudo atom node.basic.js
두 번째 Node.js 애플리케이션

Node.js로 간단하게 웹 서버를 만들어 봅니다.

# node.server.js 파일 생성
$ sudo atom node.server.js

# node.server.js 파일 작성
// 모듈을 추출
var http = require('http');

//웹 서버를 만들고 실행
http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/html'});
  response.end('<h1>Hello World...!</h1>');
}).listen(52273, function () {
  console.log('Server running at http://127.0.0.1:52273/');
});

# node.server.js 파일 실행
$ node node.server

웹 브라우저의 주소창에 http://127.0.0.1:52273/를 입력하면 다음과 같이 'Hello World!!!!' 문자열이 웹 브라우저에 출력됩니다.

PART 2. Node.js 기본[편집 | 원본 편집]

node.js의 전역 객체

  • 꼭 알아둬야할 개념 {| class="wikitable" !개념 !설명 |- |전역 변수/객체 |프로그램 전체에서 사용할 수 있는 변수와 객체 |- |process 객체 |프로그램과 관련된 정보를 나타내는 객체. 웹 브라우저에서 작동하는 자바스크립트에 존재하지 않는 Node.js만이 가진 객체 |- |모듈 |기능을 쉽게 사용하고자 메서드와 속성을 미리 정의해 모아 놓은 것 |- |exports 객체 |모듈을 생성할 때 사용 |}

다음과 같은 코드를 실행하면 파일 위치와 폴더 경로가 출력됩니다.

# node.global.js 파일 생성
$ sudo atom node.global.js

# node.global.js 파일 작성
console.log('filename: ', __filename);
console.log('dirname: ', __dirname);

# node.global.js 파일 실행
$ node node.global
Console 객체

console 객체는 Node.js의 콘솔 화면과 관련된 기능을 다루는 객체입니다.

메서드 이름 설명
log() 출력합니다.
time() 시간 측정을 시작합니다.
timeEnd() 시간 측정을 종료합니다.
log() 메서드

log() 메서드는 특수문자를 이용해 문자열을 출력할 수 있습니다. console.log에서 사용할 수 있는 특수 문자는 다음과 같습니다.

  • %d : 숫자
  • %s : 문자열
  • %j : JSON
$ node

console.log('output: %d', 273);
console.log('%d + %d = %d', 273, 52, 273 + 52);
console.log('%d + %d = %d', 273, 52, 273 + 52, 52273);
console.log('%d + %d = %d & %d', 273, 52, 273 + 52);
console.log('숫자: %d + %d = %d', 273, 52, 273 + 52);
console.log('문자열: %s', 'Hello World...!', '특수 기호와 상관 없음');
console.log('JSON: %j', { name: 'RintIanTta' });
time() 메서드와 timeEnd() 메서드[편집 | 원본 편집]

실행 시간을 출력할 때는 time() 메서드와 timeEnd() 메서드를 사용합니다.

  • console.time(): 시간 측정 시작
  • console.timeEnd(): 시간 측정 종료

time() 메서드와 timeEnd() 메서드의 매개변수로 들어가는 문자열은 타이머를 구분하기 위한 문자열이므로 어떤 것을 입력해도 상관 없습니다.

console.time('alpha');

//Time Check Start
var output =1;
for(var i=1; i<=10; i++){
  output *= i;
}
console.log('Result:', output);

//Time Check End
console.timeEnd('alpha');
process 객체

process 객체는 프로그램과 관련된 정보를 나타내는 객체로서 웹 브라우저에서 작동하는 자바스크립트에 존재하지 않는 Node.js만이 가진 객체입니다.

Process 객체 속성
속성이름 설 명
argv 실행매개변수를 나타냄
env 컴퓨터 환경과 관련된 정보를 나타냄
version Node.js 버전을 나타냄
versions Node.js와 종속된 프로그램 버전을 나타냄
arch 프로세서의 아키텍처를 나타냄
Platform 플랫폼을 나타냄
Process 객체의 메소드
메서드 이름 설 명
exit([exiitCode=0] 프로그램을 종료
memoryUsage() 메모리 사용 정보 객체를 리턴
uptime() 현재 프로그램이 실행된 시간을 리턴
process.argv 속성과 process.exit() 메서드
# node.process.js 파일 생성
$ sudo atom node.process.js

# node.process.js 파일 작성
// process.argv
process.argv.forEach(function(item, index) {
  // 출력합니다.
  console.log(index + ' : ' + typeof (item) + ' : ', item);

  // 실행 매개변수에 --exit가 있을 때
  if(item == '--exit') {
    // 다음 실행 매개변수를 얻습니다.
    var exitTime = Number(process.argv[index + 1]);

    // 일정 시간 후 프로그램을 종료합니다.
    setTimeout(function() {
      process.exit();
    }, exitTime);
  }
});

# node.process.js 파일 실행
$ node node.process.js --exit 10000

프로그램이 실행되고 10초 후 종료됩니다. 실행 결과를 보면 process.argv 속성이 어떠한 정보를 나타내는 배열인지 알 수 있습니다.

process 객체의 속성

node를 실행한 후 다음 명령들을 실행해봅니다.

console.log('- process.env: ', process.env);     # 컴퓨터 환경과 관련된 정보를 가진 객체

Node.js 버전 확인

프로세서의 아키텍처

프로세스 플랫폼

# 메모리 사용 정보를 가진 객체

# 현재 프로그램이 실행된 시간

export 객체와 모듈
  • Node.js는 모듈을 사용해 기능을 확장
  • 모듈은 기능을 쉽게 사용하고자 메서드와 속성을 미리 정의해 모아 놓은 것
  • 모듈을 생성할 때는 exports 객체 사용
  • 모듈을 추출할 때는 require() 함수를 사용

모듈을 직접 만들며 모듈을 생성하는 기본적인 방법을 알아봅니다.

모듈을 생성할 때는 exports 객체를 사용합니다. 다음 코드처럼 exports 객체에 속성이나 메서드를 지정합니다. 이렇게 하면 module 모듈에 abs() 메서드와 circleArea() 메서드가 생성됩니다.

exports.abs = function(number){
  if(0<number){
    return number;
  } else{
    return -number;
  }
};

exports.circleArea = function(radius){
  return radius * radius * Math.PI;
}

생성한 모듈을 다른 자바스크립트 파일에서 추출할 때는 require() 함수를 사용합니다. module.js 파일과 같은 폴더에 main.js 파일을 생성해 다음과 같은 코드를 추가합니다.

# main.js 파일 생성
$ sudo atom main.js

#main.js 파일 작성
// 모듈을 추출합니다.
var module = require('./module.js');

// 모듈을 사용합니다.
console.log('abs(-273) = %d', module.abs(-273));
console.log('circleArea(3) = %d', module.circleAreaOS모듈의 메서드(3));

코드를 모두 입력했으면 다음 명령을 사용해 코드를 실행합니다.

기본 내장 모듈

꼭 알아둘 개념

개념 설명
Node.js 문서 Node.js의 주요 기능을 설명하는 문서. https://nodejs.org/dist/latest-v4.x/docs/api에 접속하면 문서 확인 가능
url 모듈 인터넷 주소를 다루는 데 사용
Query String 모듈 URL 객체의 쿼리를 다루는 데 사용
util 모듈 Node.js의 보조 기능을 모아둠
crypto 모듈 해시 생성과 암호화를 수행
File System 모듈 파일을 다루는 데 사용
os 모듈

os 모듈은 다음과 같이 추출할 수 있습니다.

// 모듈을 추출합니다.
var os = require('os');
메서드 이름 설 명
hostname() 운영체제 호스트 이름을 리턴.
type() 운영체제의 이름을 리턴.
platform() 운영체제의 플랫폼을 리턴.
arch() 운영체제의 아키텍처를 리턴.
release() 운영체제의 버전을 리턴.
uptime() 운영체제가 실행된 시간을 리턴.
loadavg() 로드 에버리지 정보를 담은 배열을 리턴.
totalmem() 시스템의 총 메모리를 리턴.
freemem() 시스템의 사용 가능한 메모리를 리턴.
cpus() CPU의 정보를 담은 객체를 리턴
networkInterfaces() 네트워크 인터페이스의 정보를 담은 배열을 리턴

module.os.js 파일을 만들고 실행합니다.

# module.os.js 파일 생성
$ sudo atom module.os.js

# module.os.js 파일 작성
// 모듈을 추출합니다.
var os = require('os');

// 모듈을 사용합니다.
console.log(os.hostname());
console.log(os.type());
console.log(os.platform());
console.log(os.arch());
console.log(os.release());
console.log(os.uptime());
console.log(os.loadavg());
console.log(os.totalmem());
console.log(os.freemem());
console.log(os.cpus());
console.log(os.networkInterfaces());

# module.os.js 파일 실행
$ node module.os
url 모듈 
  • url 모듈의 메서드 {| class="wikitable" !메서드 이름 !설명 |- |parse(urlStr[,parseQueryString=false][,slashesDenoteHost=false] |URL 문자열을 URL 객체로 변환해 리턴 |- |format(urlObj) |URL 객체를 URL 문자열로 변환해 리턴 |- |resolve(from, to) |매개변수를 조합해 완전한 URL 문자열을 생성해 리턴 |}
# module.url.js 파일 생성
$ suto atom module.url.js

# module.url.js 파일 작성
// 모듈을 추출합니다.
var url = require('url');

// 모듈을 사용합니다.
var parsedObject = url.parse('http://www.hanbit.co.kr/store/books/look.php?p_code=B4250257160');
console.log(parsedObject);

# module.url.js 파일 실행
$ node module.url.js
Query String 모듈

URL 객체의 쿼리와 관련된 모듈

  • Query String 모듈의 주요 메서드 {| class="wikitable" !메서드 이름 !설명 |- |stringify(obj[, sep='&'][, eq='='][, options]) |쿼리 객체를 쿼리 문자열로 변환해 리턴 |- |parse(str[, sep='&'][, eq='='][, options]) |쿼리 문자열을 쿼리 객체로 변환해 리턴 |}
# module.querystring.js 파일 생성
$ sudo atom module.querystring.js

# module.querystring.js 파일 작성
// 모듈을 추출
var url = require('url');
var querystring = require('querystring');

// 모듈을 사용
var parsedObject = url.parse('http://www.hanbit.co.kr/store/books/look.php?p_code=B4250257160');
console.log(parsedObject.query);

# module.querystring.js 파일 실행
$ node module.querystring.js
util 모듈

Node.js의 보조적인 기능을 모아둔 모듈

  • util 모듈의 메서드 {| class="wikitable" !메서드 이름 !설명 |- |format(format[, ...]) |매개변수로 입력한 문자열을 조합해 리턴 |} format() 메서드는 console.log() 메서드와 비슷하지만 출력하지 않고 문자열을 반환하는 것이 차이점
# module.util.js 파일 생성
$ sudo atom module.util.js

# module.util.js 파일 작성
// 모듈을 추출합니다.
var util = require('util');

// 모듈을 사용합니다.
var data = util.format('%d + %d = %d', 52, 273, 52 + 273);
console.log(data);

# module.util.js 파일 실행
$ node module.util.js
cryoto 모듈

해시 생성과 암호화를 수행하는 모듈

# module.crypto.js 파일 생성
$ sudo atom module.crypto.js

# module.crypto.js 파일 작성
// 모듈을 추출합니다.
var cryoto = require('crypto');

// 해시를 생성합니다.
var shasum = cryoto.createHash('sha256');
shasum.update('crypto_hash');
var output = shasum.digest('hex');

// 출력합니다.
console.log('crypto_hash', output);


# module.crypto.js 파일 실행
$ node module.crypto.js

해시는 다른 문자열이라도 같게 나올 수 있기 때문에 해시를 원래 값으로 돌리는 것은 불가능합니다. 그렇기 때문에 보안을 위해 사용합니다.

문자열을 원래 상태로 돌릴 수 있는 것은 암호입니다.

# module.crypto2.js 파일 생성
$ sudo atom module.crypto2.js

# module.crypto2.js 파일 작성
// 모듈을 추출합니다.
var crypto = require('crypto');

// 변수를 선언합니다.
var key = '아무도 알지 못하는 나만의 비밀 키';
var input = 'PASSWORD';

// 암호화
var cipher = crypto.createCipher('aes192', key);
cipher.update(input, 'utf8', 'base64');
var cipheredOutput = cipher.final('base64');

// 암호화 해제
var decipher = crypto.createDecipher('aes192', key);
decipher.update(cipheredOutput, 'base64', 'utf8');
var decipheredOutput = decipher.final('utf8');

// 출력합니다.
console.log('원래 문자열: ' + input);
console.log('암호화: ' + cipheredOutput);
console.log('암호화 해제: '+ decipheredOutput);

# module.crypto2.js 파일 실행
$ node module.crypto2.js
    File System 모듈
    • File System 모듈의 주요 메서드 {| class="wikitable" !메서드 이름 !설 명 |- |readFile(file, encoding, callback) |파일을 비동기적으로 읽음 |- |readFileSync(file, encoding) |파일을 동기적으로 읽습니다. |- |writeFile(file, data, encoding, callback) |파일을 비동기적으로 씁니다. |- |writeFileSync(file, data, encoding) |파일을 동기적으로 씁니다. |}
    파일 읽기

    readFileSync() 메서드는 매개변수에 파일 경로와 인코딩 방식을 지정합니다.

    textfile.txt 파일을 생성해 간단한 문자열 "This is a textfile.txt"를 입력합니다.

    # module.fs.js 파일 생성
    $ sudo atom module.fs.js
    
    # module.fs.js 파일 작성
    // 모듈을 추출합니다.
    var fs = require('fs');
    
    // 모듈을 사용합니다.
    var text = fs.readFileSync('textfile.txt', 'utf8');
    console.log(text);
    
    # module.fs.js 파일 실행
    $ node module.fs.js
    

    readFile() 메서드는 readFileSync() 메서드를 비동기적으로 구현한 메서드입니다.

    따라서 readFile() 메서드를 만나는 순간 이벤트리스너를 등록하고 파일을 모두 읽게 만들어 이벤트 리스너를 실행합니다.

    # module.fs2.js 파일 생성
    $ sudo atom module.fs2.js
    
    # module.fs2.js 파일 작성
    // 모듈을 추출합니다.
    var fs = require('fs');
    
    // 모듈을 사용합니다.
    fs.readFile('textfile.txt', 'utf8', function (error, data) {
      console.log(data);
    });
    
    # module.fs2.js 파일 실행
    $ node module.fs2.js
    
    파일 쓰기 
    # module.fs3.js 파일 생성
    $ sudo atom module.fs3.js
    
    # module.fs3.js 파일 작성
    // 모듈을 추출합니다.
    var fs = require('fs');
    
    // 변수를 선언합니다.
    var data = 'Hello World .. !';
    
    // 모듈을 사용합니다.
    fs.writeFile('TextFileOtherWrite.txt', data, 'utf8', function (error) {
      console.log('WRITE FILE ASYNC COMPLETE');
    });
    
    fs.writeFileSync('TextFileOtherWrite.txt', data, 'utf8');
    console.log('WRITE FILE SYNC COMPLETE');
    
    # module.fs3.js 파일 실행
    $ sudo node module.fs3.js
    

    module.fs3 파일을 실행하면 위와 같은 화면이 출력되고, 실행한 프로그램과 같은 폴더에 TextFileOtherWrite.txt 파일과 TextFileOtherWriteSync.txt 파일이 생성됩니다.

    예외 처리

    파일을 읽으려고 했는데 파일이 존재하지 않거나 파일을 쓸 수 없는 위치에 파일을 쓰려고 한다면 프로그램은 예외가 발생해 곧바로 종료됩니다. 이러한 예외를 처리하기 위해 동기 처리를 하는 메서드와 비동기 처리를 하는 메서드의 예외 처리 방법을 알아봅니다.

    • 동기 처리 메서드의 예외 처리 방법 : try catch 구문
      // 모듈을 추출합니다.
      var fs = require('fs');
      
      // 파일을 읽습니다.
      try {
        var data = fs.readFileSync('textfile.txt', 'utf8');
        console.log(data);
      } catch (e) {
        console.log(e);
      }
      
      // 파일을 씁니다.
      try {
        fs.writeFileSync('textfile.txt', 'Hello World .. !', 'utf8');
        console.log('FILE WRITE COMPLETE');
      } catch (e) {
        console.log(e);
      }
      
    • 비동기 처리 메서드의 예외 처리 방법
      // 모듈을 추출합니다.
      var fs = require('fs');
      
      // 파일을 읽습니다.
      fs.readFile('textfile.txt', 'utf8', function ( error, data) {
        // 오류가 발생하면 곧바로 리턴
        if (error) { return console.log(error); }
      
        // 원하는 처리
        console.log(data);
      });
      
      // 파일을 씁니다.
      fs.writeFile('textfile.txt', 'Hello World .. !', 'utf8', function (error) {
        // 오류가 발생하면 곧바로 리턴
        if (error) { return console.log(error); }
      
        // 원하는 처리
        console.log('FILE WRITE COMPLETE');
      });
      
      오류가 발생하면 곧바로 리턴하게 만드는 조기 리턴을 사용하면 코드를 깔끔하게 만들 수 있습니다.

    이벤트

    꼭 알아둘 개념

    개념 설명
    on() 이벤트를 연결하는 메서드
    emit() 이벤트를 실행할 때 사용
    EventEmitter 객체 이벤트를 연결할 수 있는 모든 객체의 어머니
    이벤트 연결
    • 이벤트 연결메서드 {| class="wikitable" !메서드 이름 !설 명 |- |on(eventName, eventHandler) |이벤트를 연결합니다. |}
    # event.connect.js 파일 생성
    $ sudo atom event.connect.js
    
    # event.connect.js 파일 작성
    // process 객체에 exit 이벤트를 연결합니다.
    process.on('exit', function (code) {
      console.log('안녕히 가거라 ^^ .. !');
    });
    
    // process 객체에 uncaughtException 이벤트를 연결합니다.
    process.on('uncaughtException', function (error) {
      console.log('예외가 발생했군 ^^ 봐주겠다 ^^ .. !');
    });
    
    // 2초 간격으로 3번 예외를 발생시킵니다.
    var count = 0;
    var test = function () {
      // 탈출 코드
      count = count + 1;
      if(count > 3) { return; }
    
      // 예외를 강제로 발생시킵니다.
      setTimeout(test, 2000);
      error.error.error();  
    };
    setTimeout(test, 2000);
    
    # event.connect.js 파일 실행
    $ node event.connect.js
    

    위의 코드를 실행하면 exit 이벤트가 발생할 때 문자열 "안녕히 가거라 ^^ .. !"를 출력하고,

    uncaughtException 이벤트가 발생할 때 문자열 "예외가 발생했군 ^^ 봐주겠다 ^^ .. !"를 출력합니다.

    uncaughtException 이벤트는 실제 프로젝트를 진행할 때는 사용하지 않는 것이 좋습니다.

    예외가 발생할 수 있는 부분에 try catch 구문을 이용해 직접 예외를 처리해야 합니다.

    이벤트 연결 개수 제한

    Node.js는 한 이벤트에 10개가 넘는 이벤트 리스너를 연결할 경우 이를 개발자 실수로 간주합니다.

    # event.limit.js 파일 생성
    $ sudo atom event.limit.js
    
    # event.limit.js 파일 작성
    // 이벤트를 연결합니다.
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    
    # event.limit.js 파일 실행
    $ node event.limit.js
    

    다음과 같은 코드를 실행하면 프로그램은 정상적으로 실행되고 종료되지만 아래와 같은 경고가 발생합니다.

    많은 이벤트 리스너를 연결할 때 나타나는 경고를 제거하려면 다음 메서드를 사용합니다.

    메서드 이름 설명
    setMaxListeners(limit) 이벤트 리스너 연결 개수를 조절합니다.
    # event.limit2.js 파일 생성
    $ sudo atom event.limit2.js
    
    # event.limit2.js 파일 작성
    // 이벤트 연결 개수 제한을 15개까지 늘립니다.
    process.setMaxListeners(15);
    
    // 이벤트를 연결합니다.
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    process.on('exit', function() { });
    
    # event.limit2.js 파일 실행
    $ node event.limit2.js
    
    이벤트 제거
    • 이벤트를 제거할 때 사용하는 메서드 {| class="wikitable" !메서드 이름 !설 명 |- |removeListener(eventName, handler) |특정 이벤트의 이벤트 리스너를 제거 |- |removeAllListener([eventName]) |모든 이벤트 리스너를 제거 |}
    # event.remove.js 파일 생성
    $ sudo atom event.remove.js
    
    # event.remove.js 파일 작성
    // 변수를 선언합니다.
    var onUncaughtException = function (error) {
      // 출력합니다.
      console.log('예외가 발생했군^^ 이번에만 봐주겠다 ^^ .. !');
    
      // 이벤트를 제거합니다.
      process.removeListener('uncaughtException', onUncaughtException);
    };
    
    // process 객체에 uncaughtException 이벤트를 연결합니다.
    process.on('uncaughtException', onUncaughtException);
    
    // 2초 간격으로 예외를 발생시킵니다.
    var test = function () {
      setTimeout(test, 2000);
      error.error.error();
    };
    setTimeout(test, 2000);
    
    # event.remove.js 파일 실행
    $ node event.remove.js
    

    위와 같은 코드를 실행하면 2초 후 예외가 발생하고 uncaughtException 이벤트 리스너가 실행됩니다. 처음에는 "예외가 발생했군^^ 이번에만 봐주겠다 ^^ .. !"를 출력하고 넘어가지만 두 번째 예외가 발생하면 프로그램이 종료됩니다.

    이벤트를 한 번만 연결하고 싶은 경우에는 once() 메서드를 사용해도 됩니다.

    메서드 이름 설명
    once(eventName, eventHandler) 이벤틀 리스너를 한 번만 연결합니다.
    // process 객체에 uncaughtException 이벤트를 연결합니다.
    process.once('uncaughtException', function (error) {
      console.log('예외가 발생했군^^ 이번에만 봐주겠다 ^^ .. !');
    });
    
    // 2초 간격으로 예외를 발생시킵니다.
    var test = function () {
      setTimeout(test, 2000);
      error.error.error();
    };
    setTimeout(test, 2000);
    
    이벤트 강제 발생 
    • 이벤트 강제 발생 메서드 {| class="wikitable" !메서드 이름 !설 명 |- |emit(eventName[, arg1][, arg2][, ...] |이벤트를 실행 |}
    # event.trigger.js 파일 생성
    $ sudo atom event.trigger.js
    
    # event.trigger.js 파일 작성
    // exit 이벤트를 연결합니다.
    process.on('exit', function (code) {
      console.log('안녕히 계세요 .. !');
    });
    
    // 이벤트를 강제로 발생시킵니다.
    process.emit('exit');
    process.emit('exit');
    process.emit('exit');
    process.emit('exit');
    
    // 프로그램 실행 중
    console.log('프로그램 실행 중');
    
    # event.trigger.js 파일 실행
    $ node event.trigger.js
    

    exit 이벤트를 강제로 호출해도 프로그램이 종료되지 않습니다. emit() 메서드를 사용해서 이벤트를 강제로 호출하면 이벤트 리스너만 실행됩니다.

    프로그램을 종료할 때에는 process 객체의 exit() 메서드를 사용합니다.

    // exit 이벤트를 연결합니다.
    process.on('exit', function (code) {
      console.log('안녕히 계세요 .. !');
    });
    
    // 프로그램을 종료합니다.
    process.exit();
    
    // 이벤트를 강제로 발생시킵니다.
    process.emit('exit');
    process.emit('exit');
    process.emit('exit');
    process.emit('exit');
    
    // 프로그램 실행 중
    console.log('프로그램 실행 중');
    
    이벤트 생성

    Node.js에서 이벤트를 연결할 수 있는 모든 객체는 EventEmitter 객체의 상속을 받습니다.

    EventEmitter 객체는 process 객체 안에 있는 생성자 함수로 생성할 수 있는 객체입니다.

    • EventEmitter 객체의 메서드 {| class="wikitable" !메서드 이름 !설 명 |- |addListener(eventName, eventHandler) |이벤트 연결 |- |on(eventName, eventHandler) |이벤트 연결 |- |setMaxListeners(limit) |이벤트 연결 개수 조절 |- |removeListener(eventName, handler) |특정 이벤트의 이벤트 리스너 제거 |- |removeAllListeners([eventName]) |모든 이벤트 리스너 제거 |- |once(eventName, eventHandler) |이벤트를 한 번만 연결 |}
    # event.create.js 파일 생성
    $ sudo atom event.create.js
    
    # event.create.js 파일 작성
    // EventEmitter 객체를 생성합니다.
    var custom = new process.EventEmitter();
    
    // 이벤트를 연결합니다.
    custom.on('tick', function (code) {
      console.log('이벤트를 실행합니다. ^^');
    });
    
    // 이벤트를 강제로 발생시킵니다.
    custom.emit('tick');
    
    # event.create.js 파일 실행
    $ node event.create.js
    

    Node.js를 사용한 웹 개발 

    http 모듈

    꼭 알아둘 개념

    개 념 설 명
    요청 웹페이지에 접속하려고 하는 어떠한 요청
    응답 요청을 받아 이를 처리하는 작업
    http 모듈 HTTP 웹 서버와 관련된 기능을 담은 모듈
    server 객체 웹 서버를 생성하는데 꼭 필요한 객체
    response 객체 응답 메시지를 작성할때, request 이벤트 리스너의 두 번째 매개변수로 전달되는 객체
    request 객체 응답 메시지를 작성할때, request 이벤트 리스너의 첫 번째 매개변수로 전달되는 객체
    요청과 응답

    웹 서버가 하는 일은 요청과 응답의 연속이라고 정의할 수 있습니다. 요청하는 대상을 클라이언트(사용자), 응답하는 대상을 서버(제공자)라고 부릅니다.

    요청 메시지를 사용해야 사용자에게 적절한 웹 페이지를 제공할 수 있습니다.

    응답 메시지를 사용하면 쿠키를 저장하거나 추출할 수 있고 강제로 웹 페이지를 이동시킬 수도 있습니다.

    server 객체

    http 모듈에서 가장 중요한 객체는 server 객체입니다. http 모듈의 createServer() 메서드를 사용하면 server 객체를 생성할 수 있습니다.

    • server객체의 메서드
    메서드 이름 설 명
    listen(port[, callback])) 서버를 실행합니다.
    close([callback]) 서버를 종료합니다.

    간단하게 서버를 실행하고 종료하는 예제를 살펴봅시다.

    # server.js 파일 생성
    $ sudo atom server.js
    
    # server.js 파일 작성
    // 서버를 생성합니다.
    var server = require('http').createServer();
    
    // 서버를 실행합니다.
    server.listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    // 10초 후 함수를 실행합니다.
    var test = function() {
      //서버를 종료합니다.
      server.close();
    };
    setTimeout(test, 10000);
    
    # server.js 파일 실행
    $ node server.js
    
    • server 객체의 이벤트
    이벤트 이름 설 명
    request 클라이언트가 요청할 때 발생하는 이벤트
    connection 클라이언트가 접속할 때 발생하는 이벤트
    close 서버가 종료될 때 발생하는 이벤트
    checkContinue 클라이언트가 지속적인 연결을 하고 있을 때 발생하는 이벤트
    upgrade 클라이언트가 HTTP 업그레이드를 요청할 때 발생하는 이벤트
    clientError 클라이언트에서 오류가 발생할 때 발생하는 이벤트
    # server.event.js 파일 생성
    $ sudo atom server.event.js
    
    # server.event.js 파일 작성
    // 모듈을 추출합니다.
    var http = require('http');
    
    // server 객체를 생성합니다.
    var server = http.createServer();
    
    // server 객체에 이벤트를 연결합니다.
    server.on('request', function (code) {
      console.log('Request On');
    });
    
    server.on('connection', function (code) {
      console.log('Connection On');
    });
    
    server.on('close', function (code) {
      console.log('Close On');
    });
    
    // listen() 메서드를 실행합니다.
    server.listen(52273);
    
    # server.event.js 파일 실행
    $ node server.event.js
    
    response 객체 
    • response 객체의 메서드
    메서드 이름 설 명
    writeHead(statusCode[, statusMessage][, headers]) 응답 헤더를 작성합니다.
    end([data][, encoding][, callback]) 응답 본문을 작성합니다.

    간단한 응답 메시지를 작성해봅시다.

    # response.basic.js 파일 생성
    $ sudo atom response.basic.js
    
    # response.basic.js 파일 작성
    // 웹 서버를 생성하고 실행합니다.
    require('http').createServer(function (request, response) {
      // 응답합니다.
      response.writeHead(200, { 'Content-Type': 'text/html' });
      response.end('<h1>Hello Web Server with Node.js</h1>');
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # response.basic.js 파일 실행
    $ node response.basic.js
    
    File System 모듈을 사용한 HTML 페이지 제공

    File System 모듈을 사용해 서버에 존재하는 HTML 페이지를 클라이언트에 제공해보겠습니다.

    1. http.file.js 파일 작성
      /***** http.file.js *****/
      var fs = require('fs');
      var http = require('http');
      
      // 서버를 생성하고 실행합니다.
      http.createServer(function (request, response) {
      	// HTML 파일을 읽습니다.
        fs.readFile('HTMLPage.html', function (error, data) {
          response.writeHead(200, { 'Content-Type': 'text/html' });
          response.end(date);
        });
      }).listen(52273, function() {
        console.log('Server Running at http://127.0.0.1:52273')
      });
      
    2. HTMLPage.html 파일 작성
      .another_category { border: 1px solid #E5E5E5; padding: 10px 10px 5px; margin: 10px 0; clear: both; } .another_category h4 { font-size: 12px !important; margin: 0 !important; border-bottom: 1px solid #E5E5E5 !important; padding: 2px 0 6px !important; } .another_category h4 a { font-weight: bold !important; } .another_category table { table-layout: fixed; border-collapse: collapse; width: 100% !important; margin-top: 10px !important; } * html .another_category table { width: auto !important; } *:first-child + html .another_category table { width: auto !important; } .another_category th, .another_category td { padding: 0 0 4px !important; } .another_category th { text-align: left; font-size: 12px !important; font-weight: normal; word-break: break-all; overflow: hidden; line-height: 1.5; } .another_category td { text-align: right; width: 80px; font-size: 11px; } .another_category th a { font-weight: normal; text-decoration: none; border: none !important; } .another_category th a.current { font-weight: bold; text-decoration: none !important; border-bottom: 1px solid !important; } .another_category th span { font-weight: normal; text-decoration: none; font: 10px Tahoma, Sans-serif; border: none !important; } .another_category_color_gray, .another_category_color_gray h4 { border-color: #E5E5E5 !important; } .another_category_color_gray * { color: #909090 !important; } .another_category_color_gray th a.current { border-color: #909090 !important; } .another_category_color_gray h4, .another_category_color_gray h4 a { color: #737373 !important; } .another_category_color_red, .another_category_color_red h4 { border-color: #F6D4D3 !important; } .another_category_color_red * { color: #E86869 !important; } .another_category_color_red th a.current { border-color: #E86869 !important; } .another_category_color_red h4, .another_category_color_red h4 a { color: #ED0908 !important; } .another_category_color_green, .another_category_color_green h4 { border-color: #CCE7C8 !important; } .another_category_color_green * { color: #64C05B !important; } .another_category_color_green th a.current { border-color: #64C05B !important; } .another_category_color_green h4, .another_category_color_green h4 a { color: #3EA731 !important; } .another_category_color_blue, .another_category_color_blue h4 { border-color: #C8DAF2 !important; } .another_category_color_blue * { color: #477FD6 !important; } .another_category_color_blue th a.current { border-color: #477FD6 !important; } .another_category_color_blue h4, .another_category_color_blue h4 a { color: #1960CA !important; } .another_category_color_violet, .another_category_color_violet h4 { border-color: #E1CEEC !important; } .another_category_color_violet * { color: #9D64C5 !important; } .another_category_color_violet th a.current { border-color: #9D64C5 !important; } .another_category_color_violet h4, .another_category_color_violet h4 a { color: #7E2CB5 !important; } \n\n\t

      Hello Node.js

      \n\t

      Author. RintIanTta

      \n\t
      \n\t

      Lorem ipsum dolor sit amet.

      \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
      	<title>Index</title>
      </head>
      <body>
      	<h1>Hello Node.js</h1>
      	<h2>Author. RintIanTta</h2>
      	<hr />
      	<p>Lorem ipsum dolor sit amet.</p>
      </body>
      </html>
      
    3. http.file.js 파일 실행
      $ node http.file.js
      
    4. http://127.0.0.1:52273 접속
    이미지와 음악 파일 제공

    웹에서는 이미지나 음악 파일, 동영상 파일도 다운로드할 수 있습니다.

    특정 형식 파일을 제공할 때 가장 중요한 것은 응답 헤더의 Content-Type 속성입니다. Content-Type 속성은 MIME 형식을 입력합니다.

    • MIME 형식의 예
    Content Type 설 명
    text/plain 기본적인 텍스트
    text/html HTML문서
    text/css CSS 문서
    text/xml XML 문서
    image/jpeg JPEG 그림 파일
    image/png PNG 그림 파일
    video/mpeg MPEG 비디오 파일
    audio/mp3 MP3 음악 파일
    쿠키 생성

    쿠키는 키와 값이 들어 있는 작은 데이터 조각으로 이름, 값, 파기 날짜와 경로 정보가 있습니다. 쿠키는 서버와 클라이언트에서 모두 저장하고 사용할 수 있으며, 일정 기간 동안 데이터를 저장할 수 있으므로 로그인 상태를 일정 시간 동안 유지해야 하는 페이스북과 같은 웹 사이트에 사용합니다.

    # response.cookie.js 파일 생성
    $ sudo atom response.cookie.js
    
    # response.cookie.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    
    // 서버를 생성하고 실행합니다.
    http.createServer(function (request, response) {
      // 변수를 선언합니다.
      var date = new Date();
      date.setDate(date.getDate() + 7);
    
      // 쿠키를 입력합니다.
      response.writeHead(200, {
        'Content-Type': 'text/html ',
        'Set-Cookie': [
          'breakfase = toast;Expires = ' + date.toUTCString(),
          'dinner = chicken'
        ]
      });
    
      // 쿠키를 출력합니다.
      response.end('<h1>' + request.headers.cookie + '</h1>');
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # response.cookie.js 파일 실행
    $ node response.cookie.js
    

    처음 요청을 할 때는 클라이언트가 쿠키를 가지고 있지 않으므로 아래와 같은 결과를 출력합니다.

    하지만 두 번째 요청부터는 클라이언트가 쿠키를 가지고 있으므로 아래와 같이 생성한 쿠키를 출력합니다.

    페이지 강제 이동

    웹 페이지를 강제로 이동시킬 때는 응답 헤더의 Location 속성을 사용합니다. Location 속성에 이동하고자 하는 웹 페이지 주소를 입력하기만 하면 됩니다.

    # response.location.js 파일 생성
    $ sudo atom response.location.js
    
    # response.location.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    
    // 서버를 생성하고 실행합니다.
    http.createServer(function (request, response) {
      response.writeHead(302, { 'Location': 'http://www.hanbit.co.kr' });
      response.end();
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # response.location.js 파일 실행
    $ node response.location.js
    
    • HTTP Status Code 예 | class="wikitable" !HTTP Status Code !설명 !예 |- |1XX |처리 중 |100 Continue |- |2XX |성공 |200 OK |- |3XX |리다이렉트 |300 Multiple Choices |- |4XX |클라이언트 오류 |400 Bad Request |- |5XX |서버 오류 |500 Internal Server Error |}
    HTTP Status Code 설 명
    1XX 처리 중 100 Continue
    2XX 성공 200 OK
    3XX 리다이렉트 300 Multiple Choices
    4XX 클라이언트 오류 400 Bad Request
    5XX 서버 오류 500 Internal Server Error
    request 객체

    server 객체의 request 이벤트가 발생할 때 이벤트 리스너의 첫 번째 매개변수에는 request 객체가 들어갑니다.

    • request 객체의 속성
    속성 이름 설명
    method 클라이언트의 요청 방식을 나타냅니다.
    url 클라이언트가 요청한 URL을 나타냅니다.
    headers 요청 메시지 헤더를 나타냅니다.
    trailers 요청 메시지 트레일러를 나타냅니다.
    httpVersion HTTP 프로토콜 버전을 나타냅니다.
    url 속성을 사용한 페이지 구분

    요청 메시지의 URL에 따라 서로 다른 웹 페이지를 제공하는 애플리케이션을 만들어봅니다.

    1. Index.html 파일 생성
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n\tIndex\n \n\n\t

      Hello Node.js _ Index

      \n\t

      Author. RintIanTta

      \n\t
      \n\t

      Lorem ipsum dolor sit amet.

      \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
      	<title>Index</title>
      </head>
      <body>
      	<h1>Hello Node.js _ Index</h1>
      	<h2>Author. RintIanTta</h2>
      	<hr />
      	<p>Lorem ipsum dolor sit amet.</p>
      </body>
      </html>
      
    2. OtherPage.html 파일 생성
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n\tOtherPage\n \n\n\t

      Hello Node.js _ OtherPage

      \n\t

      Author. RintIanTta

      \n\t
      \n\t

      Lorem ipsum dolor sit amet.

      \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
      	<title>OtherPage</title>
      </head>
      <body>
      	<h1>Hello Node.js _ OtherPage</h1>
      	<h2>Author. RintIanTta</h2>
      	<hr />
      	<p>Lorem ipsum dolor sit amet.</p>
      </body>
      </html>
      
    3. app.js 파일 생성
      //모듈을 추출합니다.
      var http = require('http');
      var fs = require('fs');
      var url = require('url');
      
      // 서버를 생성하고 실행합니다.
      http.createServer(function (request, response) {
        // 변수를 선언합니다.
        var pathname = url.parse(request.url).pathname;
      
        // 페이지를 구분합니다.
        if (pathname == '/') {
          //Index.html 파일을 읽습니다.
          fs.readFile('Index.html', function (error, data) {
            //응답합니다.
            response.writeHead(200, { 'Content-Type': 'text/html' });
            response.end(data);
          });
        } else if (pathname == '/OtherPage') {
          //OtherPage.html 파일을 읽습니다.
          fs.readFile('OtherPage.html', function (error, data) {
            //응답합니다.
            response.writeHead(200, { 'Content-Type': 'text/html' });
            response.end(data);
          });
        }
      }).listen(52273, function () {
        console.log('Server Running at http://127.0.0.1:52273');
      });
      
    4. 코드 실행
    method 속성을 사용한 페이지 구분

    request 객체의 method 속성은 알파벳 대문자로 이루어진 문자열로 GET 요청과 POST 요청을 구분할 수 있습니다.

    # http.request.js 파일 생성
    $ sudo atom http.request.js
    
    # http.request.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    
    // 모듈을 사용합니다.
    http.createServer(function (request, response) {
      if (request.method == 'GET') {
        console.log('GET 요청입니다.')
      } else if (request.method == 'POST') {
        console.log('POST 요청입니다.')
      }
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # http.request.js 파일 실행
    $ node http.request.js
    

    위와 같은 코드를 실행하고 웹 브라우저로 접속하면 다음과 같이 문자열 "GET 요청입니다."라는 메시지를 확인할 수 있습니다.

    GET 요청 매개변수 추출 
    # request.get.js 파일 생성
    $ sudo atom request.get.js
    
    # request.get.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    var url = require('url');
    
    // 모듈을 사용합니다.
    http.createServer(function (request, response) {
      // 요청 매개변수를 추출합니다.
      var query = url.parse(request.url, true).query;
    
      // GET 요청 매개변수 출력
      response.writeHead(200, { 'Content-Type': 'text/html' });
      response.end('<h1>' + JSON.stringify(query) + '</h1>');
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # request.get.js 파일 실행
    $ node request.get.js
    

    코드를 실행하고 http://127.0.0.1:52273/?name=rintiantta&region=seoul에 접속하면 다음과 같이 GET 요청의 요청 매개변수가 JSON 형식으로 출력됩니다.

    POST 요청 매개변수 추출

    POST 방식은 GET 방식과 달리 데이터를 더 많이 담을 수 있고 보안 측면에서도 좋습니다. GET 방식은 요청하면서 매개변수 형식으로 노출되어 데이터를 전달하지만 POST 방식은 요청한 후 데이터를 별도로 전달하기 때문입니다.

    request 이벤트가 발생한 후 request 객체의 data 이벤트로 데이터가 전달됩니다.

    간단한 예제를 통해 POST 요청을 보내고 요청 매개변수를 출력해봅니다.

    1. HTMLPage2.html 파일 작성
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n\tNode.js Example\n \n\n\t

      Send Data With POST Method

      \n
      \n \n \n \n \n \n \n \n \n \n
      \n \n
      \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
      	<title>Node.js Example</title>
      </head>
      <body>
      	<h1>Send Data With POST Method</h1>
        <form method="POST">
          <table>
            <tr>
              <td><label>Data A</label></td>
              <td><input type="text" name="data_a" /></td>
            </tr>
            <tr>
              <td><label>Data B</label></td>
              <td><input type="text" name="data_b" /></td>
            </tr>
          </table>
          <input type="submit" />
        </form>
      </body>
      </html>
      
    2. request.post.js 파일 작성
      //모듈을 추출합니다.
      var http = require('http');
      var fs = require('fs');
      
      // 모듈을 사용합니다.
      http.createServer(function (request, response) {
        // GET 요청
        if (request.method == 'GET') {
          fs.readFile('HTMLPage2.html', function (error, data) {
            response.writeHead(200, { 'Content-Type': 'text/html' });
            response.end(data);
          });
        } else if (request.method == 'POST') {
          // POST 요청
          request.on('data', function (data) {
            response.writeHead(200, { 'Content-Type': 'text/html' });
            response.end('<h1>' + data+ '</h1>');
          });
        }
      }).listen(52273, function () {
        console.log('Server Running at http://127.0.0.1:52273');
      });
      
    3. 결과 페이지
    쿠키 추출

    쿠키는 request 객체의 headers 속성 안 cookie 속성에서 추출할 수 있습니다.

    # request.cookie.js 파일 생성
    $ sudo atom request.cookie.js
    
    # request.cookie.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    
    // 모듈을 사용합니다.
    http.createServer(function (request, response) {
      // GET COOKIE
      var cookie = request.headers.cookie;
    
      // SET COOKIE
      response.writeHead(200, {
        'Content-Type': 'text/html',
        'Set-Cookie': ['name = RintIanTta', 'region = seoul']
      });
    
      // 응답합니다.
      response.end('<h1>' + JSON.stringify(cookie) + '</h1>');
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # request.cookie.js 파일 실행
    $ node request.cookie.js
    

    코드를 실행하고 웹 페이지에 접속하면 첫 번째 접속에서는 다음과 같이 undefined를 출력합니다. 

    # request.cookie2.js 파일 생성
    $ sudo atom request.cookie2.js
    
    # request.cookie2.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    // 모듈을 사용합니다.
    http.createServer(function (request, response) {
      // 쿠키가 있는지 확인
      if (request.headers.cookie) {
        // 쿠키를 추출하고 분해합니다.
        var cookie = request.headers.cookie.split(';').map(function (element) {
          var element = element.split('=');
          return {
            key: element[0],
            value: element[1]
          };
        });
    
        // 응답합니다.
        response.end('<h1>' + JSON.stringify(cookie) + '</h1>');
      } else {
        // 쿠키를 생성합니다.
        response.writeHead(200, {
          'Content-Type': 'text/html',
          'Set-Cookie': ['name = RintIanTta', 'region = seoul']
        });
        // 응답합니다.
        response.end('<h1>쿠키를 생성했습니다</h1>');
      }
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # request.cookie2.js 파일 실행
    $ node request.cookie2.js
    

    외부 모듈

    꼭 알아둘 개념

    개념 설명
    ejs 모듈/jade 모듈 웹 페이지를 동적으로 생성하는 템플릿 엔진 모듈입니다.
    supervisor 모듈 파일의 변경 사항을 자동으로 인식하고 종료 후 다시 실행시킵니다.
    forever 모듈 웹 서비스 장애와 같은 예외 상황을 대비하고자 만들어진 모듈입니다.
    npm install 명령 외부 모듈을 설치할 때 사용합니다.
    npm init 명령 Node.js 프로젝트를 생성할 때 사용합니다.
    package.json 파일 Node.js 프로젝트의 환경설정 정보를 담은 파일입니다.

    외부 모듈을 설치할 때는 콘솔 화면에 다음 명령을 입력합니다.

    $ npm install 모듈명
    

    다음 모듈을 설치해봅시다.

    $ sudo npm install ejs
    $ sudo npm install jade
    
    ejs 모듈

    ejs 모듈은 템플릿 엔진 모듈로 특정 형식의 문자열을 HTML 형식의 문자열로 반환합니다.

    ejs 모듈의 메서드
    메서드 이름 설 명
    render(str, data, option) ejs 문자열을 HTML 문자열로 변경.

    이제 ejs 모듈을 추출하고 render() 메서드로 ejs 페이지를 HTML 페이지로 변환해 클라이언트에 제공해봅시다.

    우선, 실습을 위해 ejs.basic.js 와 ejsPage.ejs를 만듭니다.

    # ejsPage.js 파일 생성
    $ sudo atom ejsPage.js
    
    # ejsPage.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var ejs = require('ejs');
    
    // 서버를 생성하고 실행합니다.
    http.createServer(function (request, response) {
      // ejsPage.ejs 파일을 읽습니다.
      fs.readFile('ejsPage.ejs', 'utf8', function (error, data) {
        response.writeHead(200, { 'Content-type': 'text/html' });
        response.end(ejs.render(data));
      });
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # ejsPage.js 파일 실행
    $ node sudo atom ejsPage.js
    

    코드를 실행하고 http://127.0.0.1:52273에 접속하면 아직 ejsPage.ejs 파일에 아무런 내용도 입력하지 않았기 때문에 빈 페이지를 제공합니다.

    ejs 기본 형식

    ejs 기본 형식은 HTML 형식과 다르게 다음과 같은 2가지 특수 태그를 갖습니다.

    태그 설명
    <% Code %> 자바스크립트 코드를 입력합니다.
    <%= Value %> 데이터를 출력합니다.

    이 2가지 특수 태그는 render() 메서드를 사용해 ejs 페이지를 적절한 형태의 HTML 페이지로 변환시킵니다.

    데이터 전달 
    # ejs.render.js 파일 생성
    $ sudo atom ejs.render.js
    
    # ejs.render.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var ejs = require('ejs');
    
    // 서버를 생성하고 실행합니다.
    http.createServer(function (request, response) {
      // ejsPage.ejs 파일을 읽습니다.
      fs.readFile('ejsPage.ejs', 'utf8', function (error, data) {
        response.writeHead(200, { 'Content-type': 'text/html' });
        response.end(ejs.render(data, {
          name: 'RintIanTta',
          description: 'Hello ejs With Node.js .. !'
        }));
      });
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # ejsPage.ejs 파일 작성
    $ sudo atom ejsPage.ejs
    <h1><%= name %></h1>
    <p><%= description %></p>
    <hr />
    <% for(var i = 0; i < 10; i++) { %>
      <h2>The Square of <%= i %> is <%= i * i %></h2>
    <% } %>
    
    # ejs.render.js 파일 실행
    $ node ejs.render.js
    

    코드 실행 후 웹 페이지에 접속하면 다음과 같은 결과가 출력됩니다.

    jade 모듈

    jade 모듈도 템플릿 엔진 모듈입니다. Node.js에서 가장 많이 사용되는 엡 프레임워크인 express 프레임워크가 템플릿 엔진으로 ejs 모듈과 jade 모듈을 주로 사용하므로 2가지 모두 살펴보는 것입니다.

    jade 모듈의 메서드

    jade 페이지를 HTML 페이지로 변환할 때는 compile() 메서드를 사용합니다. ejs 모듈의 render() 메서드와 다르게 문자열을 리턴하는 것이 아니라 함수를 리턴한다는 차이가 있습니다.

    메서드 이름 설명
    compile(string, option) jade 문자열을 HTML 문자열로 바꿀 수 있는 함수를 생성합니다.

    compile() 메서드는 다음과 같이 사용합니다.

    # jade.compile.js 파일 생성
    $ sudo atom jade.compile.js
    
    # jade.compile.js 파일 작성
    //모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var jade = require('jade');
    
    // 서버를 생성하고 실행합니다.
    http.createServer(function (request, response) {
      // JadePage.jade 파일을 읽습니다.
      fs.readFile('JadePage.jade', 'utf8', function (error, data) {
        // jade 모듈을 사용합니다.
        var fn = jade.compile(data);
    
        // 출력합니다.
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end(fn());
      });
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    jade 기본 형식

    ejs 페이지가 HTML 페이지 위에 특수 태그를 몇 개 추가한 것처럼 jade 페이지는 특수한 형태의 HTML 페이지 위에 특수 태그를 몇 개 추가한 것입니다.

    jade 기본 형식에서 가장 중요한 것은 들여쓰기입니다. 들여쓰기는 탭과 띄어쓰기 중 한 가지 형태만 사용해야 합니다. 2가지 형태를 모두 사용하면 오류가 발생합니다.

    태그 안에 글자를 입력하고 싶을 때는 지정한 태그 아래에 한 단계 더 들여쓰기를 한 후 원하는 글지를 입력합니다. 또한 태그에 속성을 입력하고 싶을 때는 지정한 태그 뒤에 괄호를 사용하고 다음과 같은 방식으로 속성을 입력합니다. 태그에 속성을 여러 개 입력하고 싶을 때는 쉼표를 사용해 구분합니다.

    우선 그림과 같이 2개의 파일을 생성합니다.

    html
      head
        title Index Page
      body
        h1 Hello jade .. !
        h2 Lorem ipsum
        hr
        a(href="http://hanbit.co.kr", data-test="multiple Attribute") Go To Hanbit Media
    

    코드를 실행하고 http://127.0.0.1:52273에 접속하면 다음과 같은 HTML 페이지가 출력됩니다.

    이제 문서 형식과 주석을 지정하는 방법을 살펴봅니다.

    다음 코드에서 첫 줄에 추가된 문자열 doctype html은 <!DOTYPE html>로 변환되고 // JADE String은  으로 변환됩니다.

    doctype html
    html
      head
        title Index Page
      body
        // JADE String
        h1 Hello jade .. !
        h2 Lorem ipsum
        hr
        a(href="http://hanbit.co.kr", data-test="multiple Attribute") Go To Hanbit Media
    
    jade 특수 기호

    jade 페이지는 HTML 페이지 위에 다음의 특수 태그를 사용해 페이지를 구성합니다.

    태그 설명
    - Code 자바스크립트 코드를 입력합니다.
    #{Value} 데이터를 출력합니다.
    = Value 데이터를 출력합니다.
    서버 실행 모듈

    지금까지 살펴본 모듈은 지역 모듈로 자바스크립트 파일 내부에서 require() 함수로 추출했습니다. 지금부터 살펴보는 모듈은 전연 모듈로 터미널에서 곧바로 사용할 수 있는 모듈입니다.

    전역 모듈을 설치할 때는 -g 옵션을 사용합니다.

    supervisor 모듈

    supervisor 모듈은 파일의 변경 사항을 자동으로 인식하고 실행을 종료시킨 후에 다시 실행해줍니다.

    supervisor 모듈은 다음 명령어를 입력해 설치합니다. 전역 위치에 설치하므로 관리자 권한이 필요합니다.

    $ sudo npm install -g supervisor
    

    설치를 완료했으면 supervisor 명령어를 통해 기본 명령어를 확인해봅시다.

    $ supervisor
    

    이제 supervisor 모듈을 실행해보겠습니다.

    1. test.server.js 파일을 생성하고 코드를 작성합니다.
      // 모듈을 추출합니다.
      var http = require('http');
      
      // 서버를 생성 및 실행합니다.
      http.createServer(function (request, response) {
        response.writeHead(200, { 'Content-type': 'text/html' });
        response.end('<h1>Test - File - 1</h1>');
      }).listen(52273, function () {
        console.log('Server Running at http://127.0.0.1:52273');
      });
      
    2. 코드 작성이 끝나면 supervisor 모듈로 test.server.js를 실행하고, 실행 후 http://127.0.0.1:52273에 접속합니다.
    3. test.server.js 파일 코드를 변경합니다.
      // 모듈을 추출합니다.
      var http = require('http');
      
      // 서버를 생성 및 실행합니다.
      http.createServer(function (request, response) {
        response.writeHead(200, { 'Content-type': 'text/html' });
        response.end('<h1>Test - File - 2</h1>');
      }).listen(52273, function () {
        console.log('Server Running at http://127.0.0.1:52273');
      });
      
    4. http://127.0.0.1:52273에 재접속합니다, 파일을 저장하면 자동으로 supervisor 모듈이 서버를 재시작하게 합니다.
    forever 모듈

    기존 멀티스레드 기반의 웹 서비스는 예외가 발생해도 전체 웹 서비스에 크게 영향을 주지 않았습니다. 하지만 Node.js 같은 단일 스레드 기반의 웹 서비스는 예외 하나로 웹 서비스 전체가 죽어버립니다. 이러한 예외 상황을 대비하고자 만들어진 모듈이 forever 모듈입니다.

    forever 모듈을 사용하면 웹 서버를 안정적으로 유지할 수 있습니다.

    forever 모듈은 다음 명령으로 설치합니다.

    $ sudo npm install -g forever
    

    forever 모듈의 기본 명령어는 다음처럼 forever 명령어를 입력해서 확인할 수 있습니다.

    $ forever
    
    if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; ');\n response.write(' Forever NDS');\n response.write(' ');\n response.write('');\n response.write('

    Forever NDS

    ');\n response.write('');\n response.write('');\n response.end();\n } else {\n // 오류를 발합니다.\n error.error.error();\n }\n}).listen(52273, function () {\n console.log('Server Running at http://127.0.0.1:52273');\n});\n\n# forever.js 파일 실행\n$ forever start forever.js\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
    # forever.js 파일 생성
    $ sudo atom forever.js
    
    # forever.js 파일 작성
    // 서버를 생성 및 실행합니다.
    require('http').createServer(function (request, response) {
      if (request.url == '/') {
        // 응답합니다.
        response.write('<!DOCTYPE html>');
        response.write('<html>');
        response.write('<head>');
        response.write('  <title>Forever NDS</title>');
        response.write('</head>');
        response.write('<body>');
        response.write('  <h1>Forever NDS</h1>');
        response.write('</body>');
        response.write('</html>');
        response.end();
      } else {
        // 오류를 발합니다.
        error.error.error();
      }
    }).listen(52273, function () {
      console.log('Server Running at http://127.0.0.1:52273');
    });
    
    # forever.js 파일 실행
    $ forever start forever.js
    

    명령을 실행하고 http://127.0.0.1:52273에 들어가면 다음과 같은 결과가 출력됩니다.

    그러나 http://127.0.0.1:52273/error에 접속할 경우 루트 경로가 아닌 곳에 접근했으므로 서버에서 예외가 발생해 서버가 죽어버립니다.

    현재 실행되고 있는 웹 서버를 확인할 때는 list 명령어를 사용합니다.

    $ forever list
    

    현재 실행중인 웹 서버를 종료할 때는 프로세스의 번호로 서버를 종료합니다. 다음과 같이 stop 명령어를 사용합니다.

    $ forever stop 0
    
    기본적인 npm 명령어와 save 옵션
    npm init

    원래 프로젝트를 생성할 때는 npm init 명령을 사용합니다. 폴더 하나를 만들고 폴더 내부에서 npm init 명령을 입력해보면 다음과 같은 결과가 나옵니다.\

    $ sudo npm init
    

    프로젝트의 이름(name), 버전(version), 설명(description), 엔트리 포인트(entry point) 등을 요구합니다. 그냥 [Enter] 키를 눌러서 기본 값이 들어가게 합시다.

    명령을 실행한 이후에 해당 폴더를 보면 다음과 같이 package.json 파일이 생성되어 있습니다. 이 파일을 열어보면 다음과 같은 코드 내용이 있습니다.

    • package.json 파일의 기본적인 속성
    속 성 설 명
    name 이름을 적습니다.
    version 버전을 적습니다.
    description 설명을 적습니다.
    main 프로젝트의 메인 파일을 적습니다.
    author 누가 만들었는지 적습니다.
    license 라이선스를 적습니다.

    scripts 속성은 npm 명령어를 사용해 실행할 명령을 지정합니다.

    $ npm test
    

    현재 상태에서는 exit 1이라는 명령 때문에 불완전 상태로 끝나서 오류가 발생합니다.

    package.json 파일을 아래와 같이 수정하고 npm start 명령을 통해 실행합니다.

    {
      "name": "npm_test",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "echo \"Start Test\"",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC"
    }
    
    $ npm start
    
    모듈 버전 선택과 저정

    모듈의 버전을 선택해서 설치하고 싶을 때는 @ 기호를 사용합니다.

    $ npm install ejs@2.4.1       // 2.4.1 버전을 설치합니다.
    $ npm install ejs@2.3          // 2.3 버전 중에서 최신 버전을 설치합니다.
    $ npm install ejs@2             // 2 버전 중에서 최신 버전을 설치합니다.
    

    --save 옵션을 통해 내가 프로그램을 만들 때 어떤 버전을 사용했는지 명시할 수 있습니다.

    $ npm install --save jade
    $ npm install --save ejs@1
    

    참고로 dependencies 속성은 설치되어 있는 모듈의 종류와 버전 정보 등을 표시합니다. 다음과 같은 규칙으로 값을 적습니다.

    • dependencies 속성
    기 호 설 명
    * 해당 모듈의 어떠한 버전을 설치해도 상관 없습니다(따라서 npm install 명령을 입력했을 때 최신 버전이 설치됩니다.)
    > 1.0.0 1.0.0 버전보다 높은 버전이 설치됩니다.
    >= 1.0.0 1.0.0 버전보다 높거나 같은 버전이 설치됩니다.
    < 1.0.0 1.0.0 버전보다 낮은 버전이 설치됩니다.
    <=1.0.0 1.0.0 버전보다 낮거나 같은 버전이 설치됩니다.
    = 1.0.0 1.0.0 버전이 설치됩니다.
    ^1.0.0 해당 버전과 호환되는 버전이 설치됩니다(1.0.0 이상, 2.0.0 미만의 버전 중에서 최신 버전이 설치됩니다).

    npm 자체를 업데이트하고 싶을 때는 다음 명령을 사용합니다.

    $ sudo npm install -g npm
    

    express 모듈

    꼭 알아둘 개념

    개념 설명
    express 모듈 http 모듈처럼 사용할 수 있지만 훨씬 더 많은 기능이 있는 외부 모듈
    미들웨어 express 모듈 use() 메서드의 매개변수에 입력하는 함수
    router 미들웨어 페이지 라우팅을 지원하는 미들웨어
    static 미들웨어 지정한 폴더에 있는 내용을 모두 웹 서버 루트 폴더에 올릴 때 사용
    morgan 미들웨어 웹 요청이 들어왔을 때 로그를 출력
    cookie parser 미들웨어 요청 쿠키를 추출
    body parser 미들웨어 POST 요청 데이터 추출
    connect-multiparty 미들웨어 multipart/form-data 인코딩 방식을 사용해 POST 요청 데이터를 추출
    express-session 미들웨어 세션을 쉽게 생셩할 수 있게 도와줌
    RESTful 웹 서비스 일관된 웹 서비스 인터페이스 설계 기반의 REST 규정을 맞춰 만든 웹 서비스

    express 모듈은 외부 모듈로, 다음 명령으로 설치합니다.

    $ sudo npm install express@4
    
    기본 서버

    express 모듈은 express() 함수로 애플리케이션 객체를 생성하고, listen() 메서드로 웹 서버를 설정 및 실행합니다.

    # express.basic.js 파일 생성
    $ sudo atom express.basic.js
    
    # express.basic.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // request 이벤트 리스너를 설정합니다.
    app.use(function (request, response) {
      response.writeHead(200, { 'Content-type': 'text/html' });
      response.end('<h1>Hello express</h1>');
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.basic.js 파일 실행
    $ node express.basic.js
    

    코드를 실행하고 http://127.0.0.1:52273에 접속하면 다음과 같은 결과가 출력됩니다.

    기본 응답 메서드

    request 이벤트 리스너의 매개변수에는 request 객체와 response 객체가 들어갑니다. express 모듈로 서버를 생성하면 request 객체와 response 객체에 다양한 기능이 추가됩니다.

    • response 객체의 메서드
    메서드 이름 설 명
    response.send([body]) 매개변수의 자료형에 따라 적절한 형태로 응답
    response.json([body]) JSON 형태로 응답
    response.jsonp([body]) JSONP 형태로 응답
    response.redirect([status,] path) 웹 페이지 경로를 강제로 이동
    • send() 메서드의 매개변수
    자료형 설 명
    문자열 HTML
    배열 JSON
    객체 JSON

    그럼 send() 메서드를 사용해 보겠습니다.

    # express.send.js 파일 생성
    $ sudo atom express.send.js
    
    # express.send.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // request 이벤트 리스너를 설정합니다.
    app.use(function (request, response) {
      // 데이터를 생성합니다.
      var output = [];
      for (var i = 0; i < 3; i++) {
        output.push({
          count: i,
          name: 'name - ' + i
        });
      }
    
      // 응답합니다.
      response.send(output);
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.send.js 파일 실행
    $ node express.send.js
    

    코드 실행 후 http://127.0.0.1:52273에 접속하면 다음과 같이 JSON 형식으로 출력됩니다.

    기본 요청 메서드

    express 모듈을 사용하면 response 객체와 마찬가지로 request 객체에도 메서드와 속성이 추가됩니다.

    메서드/속성 이름 설 명
    params 라우팅 매개변수를 추출
    query 요청 매개변수를 추출
    headers 요청 헤더를 추출
    accepts(type) 요청 헤더의 Accept 속성을 확인
    |is(type) 요청 헤더의 Content-Type 속성을 확인
    요청 헤더의 속성 추출

    header() 메서드를 사용하면 손쉽게 요청 헤더의 속성을 지정하거나 추출할 수 있습니다.

    요청 헤더의 속성을 추출하면 웹 브라우저에 따라 별도의 처리를 할 수 있습니다. 다음 코드는 사용자의 웹 브라우저가 크롬인지 확인합니다.

    # express.userAgent.js 파일 생성
    $ sudo atom express.userAgent.js
    
    # express.userAgent.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(function (request, response) {
      // User-Agent 속성을 추출합니다.
      var agent = request.header('User-Agent');
    
      // 브라우저를 구분합니다.
      if (agent.toLowerCase().match(/chrome/)) {
        // 페이지를 출력합니다.
        response.send('<h1>Hello Chrome .. !</h1>');
      } else {
        // 페이지를 출력합니다.
        response.send('<h1>Hello express .. !</h1>');
      }
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.userAgent.js 파일 실행
    $ node express.userAgent.js
    

    코드를 실행한 후 웹 페이지에 접속하면 크롬일 경우 "Hello Chrome .. !" 문구가, 크롬이 아닌 다른 웹 브라우저로 접속하면 "Hello express .. !" 문구가 출력됩니다.

    이 예제를 활용하면 '사용자가 특정 웹 브라우저를 사용할 때'라는 필터 기능을 구현할 수 있습니다. 이를 구현하면 같은 경로로 접속해도 모바일과 데스크톱 페이지를 구분하는 것은 물론 robots.txt를 무시하는 검색 엔진이 무단으로 웹 페이지를 탐색하는 것을 막을 수 있고 간단한 해킹에도 대비할 수 있습니다.

    요청 매개변수 추출

    query 속성을 사용하면 요청 매개변수를 쉽게 추출할 수 있습니다.

    # express.query.js 파일 생성
    $ sudo atom express.query.js
    
    # express.query.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(function (request, response, next) {
      // 변수를 선언합니다.
      var name = request.query.name;
      var region = request.query.region;
    
      // 응답합니다.
      response.send('<h1>' + name + '-' + region + '</h1>');
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.query.js 파일 실행
    $ node express.query.js
    

    코드를 실행하고 http://127.0.0.1:52273/?name=NDS&region=Boramae에 접속하면 다음과 같은 결과가 출력됩니다.

    미들웨어 개요

    http 모듈과 express 모듈로 만든 서버의 가장 큰 차이점은 express 모듈은 request 이벤트 리스너를 연결하는 데 use() 메서드를 사용한다는 것입니다.

    use() 메서드는 여러 번 사용할 수 있습니다. use() 메서드의 매개변수에는 function(request, response, next) { } 형태의 함수를 입력합니다. 매개변수 next는 다음에 위치하는 함수를 의미합니다.

    요청 응답을 완료하기 전까지 요청 중간중가에 여러 가지 일을 처리할 수 있습니다. 그래서 use() 메서드의 매개변수에 입력하는 함수를 '미들웨어(middleware)'라고 부릅니다.

    미들웨어에서 request 객체와 response 객체에 속성 또는 메서드를 추가하면 다른 미들웨어에서도 추가한 속성과 메서드를 사용할 수 있습니다.

    # express.middleware.js 파일 생성
    $ sudo atom express.middleware.js
    
    # express.middleware.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(function (request, response, next) {
      // 데이터를 추가합니다.
      request.number = 52;
      response.number = 273;
      next();
    });
    
    // 미들웨어를 설정합니다.
    app.use(function (request, response, next) {
      // 응답합니다.
      response.send('<h1>' + request.number + ' : ' + response.number + '</h1>');
    });
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.middleware.js 파일 실행
    $ node express.middleware.js
    

    즉, 미들웨어를 사용하면 특정한 작업을 수행하는 모듈을 분리해서 만들 수 있습니다.

    • express 모듈과 함께 사용할 수 있는 미들웨어
    미들웨어 설 명
    router 페이지 라우트를 수행
    static 특정 폴더를 서버의 루트 폴더에 올림
    morgan 로그 정보를 출력
    cookie parser 쿠키를 분해
    body parser POST 요청 매개변수를 추출
    connect-multiparty POST 요청 매개변수를 추출
    express-session 세션 처리를 수행
    csurf CSRF 보안을 수행
    error handler 예외 처리를 수행
    limit POST 요청의 데이터를 제한
    vhost 가상 호스트를 설정
    router 미들웨어

    페이지 라우팅 기능은 express 모듈에 내장되어 있는 미들웨어의 기능으로 클라이언트 요청에 적절한 페이지를 제공하는 기술입니다.

    router 미들웨어는 다음과 같은 메서드를 사용할 수 있습니다.

    app 객체의 메서드 설 명
    get(path, callback[, callback ...]) GET 요청이 발생했을 때의 이벤트 리스너 지정
    post(path, callback[, callback ...]) POST 요청이 발생했을 때의 이벤트 리스너 지정
    put(path, callback[, callback ...]) PUT 요청이 발생했을 때의 이벤트 리스너 지정
    delete(path, callback[, callback ...]) DELETE 요청이 발생했을 때의 이벤트 리스너 지정
    all(path, callback[, callback ...]) 모든 요청이 발생했을 때의 이벤트 리스너 지정

    get() 메서드의 첫 번째 매개변수에는 요청 URL을 입력하고 두 번째 매개변수에는 요청 URL을 입력했을 때 실행할 이벤트 리스너를 입력합니다.

    express 모듈의 페이지 라우팅 기능은 토큰을 사용하는 기능이 있습니다.

    # express.get.js 파일 생성
    $ sudo atom express.get.js
    
    # express.get.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 라우터를 설정합니다.
    app.get('/page/:id', function (request, response) {
      // 변수를 선언합니다.
      var name = request.params.id;
    
      // 응답합니다.
      response.send('<h1>' + name + ' Page</h1>');
    });
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.get.js 파일 실행
    $ node express.get.js
    

    코드를 실행하고 http://127.0.0.1:52273/page/NDS_Intern에 접속하면 다음과 같은 결과가 출력됩니다.

    static 미들웨어

    static 미들웨어는 express 모듈 자체에 내장되어 있는 미들웨어로 웹 서버에서 손쉽게 파일을 제공할 수 있고, 사용 방법이 굉장히 간단합니다.

    전역 변수 __dirname을 사용해 폴더 위치를 지정하면 나머지는 express 모듈이 전부 알아서 해줍니다.

    static 미들웨어를 사용하면 지정한 폴더에 있는 내용을 모두 웹 서버 루트 폴더에 올립니다.

    morgan 미들웨어

    morgan 미들웨어는 웹 요청이 들어왔을 때 로그를 출력하는 미들웨어입니다. router 모듈과 static 모듈과는 다르게 외부 모듈이므로 따로 설치해야 합니다.

    다음과 같은 명령으로 미들웨어를 설치합니다.

    $ sudo npm install morgan
    

    이어서 다음과 같은 코드를 작성하면 morgan 미들웨어를 사용할 수 있습니다.

    # express.morgan.js 파일 생성
    $ sudo atom express.morgan.js
    
    # express.morgan.js 파일 작성
    // 모듈을 추출합니다.
    var express = require('express');
    var morgan = require('morgan');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(morgan('combined'));
    app.use(function (request, response) {
      response.send('<h1>express Basic</h1>');
    });
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    });
    
    # express.morgan.js 파일 실행
    $ node express.morgan.js
    

    코드를 실행하고 http://127.0.0.1:52273/에 접속하면 다음과 같은 결과가 출력됩니다.

    위의 코드에서는 morgan() 메서드의 매개변수에 'combined'를 입력했습니다. 이는 가장 기본적인 로그 형식이며, 그 외 원하는 토큰을 조합해서 로그를 원하는 형태로 변경할 수 있습니다.

    • morgan 미들웨어의 토큰
    토큰 설 명
    :req[header] 요청 헤더를 나타냄
    :res[header] 응답 헤더를 나타냄
    :http-version HTTP 버전을 나타냄
    :response-time 응답 시간을 나타냄
    :remote-addr 원격 주소를 나타냄
    :date[format] 요청 시간을 나타냄
    :method 요청 방식을 나타냄
    :url 요청 URL을 나타냄
    :referrer 이전 URL을 나타냄
    :User-Agent 사용자 에이전트를 나타냄
    :status 상태 코드를 나타냄
    • morgan 미들웨어의 기본 형식
    기본 형식 설 명
    combined :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
    common :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] |- |dev |:method :url :status :response-time ms - :res[content-length]
    dev :remote-addr - :remote-user :method :url HTTP/:http-version :status  :res[content-length]
    short :remote-addr - :remote-user :method :url HTTP/:http-version :status  :res[content-length] - :response-time ms
    tiny :method :url :status :res[content-length] :response-time ms

    cookie parser 미들웨어는 요청 쿠키를 추출하는 미들웨어입니다. cookie parser 미들웨어를 사용하면 request 객체와 response 객체에 cookies 속성과 cookie() 메서드가 부여됩니다.

    cookie parser 미들웨어는 다음 명령으로 설치합니다.

    • cookie() 메서드의 옵션 속성
    속성 이름 설 명
    httpOnly 클라이언트의 쿠키 접근 권한을 지정
    secure secure 속성을 지정
    expires expires 속성을 지정
    maxAge 상대적으로 expires 속성을 지정
    path path 속성을 지정
    $ sudo npm install cookie-parser
    
    body parser 미들웨어

    body parser 미들웨어는 POST 요청 데이터를 추출하는 미들웨어입니다. 이 미들웨어를 사용하면 request 객체에 body 속성이 부여됩니다.

    body parser 미들웨어는 다음 명령으로 설치합니다.

    $ sudo npm install body-parser
    

    cookie parser 미들웨어와 body parser 미들웨어를 사용하여 로그인을 구현하겠습니다.

    1. login.html 페이지를 작성합니다.
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n Login Page\n \n\n

      Login Page

      \n
      \n
      \n \n \n \n \n \n \n \n \n \n
      \n \n
      \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
        <title>Login Page</title>
      </head>
      <body>
        <h1>Login Page</h1>
        <hr />
        <form method="post">
          <table>
            <tr>
              <td><label>Username</label></td>
              <td><input type="text" name="login" /></td>
            </tr>
            <tr>
              <td><label>Password</label></td>
              <td><input type="password" name="password" /></td>
            </tr>
          </table>
          <input type="submit" name="" />
        </form>
      </body>
      </html>
      
    2. app.js 파일을 작성합니다.
      //모듈을 추출합니다.
      var fs = require('fs');
      var express = require('express');
      var cookieParser = require('cookie-parser');
      var bodyParser = require('body-parser');
      
      // 서버를 생성합니다.
      var app = express();
      
      // 미들웨어를 설정합니다.
      app.use(cookieParser());
      app.use(bodyParser.urlencoded({ extended: false }));
      
      // 라우터를 설정합니다.
      app.get('/', function (request, response) {
        if (request.cookies.auth) {
          response.send('<h1>Login Success</h1>');
        } else {
          response.redirect('/login');
        }
      });
      
      app.get('/login', function (request, response) {
        fs.readFile('login.html', function (error, data) {
          response.send(data.toString());
        });
      });
      
      app.post('/login', function (request, response) {
          // 쿠키를 생성합니다.
          var login = request.body.login;
          var password = request.body.password;
      
          // 출력합니다.
          console.log(login, password);
          console.log(request.body);
      
          // 로그인을 확인합니다.
          if (login == 'rint' && password == '1234') {
            // 로그인 성공
            response.cookie('auth', true);
            response.redirect('/');
          } else {
            // 로그인 실패
            response.redirect('/login');
          }
      });
      
      // 서버를 실행합니다.
      app.listen(52273, function () {
        console.log("Server Running at http://127.0.0.1:52273");
      })
      
    3. 코드를 실행하고 http://127.0.0.1:52273/login에 접속합니다.
    4. Login에 성공하면 다음과 같은 화면이 나옵니다.
      로그인에 성공하면 auth 쿠키가 생성되므로 웹 브라우저를 종료하고 다시 실행하기 전까지는 계속 로그인 상태를 유지합니다.
    connect-multiparty 미들웨어

    일반적인 입력 양식은 application/x-www-form-urlencoded 인코딩 방식을 사용합니다. 그런데 파일은 일반적인 입력 양식 데이터와 비교했을 때 용량이 큽니다. 따라서 웹 브라우저는 파일을 전송할 때 multipart/form-data 인코딩 방식을 사용합니다.

    body-parser 미들웨어는 multipart/form-data 인코등 방식을 지원하지 않습니다. multipart/form-data 인코딩 방식을 사용할 수 있게 해주는 미들웨어가 connect-multiparty 미들웨어입니다.

    connect-multiparty 미들웨어는 다음과 같은 명령으로 설치합니다.

    $ sudo npm install connect-multiparty
    
    express-session 미들웨어

    쿠키가 클라이언트의 웹 브라우저에 정보를 저장하는 기술이라면, 세션은 서버에 정보를 저장하는 기술입니다.

    express-session 미들웨어 (session 미들웨어)는 세션을 쉽게 생성할 수 있게 도와주는 미들웨어입니다. 다음과 같은 명령으로 설치합니다.

    $ sudo npm install express-session
    

    express-session 미들웨어를 사용하면 request 객체에 session 속성을 부여합니다.

    # express.session.js 파일 생성
    $ sudo atom express.session.js
    
    # express.session.js 파일 작성
    //모듈을 추출합니다.
    var express = require('express');
    var session = require('express-session');
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(session({
      secret: 'secret key',
      resave: false,
      saveUninitialized: true
    }));
    
    app.use(function (request, response) {
      // 세션을 저장합니다.
      request.session.now = (new Date()).toUTCString();
    
      // 응답합니다.
      response.send(request.session);
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log("Server Running at http://127.0.0.1:52273");
    })
    
    # express.session.js 파일 실행
    $ node express.session.js
    

    코드를 실행하고 http://127.0.0.1:52273/에 접속하면 다음과 같은 결과가 출력됩니다.

    개발자 도구를 사용해서 쿠키를 확인하면 connect.sid 쿠키가 생성된 것을 확인할 수 있습니다.

    브라우저를 종료하고 다시 실행하면 세션 식별자 쿠키가 소멸됩니다.

    express 모듈은 기본적으로 웹 브라우저가 켜져 있는 동안만 세션을 유지합니다. 만약 쿠키가 사라지는 시간과 쿠키의 name 속성을 바꾸고 싶다면 session() 메서드의 매개변수에 cookie 옵션 객체를 입력합니다.

    session() 메서드의 매개변수에 입력할 수 있는 옵션은 다음과 같습니다.

    • session() 메서드의 옵션
    옵션 이름 설 명
    name 쿠키의 name 속성을 지정
    store 세션 저장소를 지정
    cookie 생성할 cookie와 관련된 정보를 지정
    secret 비밀 키를 지정
    resave 세션이 변경되지 않았어도 세션 저장소에 반영(resave)할지 설정(true 또는 false 설정)
    saveUninitialized 초기화되지 않은 세션을 세션 저장소에 저아할지 설정(true 또는 false 설정)
    • session 객체의 메서드
    메서드 이름 설 명
    regenerate() 세션을 다시 생성
    destroy() 세션을 제거
    reload() 세션을 다시 불러옴
    save() 세션을 저장
    RESTful 웹 서비스 개발

    RESTful 웹 서비스는 REST(REpresentational State Transfer) 규정을 맞춰 만든 웹 서비스를 의미합니다.

    REST 규정은 일관된 웹 서비스 인터페이스 설계를 위해 만들어졌습니다.

    • RESTful 웹 서비스의 구조는 다음과 같습니다.
    경 로 /collection /collection/:id
    GET 방식 컬렉션을 조회 컬렉션의 특정 요소를 조회
    POST 방식 컬렉션에 새로운 데이터를 추가 사용하지 않음
    PUT 방식 컬렉션 전체를 한꺼번에 번경 컬렉션에 특정 요소를 수정
    DELETE 방식 컬렉션 전체를 삭제 컬렉션의 특정 요소를 삭제
    • RESTful 웹 서비스
    라우트 경 로 설 명
    GET /user 컬렉션의 특정 요소를 조회
    GET /user/:id 사용하지 않음
    POST 컬렉션 전체를 한꺼번에 번경 사용자를 추가
    PUT 컬렉션 전체를 삭제 컬렉션의 특정 요소를 수정
    DELETE /user/:id 특정 사용자 정보를 제거

    RESTful.js 파일을 생성하고 다음 코드를 저장합니다.

    //모듈을 추출합니다.
    var fs = require('fs');
    var express = require('express');
    var bodyParser = require('body-parser');
    
    // 더미 데이터베이스를 구현합니다.
    var DummyDB = (function () {
      // 변수를 선언합니다.
      var DummyDB = {};
      var storage = [];
      var count = 1;
    
      // 메서드를 구현합니다.
      DummyDB.get = function (id) {
        if (id) {
          // 변수를 가공합니다.
          id = (typeof id == 'string') ? Number(id) : id;
          // 데이터를 선택합니다.
          for (var i in storage) if (storage[i].id == id) {
            return storage[i];
          }
        } else {
          return storage;
        }
      };
    
      DummyDB.insert = function (data) {
        data.id = count++;
        storage.push(data);
        return data;
      };
    
      DummyDB.remove = function (id) {
        // 변수를 가공합니다.
        id = (typeof id == 'string') ? Number(id) : id;
        // 제거합니다.
        for (var i in storage) if (storage[i].id == id) {
          // 데이터를 제거합니다.
          storage.splice(i, 1);
          // 리턴합니다: 데이터 삭제 성공
          return true;
        }
        // 리턴합니다: 데이터 삭제 실패
        return false;
      };
    
      // 리턴합니다.
      return DummyDB;
    })();
    
    // 서버를 생성합니다.
    var app = express();
    
    // 미들웨어를 설정합니다.
    app.use(bodyParser.urlencoded({
      extended: false
    }));
    
    // 라우터를 설정합니다.
    app.get('/user', function (request, response) {
      response.send(DummyDB.get());
    });
    
    app.get('/user/:id', function (request, response) {
      response.send(DummyDB.get(request.params.id));
    });
    
    app.post('/user', function (request, response) {
      // 변수를 선언합니다.
      var name = request.body.name;
      var region = request.body.region;
    
      // 유효성을 검사합니다.
      if (name && region) {
        response.send(DummyDB.insert({
          name: name,
          region: region
        }));
      } else {
        throw new Error('error');
      }
    });
    
    app.put('/user/:id', function (request, response) {
      // 변수를 선언합니다.
      var id = request.params.id;
      var name = request.body.name;
      var region = request.body.region;
    
      // 데이터베이스를 수정합니다.
      var item = DummyDB.get(id);
      item.name = name || item.name;
      item.region = region || item.region;
    
      // 응답합니다.
      response.send(item);
    });
    
    app.delete('/user/:id', function (request, response) {
      response.send(DummyDB.remove(request.params.id));
    });
    
    // 서버를 실행합니다.
    app.listen(52273, function () {
      console.log('Server running at http://127.0.0.1:52273');
    });
    
    더미 데이터베이스 구현

    일반적으로 데이터 저장소로 MySQL같은 데이터베이스를 사용하지만 아직 MySQL을 배우지 않았으므로 더미 데이터베이스라는 가상의 데이터베이스를 만들어서 사용하겠습니다.

    • get() 메서드 : get() 메서드는 데이터를 조회하는 메서드입니다. 매개변수 id를 넣고 호출하면 특정 데이터 하나를 선택해서 리턴합니다. 반면 매개변수 id를 넣지 않고 호출하면 모든 데이터를 리턴합니다
      DummyDB.get = function (id) {
          if (id) {
            // 변수를 가공합니다.
            id = (typeof id == 'string') ? Number(id) : id;
      
            // 데이터를 선택합니다.
            for (var i in storage) if (storage[i].id == id) {
              return storage[i];
            }
          } else {
            return storage;
          }
        };
      
    • insert() 메서드 : insert() 메서드는 데이터를 추가하는 데 사용합니다. 데이터에 id 속성을 추가하고 storage 배열에 넣습니다. 모든 처리가 정상적으로 완료되면 자기 자신을 리턴합니다.
        DummyDB.insert = function (data) {
          data.id = count++;
          storage.push(data);
          return data;
        };
      
    • remove() 메서드 : remove() 메서드는 데이터를 제거하는 메서드입니다. 배열의 splice() 메서드를 사용해 특정 데이터를 제거합니다.
        DummyDB.remove = function (id) {
          // 변수를 가공합니다.
          id = (typeof id == 'string') ? Number(id) : id;
      
          // 제거합니다.
          for (var i in storage) if (storage[i].id == id) {
            // 데이터를 제거합니다.
            storage.splice(i, 1);
      
            // 리턴합니다: 데이터 삭제 성공
            return true;
          }
      
          // 리턴합니다: 데이터 삭제 실패
          return false;
        };
      
    GET 요청 
    app.get('/user', function (request, response) {
      response.send(DummyDB.get());
    });
    
    app.get('/user/:id', function (request, response) {
      response.send(DummyDB.get(request.params.id));
    });
    
    POST 요청 
    app.post('/user', function (request, response) {
      // 변수를 선언합니다.
      var name = request.body.name;
      var region = request.body.region;
    
      // 유효성을 검사합니다.
      if (name && region) {
        response.send(DummyDB.insert({
          name: name,
          region: region
        }));
      }else {
        throw new Error('error');
      }
    });
    

    Postman 크롬 애플리케이션으로 POST 요청을 보내면 데이터를 출력합니다.

    http://127.0.0.1:52273/user에 접속하면 다음과 같은 결과가 출력됩니다.

    PUT 요청 
    app.put('/user/:id', function (request, response) {
      // 변수를 선언합니다.
      var id = request.params.id;
      var name = request.body.name;
      var region = request.body.region;
    
      // 데이터베이스를 수정합니다.
      var item = DummyDB.get(id);
      item.name = name || item.name;
      item.region = region || item.region;
    
      // 응답합니다.
      response.send(item);
    });
    

    Postman 크롬 애플리케이션으로 PUT 요청을 보내면 다음과 같은 응답을 받습니다.

    (POST 요청을 먼저 하고 데이터가 있는 상태에서 PUT 요청을 보내야 에러가 나지 않음)

    DELETE 요청 
    app.delete('/user/:id', function (request, response) {
      response.send(DummyDB.remove(request.params.id));
    });
    

    코드를 실행하고 Postman 크롬 애플리케이션으로 다음과 같은 DELETE 요청을 보내면 아래와 같은 응답을 받습니다.

    MySQL 데이터베이스 

    꼭 알아둘 개념

    개념 설명
    CREATE 명령어 데이터베이스나 테이블을 생성
    INSERT 명령어 데이터를 입력할 때 사용
    SELECT 명령어 데이터를 조회할 때 사용
    UPDATE 명령어 데이터를 수정할 때 사용
    DELETE 명령어 데이터를 삭제할 때 사용
    mysql 모듈 Node.js 애플리케이션과 MySQL 데이터베이스를 연동할 때 사용

    MySQL 설치

    우분투에서는 다음 명령으로 MySQL을 설치할 수 있습니다.

    $ sudo apt-get install mysql-server mysql-client
    

    모든 설치가 정상적으로 완료되었다면 다음 명령으로 MySQL을 실행합니다.

    $ mysql -u root -p
    
    기본 명령어
    데이터베이스 생성

    데이터베이스를 생성할 때는 'CREATE BATABASE 이름' 형태의 쿼리 문장을 사용합니다.

    데이터베이스가 생성되면 'USE 이름' 쿼리 문장을 사용해 '데이터베이스를 사용하겠다'라고 선언합니다.

    mysql> CREATE DATABASE Company;
    mysql> USE Company;
    
    테이블 생성

    테이블을 생성할 때는 각 필드에 어떠한 자료형을 사용할 것인지 지정해야 합니다.

    • 테이블에 사용할 자료형
    자료형 설명
    VARCHAR 문자열
    INT 정수 숫자
    DOUBLE 실수 숫자

    테이블은 'CREATE TABLE 테이블명()' 형태의 쿼리 문장으로 테이블을 생성합니다.

    mysql> CREATE TABLE products (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    modelnumber VARCHAR(15) NOT NULL,
    series VARCHAR(30) NOT NULL
    );
    

    테이블을 생성했으면 'DESCRIBE 테이블명' 쿼리 문장을 사용해 테이블 필드와 관련된 정보를 살펴볼 수 있습니다.

    mysql> DESCRIBE products;
    
    데이터 저장

    테이블에 데이터를 입력할 때는 'INSERT INTO 테이블명 (필드, 필드) VALUE (데이터, 데이터)'과 같은 형태로 쿼리 문장을 사용합니다.

    mysql> INSERT INTO products (name, modelnumber, series) VALUES
    ('Eric Clapton Stratocaster', '0117602806', 'Artist');
    
    데이터 조회

    데이터를 조회할 때는 'SELECT 필드, 필드 FROM 테이블'과 같은 형태로 쿼리 문장을 사용합니다.

    mysql> SELECT * FROM products;
    
    조건 검사

    특정 조건이 있는 데이터를 선택할 때는 WHERE 명령어를 사용합니다. 'SELECT 필드, 필드 FROM 테이블 WHERE 조건'

    mysql> SELECT * FROM products
    WHERE series="Artist";
    

    만약 글자에서 특정 부분이 일치하는 데이터를 조회하고 싶을 때는 'LIKE' 명령어를 사용합니다.

    mysql> SELECT * FROM products
    WHERE modelnumber LIKE '001%';
    
    데이터 정렬

    데이터를 정렬할 때는 'ORDER BY ASC(오름차순)/DESC(내림차순)' 명령어를 사용합니다.

    mysql> SELECT * FROM products ORDER BY name ASC;
    mysql> SELECT * FROM products ORDER BY name DESC;
    
    특정 위치에 있는 데이터 선택

    테이블 안에서 특정 위치에 있는 데이터를 선택하고 싶을 때는 'LIMIT' 명령어를 사용합니다.

    mysql> SELECT * FROM products LIMIT 2;
    

    LIMIT 명령어 뒤에 숫자를 2개 입력하면 특정 위치에 있는 특정 개수 데이터를 선택할 수 있습니다.

    SELECT 명령어와 다른 명령어를 복합적으로 사용할 때는 명령어 순서를 꼭 지켜야 합니다. SELECT FROM WHERE ORDER BY LIMIT 명령어 순서로 입력해야 합니다.

    데이터 수정

    데이터를 수정할 때는 'UPDATE' 명령어를 사용합니다.

    mysql> UPDATE products
    SET name='American Deluxe Telecaster'
    WHERE id = 1;
    
    데이터 삭제

    데이터를 삭제할 때는 'DELETE' 명령어를 사용합니다.

    mysql> DELETE FROM products
    WHERE id = 10;
    
    mysql> DELETE FROM products;
    

    데이터를 수정할 때와 마찬가지로 WHERE 명령어를 함께 사용하지 않으면 모든 데이터가 삭제되므로 주의하세요.

    테이블이나 데이터베이스를 삭제할 때는 'DROP' 명령어를 사용합니다.

    mysql> DROP TABLE products;
    mysql> DROP DATABASE Company;
    
    mysql 모듈

    mysql 모듈을 설치하는 명령어는 다음과 같습니다.

    $ sudo npm install mysql
    
    • mysql 모듈의 메서드
    메서드 이름 설명
    createConnection(options) 데이터베이스에 접속합니다.
    • Connection 객체의 속성
    속성 이름 설명
    host 연결할 호스트를 나타냅니다.
    port 연결할 포트를 나타냅니다.
    user 사용자 이름을 나타냅니다.
    password 사용자 비밀번호를 나타냅니다.
    database 연결할 데이터베이스를 나타냅니다.
    debug 디버그 모드를 사용할지 나타냅니다.

    express 프레임워크

    꼭 알아둘 개념

    개념 설명
    express 프레임워크 express 모듈로 만든 프레임워크. 프로젝트를 손쉽게 만들어주며 기본 뷰 지원 등을 해줍니다.
    development 환경 개발 환경입니다.
    production 환경 실행 환경입니다.
    설치

    다음 명령으로 express 프레임워크를 설치합니다.

    $ sudo npm install -g express-generator@4
    

    express 프레임워크 설치가 완료됐으면 'express 프로젝트이름' 형태의 명령을 사용해 프로젝트를 생성합니다.

    $ sudo express HelloExpress
    
    프로젝트 생성 및 설정

    다음 명령으로 express 프레임워크의 도움말을 출력해봅니다.

    $ express --help
    
    • express 프레임워크 옵션
    매개변수 설명
    -h 또는 --help 도움말을 출력합니다.
    -v 또는 --version 프레임워크 버전을 출력합니다.
    ejs 템플릿 엔진을 사용합니다.
    --hbs handlebars 템플릿 엔진을 사용합니다.
    -h 또는 --hogan hogan.js 템플릿 엔진을 사용합니다.
    -c <engine> 또는 --css <engine> 스타일시트 엔진을 지정합니다.
    --git .gitignore 파일을 함께 생성합니다.
    -f 또는 --force 프로젝트가 이미 존재해도 새로 작성합니다.

    다음 명령은 템플릿 엔진으로 ejs 모듈을 사용하는 express 프로젝트를 생성하고, .gitignore 파일을 함께 생성합니다.

    $ sudo express -e --git Helloexpress
    기본 프로젝트

    폴더와 파일의 주요 역할은 다음과 같습니다.

    • bin 폴더는 프로그램의 실행과 관련된 파일이 들어있는 폴더입니다. 이 폴더 내부에 있는 www 파일을 실행해서 프레임워크를 실행합니다.
    • public 폴더는 express 모듈의 static 미들웨어를 사용해 웹 서버에 올라가는 폴더입니다. 이 폴더에 자바스크립트 파일, CSS 파일, 그림 파일 등 리소스 파일을 생성합니다.
    • routes 폴더는 페이지 라우트와 관련된 모듈입니다. routes 폴더에는 index.js 파일과 routes 파일이 있습니다.
    • views 폴더는 ejs 파일 또는 jade 파일과 같은 템플릿 파일을 저장하는 공간입니다.
    • app.js 파일은 프로젝트에서 중심이 되는 파일이면 package.json 파일은 현재 프로젝트와 관련된 정보와 모듈을 설치하는 데 필요한 내용을 담고 있습니다.
    실행 환경 설정

    express 프레임워크는 기본적으로 development와 production이라는 2가지 실행 환경을 제공합니다.

    • development 환경과 production 환경의 차이
    development 모드 production 모드
    오류 출력 출력 출력 안 함
    view cache 사용 안 함 사용
    디버그 모드 설정하면 사용 사용 불가

    development 환경은 기본 설정이며 다음과 같이 실행하면 됩니다.

    $ export NODE_ENV=development
    $ npm start

    production 환경으로 실행하고 싶을 때는 다음과 같이 node를 실행하기 전에 NODE_ENV 환경 변수에 production 값을 부여하면 됩니다.

    $ export NODE_ENV=production
    $ npm start

    socket.io 모듈

    꼭 알아둘 개념

    개념 설명
    웹 소켓 실시간 양방향 통신을 위한 스펙
    public 자신을 포함한 모든 클라이언트에 데이터를 전달
    broadcast 자신을 제외한 모든 클라이언트에 데이터를 전달
    private 특정 클라이언트에 데이터를 전달

    socket.it 모듈은 웹 소켓 서버를 쉽게 구현할 수 있게 도와주는 모듈입니다. socket.io 모듈은 자체적인 웹 소켓을 별도로 제공하므로 현존하는 웹 브라우저 대부분에서 웹 소켓을 사용할 수 있게 합니다.

    socket.io 모듈 기본

    socket.io 모듈은 다음 명령을 입력해 설치합니다.(1.X.X 버전을 설치합니다.)

    $ sudo npm install socket.io@1

    간단한 예제를 만들어봅시다. socket.io.server.js 파일은 웹 소켓 서버를 생성할 Node.js 코드고, HTMLPage.html 페이지는 웹 소켓 클라이언트 HTML 페이지입니다.

    웹 소켓 서버


    • socket.io 모듈의 메서드
    메서드 이름 설명
    listen() 서버를 생성 및 실행합니다.

    listen() 메서드의 매개변수에는 웹 소켓 서버 포트 번호를 입력할 수 있지만 일반적으로 웹 서버와 함께 사용하므로 다음 코드처럼 server 객체를 매개변수로 입력합니다.

    /***** socket.io.server.js *****/
    // 모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var socketio = require('socket.io');
    
    // 웹 서버를 생성합니다.
    var server = http.createServer(function (request, response) {
      // HTMLPage.html 파일을 읽습니다.
      fs.readFile('HTMLPage.html', function (error, data) {
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end(data);
      });
    }).listen(52273, function () {
      console.log('Server running at http://127.0.0.1:52273');
    });
    
    // 소켓 서버를 생성 및 실행합니다.
    var io = socketio.listen(server);
    io.sockets.on('connection', function (socket) {
    
    });
    

    디버그 모드로 실행하면 다음과 같은 결과가 출력됩니다.

    $ export DEBUG=socket.io*
    $ node socket.io.server
    웹 소켓 클라이언트

    이제 HTMLPage.html 파일을 작성합니다. io 객체의 connect() 메서드를 호출하면 자동으로 웹 소켓 서버에 연결됩니다.

    if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n \n \n \n\n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
    <!DOCTYPE html>
    <html>
    <head>
      <script src="/socket.io/socket.io.js"></script>
      <script>
        window.onload = function () {
    			// 소켓을 연결합니다.
    			var socket = io.connect();
        };
      </script>
    </head>
    <body>
    </body>
    </html>
    

    socket.io.server 코드를 실행하고 웹 브라우저로 http://127.0.0.1:52273/에 접속합니다. 웹 브라우저가 페이지를 불러오는 순간 HTMLPage.html의 클라이언트 소켓 연결 코드가 실행되고 콘솔 화면에 다음과 같이 클라이언트가 접속했음을 출력합니다.

    웹 소켓 이벤트

    이제 서버와 클라이언트 사이에 데이터를 교환해봅시다. socket.io 모듈은 서버와 클라이언트 사이에 데이터를 교환할 때 이벤트를 사용합니다.

    • socket.io 모듈의 이벤트
    이벤트 이름 설명
    connection 클라이언트가 연결할 때 발생합니다.
    disconnect 클라이언트가 연결을 해제할 때 발생합니다.
    • socket.io 모듈의 메서드
    메서드 이름 설명
    on() 소켓 이벤트를 연결합니다.
    emit() 소켓 이벤트를 발생시킵니다.

    클라이언트가 서버로 데이터와 함께 rint 이벤트를 발생시키면 서버는 smart 이벤트를 발생시켜 사용자가 전달한 데이터를 그대로 클라이언트에 전달하는 간단한 이벤트를 생성하겠습니다.

    1. socket.io.server.js 파일을 수정합니다. socket 객체에 rint 이벤트(사용자 정의 이벤트)를 연결하고 이벤트가 발생할 때 smart 이벤트를 클라이언트에 전달하도록 만듭니다.
      // 모듈을 추출합니다.
      var http = require('http');
      var fs = require('fs');
      var socketio = require('socket.io');
      
      // 소켓 서버를 생성 및 실행합니다.
      var server = http.createServer(function (request, response) {
        // HTMLPage.html 파일을 읽습니다.
        fs.readFile('HTMLPage.html', function (error, data) {
          response.writeHead(200, { 'Content-Type': 'text/html' });
          response.end(data);
        });
      }).listen(52273, function () {
        console.log('Server running at http://127.0.0.1:52273');
      });
      
      // 소켓 서버를 생성 및 실행합니다.
      var io = socketio.listen(server);
      io.sockets.on('connection', function (socket) {
        // rint 이벤트
        socket.on('rint', function (data) {
          // 클라이언트가 전송한 데이터를 출력합니다.
          console.log('Client Send Data:', data);
      
          // 클라이언트에 smart 이벤트를 발생시킵니다.
          socket.emit('smart', data);
        });
      });
      
    2. 이제 클라이언트 파일을 바꿔봅니다. HTMLPage.html 파일을 수정합니다.
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n \n \n \n\n \n \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          window.onload = function () {
            // 소켓을 생성합니다.
            var socket = io.connect();
      
            // 소켓 이벤트를 연결합니다.
            socket.on('smart', function (data) {
              alert(data);
            });
      
            // 문서 객체 이벤트를 연결합니다.
            document.getElementById('button').onclick = function () {
              // 변수를 선언합니다.
              var text = document.getElementById('text').value;
      
              // 데이터를 전송합니다.
              socket.emit('rint', text);
            };
          };
        </script>
      </head>
      <body>
        <input type="text" id="text" />
        <input type="button" id="button" value="echo" />
      </body>
      </html>
      
    3. socket.io.server.js 파일을 실행하고 http://127.0.0.1:52273/에 접속합니다.
    4. 적당한 글자(NDS Intern)을 입력하고 <echo> 버튼을 클릭하면 다음과 같이 경고 화면이 열립니다.
    소켓 통신 종류

    socket.io 모듈로 생성할 수 있는 소켓 통신 방법은 다음과 같은 3가지로 나눌 수 있습니다.

    통신 방법 이름 설명
    public 자신을 포함한 모든 클라이언트에 데이터를 전달
    broadcast 자신을 제외한 모든 클라이언트에 데이터를 전달
    private 특정 클라이언트에 데이터를 전달
    public 통신

    public 통신은 io.sockets 객체의 emit() 메서드를 사용해 smart 이벤트를 발생시키며 간단히 구현할 수 있습니다.

    # socket.io.public.js 파일 생성
    $ sudo atom socket.io.public.js
    
    /***** socket.io.public.js *****/
    // 모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var socketio = require('socket.io');
    
    // 소켓 서버를 생성 및 실행합니다.
    var server = http.createServer(function (request, response) {
      // HTMLPage.html 파일을 읽습니다.
      fs.readFile('HTMLPage.html', function (error, data) {
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end(data);
      });
    }).listen(52273, function () {
      console.log('Server running at http://127.0.0.1:52273');
    });
    
    // 소켓 서버를 생성 및 실행합니다.
    var io = socketio.listen(server);
    io.sockets.on('connection', function (socket) {
      // rint 이벤트
      socket.on('rint', function (data) {
        // public 통신
        io.sockets.emit('smart', data);
      });
    });
    

    코드를 실행하고 브러우저 여러 개를 실행해 http://127.0.0.1:52273/에 접속해 메시지를 전달합니다. 한 브라우저에서 <echo> 버튼을 클릭하는 순간 모든 웹 브라우저에서 경고 화면을 출력합니다.

    broadcast 통신

    broadcast 통신을 할 때는 socket 객체의 broadcast 속성을 사용합니다. broadcast 객체의 emit() 메서드를 사용하면 자신을 제외한 모든 사용자에게 이벤트를 전달할 수 있습니다.

    # socket.io.broadcast.js 파일 생성
    $ sudo atom socket.io.broadcast.js
    
    /***** socket.io.broadcast.js *****/
    // 모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var socketio = require('socket.io');
    
    // 소켓 서버를 생성 및 실행합니다.
    var server = http.createServer(function (request, response) {
      // HTMLPage.html 파일을 읽습니다.
      fs.readFile('HTMLPage.html', function (error, data) {
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end(data);
      });
    }).listen(52273, function () {
      console.log('Server running at http://127.0.0.1:52273');
    });
    
    // 소켓 서버를 생성 및 실행합니다.
    var io = socketio.listen(server);
    io.sockets.on('connection', function (socket) {
      // rint 이벤트
      socket.on('rint', function (data) {
        // broadcast 통신
        socket.broadcast.emit('smart', data);
      });
    });
    

    public 통신과 마찬가지로 브라우저를 여러 개 실행하고 http://127.0.0.1:52273/에 접속해 한 브라우저에서 메시지를 전달하는 순간 자신을 제외한 모든 웹 브라우저에 경고 화면을 출력합니다.

    private 통신

    간단하게 가장 최근 접속한 클라이언트 한 명에게 데이터를 전달하는 예제를 만들어봅시다.

    # socket.io.private.js 파일 생성
    $ sudo atom socket.io.private.js
    
    /***** socket.io.private.js *****/
    // 모듈을 추출합니다.
    var http = require('http');
    var fs = require('fs');
    var socketio = require('socket.io');
    
    // 소켓 서버를 생성 및 실행합니다.
    var server = http.createServer(function (request, response) {
      // HTMLPage.html 파일을 읽습니다.
      fs.readFile('HTMLPage.html', function (error, data) {
        response.writeHead(200, { 'Content-Type': 'text/html' });
        response.end(data);
      });
    }).listen(52273, function () {
      console.log('Server running at http://127.0.0.1:52273');
    });
    
    // 소켓 서버를 생성 및 실행합니다.
    var id = 0;
    var io = socketio.listen(server);
    io.sockets.on('connection', function (socket) {
      // id를 설정합니다.
      id = socket.id;
    
      // rint 이벤트
      socket.on('rint', function (data) {
        // private 통신
        io.sockets.to(id).emit('smart', data);
      });
    });
    

    코드를 실행하고 http://127.0.0.1:52273/에 접속해 메시지를 전달하면 가장 최근에 접속한 클라이언트에 경고 화면을 출력합니다.

    방 생성

    socket.io 모듈은 방(room/group)을 생성하는 기능을 포함하고 있습니다.

    • 방을 만들 때 사용하는 메서드 {| class="wikitable" !메서드 이름 !설명 |- |socket.join() |클라이언트를 방에 집어넣습니다. |- |io.sockets.in() / io.sockets.to() |특정 방에 있는 클라이언트를 추출합니다. |}

    join() 메서드와 in() 메서드는 각각 socket 객체와 io.sockets 객체에 존재하는 메서드입니다.

    1. socket.io.room.js 파일을 생성하고 작성합니다.
      $ sudo atom socket.io.room.js
      
      /***** socket.io.room.js *****/
      // 모듈을 추출합니다.
      var fs = require('fs');
      
      // 서버를 생성합니다.
      var server = require('http').createServer();
      var io = require('socket.io').listen(server);
      
      // 서버를 실행합니다.
      server.listen(52273, function () {
        console.log('Server Running at http://127.0.0.1:52273');
      });
      
      // 웹 서버 이벤트를 연결합니다.
      server.on('request', function (request, response) {
        // HTMLPage.html 파일을 읽습니다.
        fs.readFile('HTMLPage.html', function (error, data) {
          response.writeHead(200, { 'Content-Type': 'text/html' });
          response.end(data);
        });
      });
      
      // 소켓 서버 이벤트를 연결합니다.
      io.sockets.on('connection', function (socket) {
        // 방 이름을 저장할 변수
        var roomName = null;
      
        // join 이벤트
        socket.on('join', function (data) {
          roomName = data;
          socket.join(data);
        });
      
        // message 이벤트
        socket.on('message', function (data) {
          io.sockets.in(roomName).emit('message', 'test');
        });
      });
      
    2. HTMLPage.html 파일을 수정합니다.
      if (!window.T) { window.T = {} } window.T.config = {"TOP_SSL_URL":"https://www.tistory.com","PREVIEW":false,"ROLE":"guest","PREV_PAGE":"","NEXT_PAGE":"","BLOG":{"id":2858985,"name":"racoonlotty","title":"적어야 사는 너구리","isDormancy":false,"nickName":"로티이","status":"open","profileStatus":"normal"},"NEED_COMMENT_LOGIN":false,"COMMENT_LOGIN_CONFIRM_MESSAGE":"","LOGIN_URL":"https://www.tistory.com/auth/login/?redirectUrl=https://racoonlotty.tistory.com/entry/Nodejs-%25EC%258B%25A4%25EC%258A%25B5-%25EB%25B0%258F-%25EC%2598%2588%25EC%25A0%259C","DEFAULT_URL":"https://racoonlotty.tistory.com","USER":{"name":null,"homepage":null,"id":0,"profileImage":null},"SUBSCRIPTION":{"status":"none","isConnected":false,"isPending":false,"isWait":false,"isProcessing":false,"isNone":true},"IS_LOGIN":false,"HAS_BLOG":false,"IS_SUPPORT":false,"IS_SCRAPABLE":false,"TOP_URL":"http://www.tistory.com","JOIN_URL":"https://www.tistory.com/member/join","PHASE":"prod","ROLE_GROUP":"visitor"}; window.T.entryInfo = {"entryId":28,"isAuthor":false,"categoryId":730305,"categoryLabel":"IT/Node.js"}; window.appInfo = {"domain":"tistory.com","topUrl":"https://www.tistory.com","loginUrl":"https://www.tistory.com/auth/login","logoutUrl":"https://www.tistory.com/auth/logout"}; window.initData = {}; window.TistoryBlog = { basePath: "", url: "https://racoonlotty.tistory.com", tistoryUrl: "https://racoonlotty.tistory.com", manageUrl: "https://racoonlotty.tistory.com/manage", token: "oA2ggk9KA998aCuBZX4GoVfHmRrCHU/opR1WVjGL9FJmAVhc4ijVbQPZPZ7/B40t" }; var servicePath = ""; var blogURL = ""; \n \n \n \n \n \n\n \n\n\n"}}" data-ve-attributes="{"typeof":"mw:Extension/syntaxhighlight","about":"#mwt3"}">
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="utf8" />
        <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
          window.onload = function () {
            // 변수를 선언합니다.
            var room = prompt('방 이름을 입력하세요.', '');
            var socket = io.connect();
            // 소켓 이벤트를 연결합니다.
            socket.emit('join', room);
            socket.on('message', function (data) {
              $('<p>' + data + '</p>').appendTo('body');
            });
      
            // 문서 객체 이벤트를 연결합니다.
            document.getElementById('button').onclick = function () {
              socket.emit('message', 'socket.io room message');
            };
          };
        </script>
      </head>
      <body>
        <button id="button">EMIT</button>
      </body>
      </html>
      
    3. 코드를 실행하고 http://127.0.0.1:52273/에 접속하면 다음과 같이 방 이름을 입력하는 부분이 나옵니다.
      $ node socket.io.room.js
      
    4. 웹 브라우저 3개를 실행해 2가는 같은 방 이름을 입력하고 1개는 다른 방 이름을 입력하면 이벤트를 발생시켰을 때 같은 방의 브라우저에만 글자가 나타납니다.


    'IT > Node.js' 카테고리의 다른 글

    npm이란 무엇일까?  (0) 2021.02.18
    nvm 버전 고정하기  (0) 2020.12.02
    [JWT] 토큰(Token) 기반 인증에 대한 소개  (0) 2020.09.17
    JSON Web Token 소개 및 구조  (0) 2020.09.17
    nvm 설치  (0) 2020.06.08