NPM (Node Package Manager)


패키지를 관리하는 매니저이며, 각 언어별로 pip, gem, maven등이 같은 역활을 수행하고 있습니다.

불편한점, 버그는 github, twitter를 사용하여 문의를 합니다. 필자가 사용을 시작했을때는 주로 NPM V2를 사용하였는데 dependence tree가 가장 큰 문제점이였습니다. "npm install package" 형태로 설치를 진행하는데 각 패키지가 하위로 꼬리를 물고 포함되어 있기 때문입니다.

아래 그림은 패키지의 관계를 설명하고 있습니다. A v1.0 → B v1.0 하위로 패키지가 의존하게 되는데, A → B → C → N 까지 의존하여도 제약이 없었고, N 까지 내려가는 경우, 너무 깊숙히 폴더를 생성하기 때문에 문제 발생은 빈번했습니다. 

이 점을 극복한게 NPM V3으로 패키지는 모두 동일선상에 위치하고, 버전이 다른경우에만 하위로 패키지를 이동시켜서 해소되었습니다.




패키지가 하위로 서로 의존하고 있기때문에, 하위 패키지가 취약점을 가지고 있는 경우 쉽게 발견하기가 어렵습니다. 패키지를 도입할때에는 "package.json"의 "dependencies"를 꼭 참고하여 추가확인하는 습관을 갖는편이 좋습니다.

NSP (Node Security Platform)


패키지의 취약점을 발견하여 알려주는 플랫폼입니다. 하위 패키지 정보를 모두 검색하여 취약점을 찾습니다.

프로젝트 경로에서 "nsp check" 명령어를 실행하면 하위 패키지와 관련정보가 기록된 주소를 출력합니다.


$ nsp check --output summary
(+) 1 vulnerabilities found
 Name    Installed   Patched   Path                                             More Info
 growl   1.9.2       None      mocha@3.5.0 > growl@1.9.2                        https://nodesecurity.io/advisories/14


웹 브라우저로 들어가서 확인하면 아래와 같은 화면이 나오는데, Command Injection 으로 분류되어 있고, Critical 9.8점을 기록하고 있습니다.

growl 라이브러리는 알람을 지원하는 기능을 지원하는데, 임의의 명령을 수행할 수 있는 취약점이 있습니다. 

취약점을 해결한 버전은 나오지 않았으며, 신뢰할 수 없는 명령어를 사용할 수 있다면 패키지 사용을 권장하지 않고 있습니다.

다른 취약점들도 https://nodesecurity.io/advisories 권고사항 목록을 참고하면 사전에 예방할 수 있을 것 입니다.



Retire 


NSP와 동일한 기능을 제공하며, 하위 패키지 정보를 모두 검색하여 취약점을 찾습니다.

프로젝트 경로에서 "retire" 명령어를 실행하면 동일하게 실행합니다.

출력방식만 다르고 관련정보는 nsp와 동일한 nodesecurity를 사용하고 있습니다.


$ retire
growl 1.9.2 has known vulnerabilities:  severity: high; summary: growl_command-injection; https://nodesecurity.io/advisories/146
growl 1.9.2


패키지를 도입할 때에는 마지막 Commit이 언제인지, 이슈는 정상적으로 처리되고 있는지 확인이 필요합니다.

위 조건이 성립했다면, NSP, Retire 툴을 이용하여 추가로 검사한다면 크리티컬한 위험요소는 많이 상쇄될 것으로 기대합니다.


관련 링크


  • npm: https://www.npmjs.com/
  • nodesecurity: https://nodesecurity.io


'Nodejs' 카테고리의 다른 글

Node.js Error 처리방법  (0) 2017.05.18
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

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

Node.js 다운로드


Windows 설치

다운로드 후 클릭 > Next 클릭



Linux 설치

wget https://nodejs.org/dist/v6.10.0/node-v6.10.0-linux-x64.tar.xz
tar xvf node-v6.10.0-linux-x64.tar.xz
mv node-v6.10.0-linux-x64 nodejs
mkdir bin
ln -s ../nodejs/bin/node node
ln -s ../nodejs/bin/npm npm
node -v


