Node.js는 Javascript로 개발한다. 아래 코드를 주목해보자.


'use strict';

try {
    null.error;
} catch (e) {
    console.error('catch error: ', e.stack);
}


평상시 에러 잡는 코드를 작성할 때 사용한다. 아무런 의구심도 들지 않는다.
자연스럽게 catch에서 실행될 것이란 걸 알기 때문이다.


아이러니하게도, 비동기 방식으로 개발하게 된다면 상황은 달라진다.
순차적으로 진행되는 코드에 익숙한 게 대부분이다. 들쑥날쑥 동작하는 코드를 보는 순간 당황할 수 있다.


'use strict';

const fs = require('fs');

try {
    fs.readFile('bad', (err) => {
        throw new Error('test error');
    });
} catch (e) {
    console.error('catch error: ', e.stack);
}


비동기 방식으로 파일을 읽고 있으며, 동일하게 에러를 발생시킨다.
catch로 들어오지 못하고, process exit 되는 상황에 놓이게 된다.
callback function은 try ~ catch scope에서 벗어나기 때문에 잡을 수 없다.


'use strict';

const fs = require('fs');

try {
    fs.readFile('bad', (err) => {
        try {
            throw new Error('test error');
        } catch (e) {
            console.error('fs catch error: ', e.stack);
        }
    });
} catch (e) {
    console.error('catch error: ', e.stack);
}


사용하던 대로, 벗어나고 있는 scope에 새로운 try ~ catch를 사용하면 생각대로 진행된다.


전적으로 의존한다면, try ~ catch 천국이 될 것이다.


좀 더 나은 방법으로 고민해보면, node.js의 domain module을 사용하면 된다.
현재도 Deprecated로 명시가 되어있으나, 마땅한 대안이 없어 유지되고 있는 것으로 보인다.


쉽게 보면, global scope, local scope 2가지가 전부다.


'use strict';

const fs = require('fs');
const domain = require('domain');

const fsProtect = domain.create();

fsProtect.on('error', (err) => {
    console.log(err);
    console.error('domain error: ', err.stack);
});

fsProtect.run(() => {
    fs.readFile('bad', (err) => {
        throw new Error('test error');
    });
});


run 함수는, try ~ catch scope와 동일하게 보면 이해가 쉽게 된다.
callback function의 영역도 scope에 포함 되기 때문에, error 이벤트로 진입하게 된다.


{ Error: test error
    at ReadFileContext.fs.readFile [as callback] (error.js:14:15)
    at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:365:13)
  domain:
   Domain {
     domain: null,
     _events: { error: [Function] },
     _eventsCount: 1,
     _maxListeners: undefined,
     members: [] },
  domainThrown: true }


error 객체를 자세히 살펴보면, 다른 점이 한가지 있다. run 함수 scope 안에서 에러가 발생하는 경우,
domain 객체가 property로 제공되며, _events에 error 이벤트가 등록되어 있다. 이 점 때문에 error 이벤트를 사전에 등록해줘야만 한다.


간편하게 사용할 수 있는 대표적인 방법이며, 대부분 위와 같은 방식을 사용한다.
한가지 의구심이 드는 부분은, 원하는대로 scope 조절은 불가능 하다는 것이다.


'use strict';

const fs = require('fs');
const domain = require('domain');

const fsProtect = domain.create();

fsProtect.on('error', (err) => {
    console.log(err);
    console.error('domain error: ', err.stack);
});

fs.readFile('bad', fsProtect.intercept((err) => {
    throw new Error('test error');
}));


try ~ catch scope를 생각해보자. 벗어나는 부분만, intercept 함수로 scope 영역을 확대했으며,
동일하게 error 이벤트가 등록되어 있다. scope 조절은 입맛에 맞게 할 수 있다.


일반적으로 프로젝트에 투입되게 되면, web server app, scheduler app을 개발하게 된다.


'use strict';

const http = require('http');
const server = http.createServer();