OSX 설치

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install node
node -v


N 설치

개발환경에서 유용하게 버전을 바꿔가며, 테스트하며 사용할 수 있다.

npm install -g n
sudo n 6.9.2
node -v

신뢰하지 않는 HOST로 판단하면, certificate has expired 에러가 발생한다. 테스트서버 구축 시 Openssl로 인증서를 생성해서 사용하기도 하는데 대부분 이런 경우이다. Node.js는 아래와 같이 환경변수를 지정하면 HOST를 판단하지 않는다.


process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';


'Nodejs' 카테고리의 다른 글

Node.js Error 처리방법  (0) 2017.05.18
Node.js 설치하기  (0) 2017.03.26
Node.js Callback에 관한 고찰  (0) 2016.05.30
Node.js v6 버전 업그레이드 후기  (0) 2016.05.29
NPMv3 사용하기  (0) 2016.04.04

Node.js로 개발한다 라고 말한다면, 누구나 난항을 겪게 된다. 한 번쯤은 들어봤을 것이다. Callback Hell에 관한 이야기이다.


asncFunction1(function(err, result) {
  asncFunction2(function(err, result) {
    asncFunction3(function(err, result) {
      asncFunction4(function(err, result) {
        asncFunction5(function(err, result) {
          // do something useful 
        })
      })
    })
  })
})


위 그림처럼 너무 많아져서 좌절하는 상황을 빈번하게 맞닥뜨리게 된다. 결국 키보드에 손을 놓고, 쉴 수밖에 없다.


무턱대고 라이브러리만 사용하다 보면, 순간 흐름을 놓치는 경우, 아무것도 할 수 없게 된다.
아래 주소는 2012년에 작성된 글인데, 설명이 잘 되어 있어서, 자주 보던 글이다.


주소 - http://book.mixu.net/node/ch7.html


흔히 사용하는 Series, Parallel에 관해 설명되어 있다. 한 번씩 따라 해본다면, 많은 도움이 될 것이다. 취향에 따라 Promise, Async 라이브러리로 나뉘는 것 같다. 두 가지 라이브러리를 다 사용해 보았지만, 결국 협업을 하거나, 인수인계를 하는 시점에서 어려움을 겪었다. 어떤 식으로 개발을 해야 쉽게 만들까.


입문자들은 Express.js Framework를 많이 경험한다. 책에 나오는 단골손님이기 때문이다.


function do(param, next) {

    // logic...

    next();
}


위와 같은 형태로 함수는 구성된다. 오히려 직관적이기 때문에 조금 응용해봤다.


function doA() {

    Model.getContent(param, function(err, data) {

        doB();
    });
}

function doB() {

    Model.getContent(param, function(err, data) {

        doC();
    });
}

function doC() {

    console.log('finish');
}

doA();


직접 필요한 함수를 호출해주는 방식이다. Callback 중첩도 되지 않으면서, 순차적으로 코드를 읽을 수 있다. 협업하면서 의견을 나누어 봤지만, 라이브러리를 사용해서 했을 때보단 진입장벽이 낮아진 것 같았다. 먼저 위와 같은 방법으로 경험하고, 필요한 때에만 라이브러리를 사용하면 개발이 한결 편해질 것 같다는 생각이 든다.



'Nodejs' 카테고리의 다른 글

Node.js 설치하기  (0) 2017.03.26
Node.js certificate has expired Error 해결하기  (0) 2016.06.17
Node.js v6 버전 업그레이드 후기  (0) 2016.05.29
NPMv3 사용하기  (0) 2016.04.04
Node.js와 ECMAScript 2015 (ES6)  (0) 2016.01.04

지금으로부터 한달전 2016년 4월 29일 Node.js v6이 출시됐다. 개발하고 있는 프로젝트는 LTS 버전인 v4를 사용하고 있었다.


ES6(Ecmascript6)을 개발할 때 적극적으로 도입하고 있었고, 개인적으로 마음에 들었던 부분은 자바스크립트의 단점인 변수의 범위를 좀 더 명확하게 지정할 수 있는 Block Scope, 기존 Function는 this를 자유자재로 변조할 수 있었다. Allow Function은 this가 할당되지 않기 때문에 명확하게 개발할 수 있다. v4는 문법 지원이 미미하므로, 아쉬운 점이 많았었는데, v6에서는 v8 엔진 5.0이 지원되기 때문에 ES6 문법을 93%까지 지원하게 되므로, 업그레이드를 고려하는 큰 이유가 되었다.


다음은 성능이다. 중요한 업그레이드 내용이며, v4보다 모듈 로딩속도가 4배 정도 빠르다고 한다. 기존 문법이 호환되기 때문에, v6으로 프로젝트 전체 테스트 시 큰 문제가 없다면, 업그레이드만 진행되어도 큰 성능향상을 확인할 수 있다. 테스트 진행은 mocha를 이용했고, jenkins에 연동했었다.


NPM(Node Package Manager)는 v4인 경우 v2로 설치가 되는데, 모듈 중복 등 문제점이 여럿 발견되었다. v3에서 대부분 해결되었는데, v6 업그레이드 시 v3으로 설치가 된다.


ES6, 성능을 충족했기 때문에 버전 업그레이드를 하기로 하였고, v4 -> v6 으로 진행하였다.


참고 사이트


NPMv2, NPMv3은 Dependencies를 제공하는 방법이 다르다. NPM에서 제공되는 모듈도 추가로 모듈을 사용할 수 있는데, 그 정보는 package.json에 고스란히 기록되어 있다. NPMv2는 각 모듈 폴더 기준 하위에 모듈이 설치 되고, NPMv3은 App 폴더 기준으로 해당 모듈이 있는지를 판단하여 없는 경우 모듈을 설치한다. 그로 인해 알아차릴 수 있는 변화가 있다.


NPMv2는 각 모듈 기준 설치가 되기 때문에 중복되는 모듈 코드의 양이 많은 편이며, 모듈에 모듈이 설치되는 깊이가 깊어질수록, 소스 형상관리에 문제점이 발생한다. 폴더의 깊이가 너무 심하게 들어가기 때문이다. NPMv3은 이런 부분을 개선하고자 App 폴더 기준으로 하위 모듈도 같이 관리 한다. 이미 설치가 되어있을 때에는 설치를 생략한다.



조건이 따르게 된다. 하위 모듈에 버전이 같은 경우에만 성립한다. 만약 그 점을 간과한다면, 모듈이 오작동 할 수도 있다. 해결을 위해서는 기존 NPMv2 방식도 병렬로 같이 사용한다.



App 폴더 기준 설치는 되어 있지만, 버전이 다른 경우에는 모듈 기준으로 설치된다. 이로써 버전에 대한 문제도 해결되고, 공통 하위 모듈은 일괄적으로 관리되기 때문에 전체적인 관리는 줄어들게 되고, 프로젝트의 효율도 올라가게 된다.


Node 버전 v4.x.x (LTS)는 NPMv2가 설치되어 있다. v5.x.x (stable)를 사용하지 않고 NPMv3을 사용하려면 npm install -g npm@3 명령어를 사용하자. npm -v 로 확인할 수 있을 것이다.


참고 사이트


Node.js는 ES6을 지원하고 있다. 고질적인 자바스크립트의 문제점을 보완하는 문법이 제공된다.


무턱대고 사용하다가는 큰 코 다칠 수 있다. 버전 별로 지원되는 기능이 차이가 있기 때문이다. 아래 링크는 지원되는 ES6의 목록이다. Server/runtimes 부분을 참고하자.


http://kangax.github.io/compat-table/es6/



Node.js는 아래와 같은 기능을 기본으로 제공하고 있다.



전체적으로 사용하기에는 시기상조이고, 개인적으로 현재 사용할만한 기능은 자바스크립트에서 항상 문제가 되어왔던 Scope 를 좀더 엄격하게 제한을 두는 문법인 Block scoping, 그리고 Connections 정도라고 생각한다.


참고 사이트


+ Recent posts