const domain = require('domain');

server.on('request', (req, res) => {
    const httpProtect = domain.create();

    httpProtect.add(req);
    httpProtect.add(res);

    httpProtect.on('error', (err) => {
        res.statusCode = 500;
        res.setHeader('Content-Type', 'text/plain');
        res.end(`${err.message}\n`);
    });

    httpProtect.run(() => {
        // throw new Error('test error');

        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World\n');
    });
});

server.listen(3000, '0.0.0.0', () => {
    console.log('server listening');
});


위 코드는 global scope 형태를사용하고 있는데, add 함수는 scope 영역에 객체를 포함한다는 의미이며, 에러가 발생하는 경우, 등록된 error 이벤트로 진입하게 된다.


추가로 고민해야 할 부분이 있는데, 주석을 풀고 요청을 해보자. process exit 될 것이다. error 이벤트로 진입 시 추가적인 에러가 발생했기 때문이다. 보통 error 처리 함수로 정의하고 구현하는데, scope에서 벗어난 영역이므로, 다시 scope를 정의하려고 하는 생각이 일반적이다.


'use strict';

const http = require('http');
const server = http.createServer();

const domain = require('domain');

server.on('request', (req, res) => {
    const httpProtect = domain.create();

    httpProtect.add(req);
    httpProtect.add(res);

    httpProtect.on('error', (err) => {

        const httpErrProtect = domain.create();
        httpErrProtect.add(req);
        httpErrProtect.add(res);

        httpErrProtect.on('error', (err) => {
            console.error(err);
        });

        httpErrProtect.run(() => {
            res.statusCode = 500;
            res.setHeader('Content-Type', 'text/plain');
            res.end(`${err.message}\n`);
        });
    });

    httpProtect.run(() => {
        throw new Error('test error');

        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World\n');
    });
});

server.listen(3000, '0.0.0.0', () => {
    console.log('server listening');
});


별로 문제가 없어 보이긴 한다. scope는 중첩되는 순간, 새로 선언한 scope로 error 이벤트는 등록된다. 상위 scope의 error 이벤트는 동작하지 않는다. run 함수 중첩사용은 시나리오를 예측해서 작성해야만, 대응이 수월할 것이며, 그렇지 않은 경우, error 이벤트의 호출 시점을 예측 할 수 없다.


'Nodejs' 카테고리의 다른 글

Node.js Package 취약점 분석하기  (0) 2017.08.16
Node.js 설치하기  (0) 2017.03.26
Node.js certificate has expired Error 해결하기  (0) 2016.06.17
Node.js Callback에 관한 고찰  (0) 2016.05.30
Node.js v6 버전 업그레이드 후기  (0) 2016.05.29

Http 인증 방식 중 주소를 기반으로 한 인증방식을 제공한다.


authority = [ userinfo "@" ] host [ ":" port ]


rfc3986의 3.2 Authority에 따르면 userinfo는 user:password 형태를 준수해야 하며, 일반문자열은 보안이 취약하므로, 암호화된 문자열을 사용해야 한다. 예시는 아래와 같다.


http://user:password@test.com


참고


CentOS6 환경에서 진행하며, nginx와 php는 php-fpm을 이용하여 연결된다.
설치는 yum을 이용한다.


  • Nginx 설치
sudo yum install nginx
vim /etc/nginx/nginx.conf
+location ~ \.php$ {
+    root           html;
+    fastcgi_pass   127.0.0.1:9000;
+    fastcgi_index  index.php;
+    fastcgi_param  SCRIPT_FILENAME  /usr/share/nginx/html$fastcgi_script_name;
+    include        fastcgi_params;
+}
sudo service nginx start

  • Php7 설치
sudo yum -y update
sudo yum -y install epel-release
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
wget https://centos6.iuscommunity.org/ius-release.rpm
sudo rpm -Uvh ius-release*.rpm
sudo yum -y update
sudo yum -y install php70u php70u-pdo php70u-mysqlnd php70u-opcache php70u-xml php70u-mcrypt php70u-gd php70u-devel php70u-mysql php70u-intl php70u-mbstring php70u-bcmath php70u-json php70u-iconv
vim /etc/php.ini
+ date.timezone = Asia/Seoul
+ cgi.fix_pathinfo=0
+ expose_php = Off


Error: Package: php70u-gd-7.0.18-1.ius.centos6.x86_64 (ius)
        Requires: libwebp.so.5()(64bit)

위와 같은 에러 발생 시 https://www.rpmfind.net/linux/rpm2html/search.php?query=libwebp.so.5()(64bit) 에서 lib64webp5-0.4.3-1.mga5.x86_64.rpm 다운로드 후 sudo yum install lib64webp5-0.4.3-1.mga5.x86_64.rpm 설치한다.


  • php-fpm 설치
sudo yum install php70u-fpm
sudo service php-fpm start

  • index.php 생성
sudo vim /usr/share/nginx/html/index.php
<?php
phpinfo()


http://localhost/index.php 접속하여 확인한다.


RancherOS 설치가 되지 않은 경우 RancherOS, Docker 소개 및 설치를 참고한다.


Rancher Server는 Docker를 GUI 환경에서 구성할 수 있게 도와준다. 사용하기 위해서 Ubuntu 16.04, Kernal 3.10+, Docker가 준비되어야 한다.


현재 windows, osx docker는 지원되지 않는다.


위처럼 각각 구성하지 않아도, RancherOS를 설치하면 사용할 수 있다. 앞서 소개한 글은 후자를 위한 것이다. Rancher Server도 Docker Image로 제공된다.


sudo docker run -d --restart=unless-stopped -p 8080:8080 rancher/server


위와 같이 설치를 진행한 후 Docker Container를 확인한다. 내부적으로 mysql을 사용하는데 container에 내장되어있으므로 신경 쓰지않아도 된다.


docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                              NAMES
7a3e5ba1e633        rancher/server      "/usr/bin/entry /u..."   About a minute ago   Up About a minute   3306/tcp, 0.0.0.0:8080->8080/tcp   serene_euler


로그 확인은 아래와 같다.


docker logs 7a3e5ba1e633 -f


정상적으로 서버가 구동되면 웹브라우저로 접속해보자.


스크린샷 2017-05-10 오전 3.07.48.png


Welcome to Rancher! 를 만나게 된다. Stack > All을 클릭하여 살펴보면, 기본적인 Docker Image가 설정되어있다. 정상적으로 구동하려면 Add Host를 클릭하여 Host 서버 설정을 완료해야 한다.


스크린샷 2017-05-10 오전 3.11.47.png


Host 서버에서 사용할 URL을 설정하고 Save 버튼을 클릭한다.


스크린샷 2017-05-10 오전 3.12.24.png


위 화면의 절차대로 수행한다. IPSec에서 UDP 500, 4500을 사용하므로 방화벽을 허용해줘야 하고, 5번은 기본적인 Docker Image를 수행하기 위한 rancher/agent 설치스크립트이므로, Copy & Paste 해서 실행한다.


스크린샷 2017-05-10 오전 3.16.42.png


모두 정상적으로 수행됐다면, 정상적으로 Active 상태가 된다. 이제 Docker Image를 만들 준비는 모두 완료되었다.


'DevOps' 카테고리의 다른 글

RancherOS, Docker 소개 및 설치  (0) 2017.05.10

RancherOS는 docker를 손쉽게 사용할 수 있도록 도와준다. RancherOS는 docker를 실행하기 위해 필요한 서비스만 포함하고 있기 때문에 가볍고, 불필요한 부분은 제거되어있다.


항상 최신 버전의 docker를 사용할 수 있고, 빠르게 부팅되는 특징을 가지고 있다. 5~10초 정도 소요된다.


docker는 개발자 및 시스템관리자 용도로 설계된 오픈 소스 플랫폼이며 Command Line Interface (CLI)를 사용하여 Container를 자유롭게 사용할 수 있다.


개발자가 RancherOS를 이해하고 docker를 자유롭게 사용할 수 있다면, 기술벤치마킹 및 성능 테스트 다양한 환경에서 좀 더 손쉽고 유연하게 대처할 수 있을 것이다.


손쉽게 가상환경을 만들 수 있는 VirtualBox를 통하여 설치해 보자.

사전에 rancheros.iso 파일을 내려받는다.


VitrualBox에서 가상머신을 만들어보자. 메모리 크기 및 하드디스크는 테스트환경에 따라 변경될 수 있다.


  • 이름: Rancheros
  • 종류: Linux
  • 버전: Other Linux (64 bit)
  • 메모리 크기: 4096MB
  • 하드디스크: 40GB

위 옵션을 사용하여 가상머신을 생성한 후 내려받은 rancheros.iso를 DVD/CD에 삽입한다.


스크린샷 2017-05-10 오전 2.17.19.png


가상머신을 시작하면 부팅이 완료된다.


VirtualBox_Rancheros_10_05_2017_02_19_41.png


rancheros는 ros명령어를 기반으로 동작하는데, 자세한 가이드 및 옵션은 문서를 참고하도록 한다.


sudo ros install -d /dev/sda -a rancher.password=password


설치 중 ssh-key 설정이 없으면, warn이 뜨게 되는데 초기비밀번호 설정이 있으므로 무시해도 된다. real pc에 usb로 설치할 때 static ip를 사용해야 하므로 난감한 상황에 맞닥뜨린 적이 있다. 위 명령어를 사용하여 설치 완료 후 reboot 시에는 DVD/CD에서 rancheros.iso를 제거한다.


VirtualBox_Rancheros_10_05_2017_02_40_15.png


정상적으로 설치되었다면, login 화면이 나타난다. 사용자: rancher, 초기비밀번호: password를 입력하여 접속하자. 이로써 설치는 모두 완료되었다.


'DevOps' 카테고리의 다른 글

Rancher Server 설치하기  (0) 2017.05.10

초록색을 보면 마음이 안정된다고 한다. 그렇기에 사람들은 등산을 많이 가는 걸까. 목이 말라 물을 벌컥벌컥 마시는 장면을 마주치곤 한다. 개발자도 새로운 업무를 받게 되면 어떤 프레임워크를 사용할지 찾아보곤 하는데...


보통 프레임워크는 반복되는 행위를 대신해준다. 어디까지 맡겨야 할까. 마음 같아서는 다 맡기고 싶지만, 비지니스적인 부분은직접 만들어야 한다.


너무 맡기다 보면, 새로운 상황에 직면한다. 프레임워크에 반대로 맞춰야 하는 상황이온다.
이 상황을 원하지는 않았을 것이다.


프레임워크를 선정하는 기준은, 어디까지 맡겨야 할지 정확하게 고민되어야 정할 수 있을 것이라는 생각이 든다.


개발자 하면 자동화란 단어가 같이 떠오른다. 일상생활에서 일어나는 많은 부분 중 반복되는 일이 생길 때, 업무로 다가오게된다.
업무를 파악하고, 요구사항을 도출한 후, 자연어를 프로그래밍언어로 번역하여, 컴퓨터에 전달하면 알아서 해준다. 얼마나 성취감 넘치는 일인가.


여기서 한가지 짚고 넘어가야 할 문제가 있다. 업무의 출발점은 어디인가. 개발언어 및 프레임워크만 능숙하게 다룬다면 전문가로 가는 길일까. 요구사항 없는 범용적인 공통라이브러리 개발은 종점이 어디인가.


업무에 임할 때 개발부터 시작하기보단, 자연어를 잘 파악하여 요구사항부터 정의하는 편이 시야를 넓게 하는 방법이 아닌가 하는 생각이 든다.


+ Recent posts