블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

Serveless를 위한 오픈소스 KNative #2 Eventing


조대협 (http://bcho.tistory.com)


knative의 다른 모듈로써는 비동기 메세지 처리를 위한 eventing 이라는 모듈이 있다. 카프카나, 구글 클라우드 Pub/Sub, AWS SQS와 같은 큐에서 메시지를 받거나 또는 Cron과 같은 타이머에서 이벤트가 발생하면 이를 받아서 처리할 수 있는 비동기 메커니즘을 제공하는 모듈이라고 보면 된다.


메시지 큐나 cron 과 같이 이벤트를 발생 시키는 자원들은 knative에 event source 라는 Custom Resource로 등록이 되고, 등록된 event source는 이벤트가 발생되면 지정된 knative 서비스로 이벤트 메시지를 HTTP로 전송한다. 이때 이벤트를 받는 knative 서비스는 앞에서 언급한 knative serving의 서비스이다. 이때 이벤트에 대한 스펙은 CNCF Serverless WG 에서 정의한 CloudEvents 스펙에 기반한다.

Hello Eventing

자세하게 Eventing에 대해서 알아보기 전에 간단한 예제를 살펴보자. 예제는 knative.dev의 cronjob  예제이다.  Crontab으로 이벤트를 생성하면, event-display 라는 서비스에서 이 이벤트를 받아서 이벤트의 내용을 간략하게 로그로 출력하는 예제이다.


먼저 이벤트를 읽어드릴 event-display 서비스를 배포하자. 해당 서비스는 HTTP post로 받은 이벤트의 내용을 log로 출력해주는 코드로 이벤트의 포맷은 앞에서 설명한 CloudEvent의 포맷을 따른다.

Go 로 구현된 코드이며, 코드 원본은 여기에 있다.

 해당 컨테이너를 배포하기 위해서 아래와 같이 service.yaml 파일을 만들고, kubectl apply -f service.yaml 을 이용해서 배포하면, crontab 에서 이벤트를 받는 serving 인스턴스가 준비된다.

apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: event-display

spec:

 runLatest:

   configuration:

     revisionTemplate:

       spec:

         container:

           image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display

<그림. Event consumer용 knative 서비스 배포>


다음 Crontab event 소스를 아래와 같이 yaml로 정의한다.


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: CronJobSource

metadata:

 name: test-cronjob-source

spec:

 schedule: "*/2 * * * *"

 data: '{"message": "Hello world!"}'

 sink:

   apiVersion: serving.knative.dev/v1alpha1

   kind: Service

   name: event-display

<그림. Crontab event source 정의>


spec>schedule 부분에 이벤트 주기에 대한 설정을 crontab 포맷을 따라서 하고, data 부분에 cron 이벤트가 발생할때 마다 보낼 데이타를 정의한다.

데이타를 보낼 목적지는 sink 부분에 지정하는데, kind에 타입을 정의하고 (여기서는 knative의 Service로 지정) 그리고 service 의 이름을 name에 정의한다. 앞에서 knative serving 서비스를 event-display로 지정하였기 때문에, 서비스명을 event-display로 정의한다.

yaml 파일 설정이 끝났으면 kubectl apply -f  명령을 이용해서 이벤트 소스를 등록하고, 동작을 하는지 확인해보도록 하자.


%kubectl logs -l serving.knative.dev/service=event-display -c user-container --since=10m


명령을 이용하면 앞에서 배포한 event-display 서비스의 로그를 볼 수 있는데, 결과를 보면 다음과 같다.



Data 부분에서 crontab 이벤트 소스에서 보내온 “message”:”Hello world!” 문자열이 도착한것을 확인할 수 있다.

Eventing detail

이벤트는 앞의 예제에서 본것과 같이 이벤트 소스에서 바로 Knative 서빙에서 받아서 처리하는 가장 기본적인 비동기 이벤트 처리 패턴이다.


Broker & Trigger

이러한 패턴이외에도 좀 더 다양한 패턴 구현이 가능한데, 두번째가 Broker와 Trigger이다. Broker는 이벤트 소스로 부터 메시지를 받아서 저장하는 버킷 역할을 하고, Broker에는 Trigger를 달 수 있는데, Trigger에는 메시지 조건을 넣어서, 특정 메시지 패턴만 서비스로 보낼 수 있다. 위의 패턴에서 필터를 추가한 패턴으로 보면 된다.



이해를 돕기 위해서 예제를 보자. 다음은 knative.dev 공식 사이트에 나와 있는 예제중에, Google Cloud Pub/Sub Source를 Broker로 연동하는 예제이다.


# Replace the following before applying this file:

#   MY_GCP_PROJECT: Replace with the GCP Project's ID.


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: GcpPubSubSource

metadata:

 name: testing-source

spec:

 gcpCredsSecret:  # A secret in the knative-sources namespace

   name: google-cloud-key

   key: key.json

 googleCloudProject: MY_GCP_PROJECT  # Replace this

 topic: testing

 sink:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Broker

   name: default

<그림. github-pubsub-source.yaml>


위의 코드는 GCP Pub/Sub Source를 등록하는 부분인데, sink 부분은 이 소스에서 오는 메시지를 어디로 보낼지를 정하는 부분이다. 위에 보면 Broker로 보내는것을 볼 수 있다. Broker는 Default Broker로 보낸다.


다음은 Broker에서 받은 메시지를 Trigger 조건에 따라서 Knative Serving 서비스로 보내는 설정이다.


apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: event-display

spec:

 template:

   spec:

     containers:

     - # This corresponds to

       # https://github.com/knative/eventing-sources/blob/release-0.5/cmd/event_display/main.go           

       image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d


---


# The GcpPubSubSource's output goes to the default Broker. This Trigger subscribes to events in the

# default Broker.


apiVersion: eventing.knative.dev/v1alpha1

kind: Trigger

metadata:

 name: gcppubsub-source-sample

spec:

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: event-display


< 그림. Trigger와 이벤트 메시지를 수신하는 Service를 정의한 부분>


서비스는 event-display라는 서비스를 정의하였고, 그 아래 Trigger 부분을 보면 gcppubsub-source-sample 이라는 이름으로 Trigger를 정의하였다. Broker 명을 정의하지 않으면 이 Trigger는 default broker에 적용된다. 별다른 조건이 없기 때문에, Broker의 모든 메시지를 대상 서비스인 event-display로 전달한다.

Channel & subscription

다음 개념은 Channel과 subscription 이라는 개념인데, Channel을 메시지를 저장 후에, Channel에 저장된 메시지는 메시지를 수신하는 Subscription을 통해서 다른 Channel로 포워딩 되거나 또는 Service로 전달 될 수 있다.



<그림. Channel과 Subscription 개념도>


앞에서 Channel에서는 메시지를 저장한다고 했는데, 그러면 저장할 장소가 필요하다. 저장할 장소는 설정으로 다양한 메시지 저장소를 사용할 수 있는데, 현재 메모리, Apache Kafka 또는 NATS Streaming을 지원한다.


간단한 예제를 살펴보자 예제는 이 문서를 참고하였다

먼저 아래 설정을 보자


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: GcpPubSubSource

metadata:

 name: testing-source

spec:

 gcpCredsSecret:  # A secret in the knative-sources namespace

   name: google-cloud-key

   key: key.json

 googleCloudProject: knative-atamel  # Replace this

 topic: testing

 sink:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test



< 그림. GCPPubSub Event Source 정의한 코드>


위 설정은 GCP Pub/Sub을 Event source로 등록하는 부분이다. 이벤트 소스로 등록 한후에, 이벤트를 sink 부분에서 pubsub-test라는 Channel로 전달하도록 하였다.

다음 아래는 Channel을 정의한 부분인데, pubsub-test 라는 이름으로 Channel을 정의하고 "provisioner” 부분에, 메시지 저장소를 "in-memory-channel” 로 지정해서 메모리에 메시지를 저장하도록 하였다.

apiVersion: eventing.knative.dev/v1alpha1

kind: Channel

metadata:

 name: pubsub-test

spec:

 provisioner:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: ClusterChannelProvisioner

   name: in-memory-channel

< 그림. Channel 정의한 코드>



apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: message-dumper-csharp

spec:

 runLatest:

   configuration:

     revisionTemplate:

       spec:

         container:

           # Replace {username} with your actual DockerHub

           image: docker.io/{username}/message-dumper-csharp:v1

---

apiVersion: eventing.knative.dev/v1alpha1

kind: Subscription

metadata:

 name: gcppubsub-source-sample-csharp

spec:

 channel:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: message-dumper-csharp

< 그림. Serving과 subscription을 정의 코드>


Channel에 저장된 메시지를 다른 Channel로 보내거나 또는 Service로 보내려면 Subscription을 거쳐야 한다. 위에서 gcppubsub-source-sample-charp이라는 subscription을 정의하였고, 이 subscription이 연결되는 Channel은 spec > channel 부분에 아래와 같이 정의 하였다.


aspec:

 channel:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test

< 그림. 위의 Subscription 정의에서 Channel 정의 부분>


그리고 그 채널에서 받은 메시지를 subscriber > ref 부분에서 아래와 같이 message-dumper-charp이라는 서비스로 포워딩 하도록 하였다.

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: message-dumper-csharp

< 그림.위의 Subscription 정의에서 Service 정의 부분>


전체적으로 Eventing 모듈을 이해하는데 시간이 많이 걸렸는데, Eventing 모듈은 Serving 모듈에 비해서 예제가 적고, 공식 문서에 아직 설명이 부족하다. 예를 들어서 소스 → 서빙으로 메시지를 보낼때 스케일링할 경우 문제가 없는지. Channel → subscription 으로 메시지를 보낼때 Trigger를 사용할 수 있는지 등 정보가 아직 부족해서 자세한 분석이 어려웠다. Knative는 현재 0.5 버전으로 버전이고, Event Source 들도 아직 개발 단계가 아니라 PoC (Proof Of Concept : 기술적으로 가능한지 테스트를 하는 단계) 단계 이기 때문에 제대로 사용하기에는 시간이 더 걸릴 듯 하다.

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

빠르게 훝어보는 node.js

#3 - Event,Module,NPM

조대협 (http://bcho.tistory.com

비동기 이벤트 프로그래밍

기존의 프로그래밍 언어들은 일반적으로 함수를 부르는 형태의 프로그래밍 구조를 가지고 있다. 이를 procedural programming model이라고 하는데코드가 순차적으로 실행되면서 함수를 호출하는 식의 구조를 가지고 있기 때문에 코드를 보면 코드의 수행 순서를 예측할 수 있다.

 

node.js event driven programming 이라는 개념을 가지고 있는데, 이 개념은 특정 이벤트가 발생되면 미리 이벤트에 맵핑된 함수가 실행되는 형태이다. 즉 해당 함수가 언제 호출 되는지를 예측할 수 가 없다.

var callback = function(data){

        console.log("call back has been called "+data);

}

 

$.get('/endpoint',callback);

 

위의 코드에 있는 callback 이라는 함수는 HTTP GET /endpoint request가 발생할 때만 수행된다.

  cf. procedural programming의 경우 함수는 코드상에서 명시적으로 호출을 해줘야 발생하지만, event driven programming은 이벤트에 의해서 함수가 호출 된다.

이러한 이벤트 방식의 유사한 사례는 윈도우즈나, 자바 SWING과 같은 GUI 계통의 프로그래밍에서도 찾아볼 수 있다. Mouse Click이나 Button Click 같은 이벤트에, callback 함수를 Binding 시켜놓는 형태에서 볼 수 있다.

 

다음으로 node.js 특징은 비동기 프로그래밍 방식이 라는 것인데, 앞서 설명한 바와 같이 node.js는 비동기식 IO를 이용한다. IO 요청을 보내놓고, 코드를 blocking 상태에서 기다리는 것이 아니라 다음 코드로 진행한 다음, IO 가 끝났다는 이벤트가 오면, 미리 지정해놓은 함수를 실행하는 형태이다. 이렇게 함수를 호출한후, 작업이 끝난 후에, 호출되도록 정의한 함수를 “callback”함수라고 한다.

 

아래 코드를 보자

var fs = require('fs');

 

var contents = fs.readFile('hello.txt','utf-8',function(err,contents){

        console.log('read 1:'+contents);

});

이 코드는 hello.txt 라는 파일을 읽는 코드인데, 맨 뒤에 function(err.contents)라는 함수를 정의했다. 이 함수는 파일을 다 읽었을때 호출되는 callback 함수이다. fs.readFile을 호출하면, node는 파일이 다 읽을때 까지 이 코드에서 block되어 있는 것이 아니라 다음코드로 진행을 한다음, 파일을 다 읽으면 이벤트를 발생시켜서 여기에 연결된 function(err,contents)를 수행하게 되는 것이다.

 

node.js를 공부하다보면, 가장 큰 진입장벽중의 하나가, javascript node.js의 라이브러리를 새롭게 배우는 것보다, 기존의 procedural programming model에서 이러한 event driven programming의 개념을 익히는 것이 더 어렵다.

 

Event Emitter

그러면 이러한 이벤트를 어떻게 정의하고 처리할까? node.js에서 이벤트를 발생시키고 처리하는 방식은 EventEmitter 객체를 상속 받아서 구현한다.

 

라디오객체를 만들어서, on,off,change channel 이라는 이벤트가 발생했을때 각각 radoTurnOnCallBack,radioChangeChannelCallback, radioTurnOffCallback 함수가 각각 호출 되도록 해보자

 

먼저 Radio 객체를 만들어 보자

Radio = function(){

    events.EventEmitter.call(this); // call super class constructor

};

다음으로 Radio 객체를 EventEmitter로 부터 상속 받도록 하자.

클래스의 상속은 util 모듈의 inherits 메서드를 사용하면 된다.

util.inherits(Radio,events.EventEmitter);

 

다음으로, 호출될 callback 함수를 정의한 다음

// this is listener

var radioTurnOnListener = function(){

        util.debug('Radio turned on!!')

    }

var radioChangeChannelListener = function(channel){

        util.debug('Channel has been changed to '+ channel);

    }

var radioTurnOffListener = function(){

        util.debug('Radio turned off!!')

    }

radio 객체를 만들어서, 이 객체에 각각의 이벤트를 바인딩해보자

이벤트에 대한 바인딩은 emitter객체.on(‘이벤트명’,callback함수); 식으로 정의하면 된다.

radio.on('turnon',radioTurnOnListener);

radio.on('changechannel', radioChangeChannelListener);

radio.on('turnoff', radioTurnOffListener);

또는 event 바인딩시에, 함수명 대신 직접 함수를 다음과 같이도 정의할 수 있다.

radio.on('turnon', function(){

        util.debug('Radio turned on!!')

    });

 

이제 event가 바인딩 된 radio 객체가 생성되었다. 이제 이 객체에 이벤트를 날려보자

radio.emit('turnon');

radio.emit('changechannel');

radio.emit('turnoff');

다음은 실행 결과 이다.



전체 소스코드

var events = require('events');

var util = require('util');

 

// This is object that generate(emit) events

var Radio = function(){

    events.EventEmitter.call(this); // call super class constructor

 

};

util.inherits(Radio,events.EventEmitter);

 

// this is listener

var radioTurnOnListener = function(){

        util.debug('Radio turned on!!')

    }

var radioChangeChannelListener = function(channel){

        util.debug('Channel has been changed to '+ channel);

    }

var radioTurnOffListener = function(){

        util.debug('Radio turned off!!')

    }

 

var radio = new Radio();

 

radio.on('turnon',radioTurnOnListener);

radio.on('changechannel', radioChangeChannelListener);

radio.on('turnoff', radioTurnOffListener);

 

radio.emit('turnon');

radio.emit('changechannel');

radio.emit('turnoff');

 

Event Emitter methods

그러면 EventEmitter method들을 살펴보자..

Ÿ   emitter.addListener(eventname,listener function)

Ÿ   emitter.on(eventname,listener function)

이 메서드들은 eventname에 해당하는 이벤트에 대해서 ‘listener function’ 이름의 함수가 매번 호출 되도록 한다. 이벤트에 함수를 binding 할때는 하나의 이벤트에 여러개의 listener를 바인딩 할 수 있으며, 최대 바인딩 개수는 디폴트 값은 10개이다.

Ÿ  emitter.once(eventname,listener function)

이 메서드들은 eventname에 해당하는 이벤트에 대해서 ‘listener function’ 이름의 함수가 처음 한번만 호출 되도록 한다.

Ÿ  emitter.removeListener(eventname,listener function)

이 메서드는 “eventname”에 바인딩 되어 있는 “listener function” 이름의 함수와의 binding을 제거한다.

Ÿ  emitter.removeAllListener([eventnames])

인자는 배열형으로, 배열내에 들어가 있는 “eventnames”에 각각 바인딩 된 모든 함수에 대한 바인딩을 제거한다.

Ÿ  emitter.setMaxListeners(n)

해당 eventEmitter에 바인딩될 수 있는 이벤트의 수를 조정한다.

Ÿ  emitter.listeners(event)

“event”이름의 이벤트에 바인딩된 모든 callback 함수 이름을 리턴한다.

Ÿ  emitter.emit(eventname,[args])

“eventname”의 이벤트를 생성하고, 이벤트를 생성할 당시 [args]에 정의된 값 들을 이벤트와 함께 전달한다.

Module

모듈은 개념은, 다른 파일에서 모듈을 불러다 쓸 수 있는 일종의 라이브러리 개념이다.

java import되는 다른 클래스나 C에서 #include 되는 라이브러리의 개념을 생각하면 된다.

모듈은 파일 단위로 구현되는데, export를 이용하여, 외부에 노출된다. 마치 java class public method와 같은 개념으로 생각하면 된다. 해당 파일에 있는 함수라도 exports를 하지 않으면, 외부에서 호출할 수 없다. (일종의 java class private과 같은 개념)

Hello.js 파일에 hello라는 함수가 있고, 이를 다른 파일에서 불러쓰고 싶다면, Hello.js 파일에서 다음과 같이 정의한후

var hello = function(){...}

exports = hello;

hello 함수를 사용하고자 하는 파일 (예를 들어 app.js에서) require 를 이용해서 모듈을 불러오고, 호출해서 사용한다.

var hello = require('./Hello');

hello();

require에는 사용하고자 하는 모듈의 파일명을 “.js” 확장자를 제외 하고 서술한다.

모듈에서 exports 될 수 있는 것은 함수와 자바스크립트 객체가 된다. 위의 예는 함수 형태를 이용하여 모듈을 사용하는 경우인데, 만약에 객체형으로 export 하고 싶다면 export하는 파일에서는

exports.hello = function(){...}

export하고 불러 사용하는 쪽에서는

var h = require('./Hello');

h.hello();

형태로 호출한다.

Module의 경로

앞의 예에서는 “./Hello” Hello.js에서 .js를 제거하고 서술하였으며, 앞에 “./”를 이용하여 경로를 서술하였다. 파일의 경로를 아래와 같이 서술할 수 있는데,

var hello = require('Hello');

 

이 경우 node.js는 현재 실행 디렉토리를 먼저 찾고 없으면, 애플리케이션 디렉토리의 하위디렉토리인 /node_modules/ 라는 디렉토리를 찾는다.

이 디렉토리는 node의 모듈을 저장하는 디렉토리이다. 만약에 이 디렉토리에서 찾지 못하면, 하위 디렉토리의 /node_modules/ 디렉토리를 찾게 된다.

예를 들어서 애플리케이션 디렉토리가 /home/terry/myapp 인 경우,

/home/terry/myapp/node_modules를 먼저 찾고 없으면 다음과 같은 순서로 찾게 된다.

Ÿ  /home/terry/node_modules

Ÿ  /home/node_modules

Ÿ  /node_modules

 

 

Module의 종류

Native Module javascript 모듈

node.js는 엔진은 C++로 짜야져 있고, 그 위에서 동작하는 애플리케이션은 javascript로 구현된다. 그래서 모듈도 두 가지 타입을 가지고 있다. C++/C로 된 모듈을 Native 모듈이라고 하고, Javascript로 된 모듈을 javascript 모듈이라고 한다. Javascript 모듈의 경우, 설치시에 파일이 복사되는 수준에서 설치가 되지만, native module의 경우에는 컴파일을 하면서 설치를 한다. (마치 Linux make install 처럼). 그래서 반드시 C/C++ 컴파일러가 설치되어 있어야 한다. Linux에서는 GCC, Windows에서는 Visual Studio Expess(무료)등을 설치하면 된다.

Global Module Local Module

다음으로 Global Module Local Module이라는 개념을 가지고 있는데, Global Module은 시스템내에 설치된 모든 node.js 프로그램들이 참조할 수 있는 전역 모듈이다.

윈도우즈의 경우 디폴트로 ${user_home}/AppData/Roaming/npm/node_module 디렉토리에 설치된다.

또는 환경 변수 NODE_PATH에 그 경로를 다음과 같이 지정할 수 있다.

NODE_PATH=C:\Users\terry\AppData\Roaming\npm\node_module

Local Module의 경우 application 디렉토리의 /node_module 디렉토리에 설치되며, 해당 애플리케이션만 그 모듈을 참조할 수 있다.

기본 모듈과 확장 모듈

node의 모듈에도 node 설치시에 기본적으로 설치되는 모듈과, 추가로 설치해야 하는 확장 모듈이 있다. 기본 모듈은 http 프로토콜 핸들링이나, file system, event, cluster,TLS/SSL와 같은 암호화 등의 모듈이 있고, 확장 설치로는 웹 개발 프레임웍인 express등이 있다. 기본/확장 모듈에 대해서는 https://github.com/joyent/node/wiki/modules를 참고하기 바란다.


NPM

npm node package manager의 약자로, 앞서 설며한 모듈들에 대한 설치 및 의존성을 관리해 주는 도구이다. 마치 Linux rpm이나 Python pip 처럼 설치를 하면, repository에서 해당 모듈을 읽어다가 설치를 해주며, java maven처럼 package.json 이라는 파일에 (pom.xml과 비슷한 역할을 함) module간의 dependency (의존성)에 따라서 의존성이 있는 모듈을 같이 설치한다.

주요 명령어

여러가지 기능들이 있지만, 주요한 명령을 설명한다.

Ÿ      npm list {module} {-g} : 이 명령은 현재 디렉토리 아래에 설치되어 있는 확장 모듈을 리스트 해준다. {module}을 정해주면, 해당 모듈에 대한 리스트를 출력해주고, {-g} 옵션을 추가하면 global에 설치된 모듈 리스트들을 출력해준다.

Ÿ      npm install {-g} : npm 레파지토리 (maven 레파지토리 처럼 외부에 설치되어 있음)로부터, 모듈을 읽어서 로컬에 설치한다. –g 옵션을 적용할 경우 전역 모듈로 설치한다.

Ÿ      npm update {module} {-g} : 설치된 모듈을 최신 버전으로 업데이트 한다.

Ÿ      npm remove {module} {-g} : 설치된 모듈을 삭제한다.

Ÿ      npm info {module} : 해당 모듈의 의존성, 모듈명등 상세 정보를 출력한다.

Ÿ      npm init : 이 명령어를 수행하면 interactive prompt 모드를 통해서 package.json 파일을 만들기 위한 사용자로부터 받아서, package.json 파일을 생성해준다.

package.json 파일

package.json maven pom.xml과 같은 역할을 한다. 모듈에 대한 정보 (버전,제작자,모듈명 등) 기술을 하면서, 모듈에 대한 의존성, repository 경로등을 정의한다.

의존성 관리

아래 코드는 샘플 package.json 파일로, app이라는 모듈 0.0.1 버전에 대해서 서술하였으며, 이 모듈을 사용하기 위해서는 express 모듈 3.x버전과 redis 모듈 (버전에 상관없이 최신)을 필요로 한다. 또한 모듈은 git://xxx URL로부터 읽어서 설치하도록 되어 있다.

{

  "name": "app",

  "version": "0.0.1",

  "dependencies": {

    "express": "3.x",

    "redis": "*"

}

“repository”: {“type”:”git”,”url”,”git://xxxx”}

}

npm을 이용한 스크립트와 테스트 수행

npm make maven 처럼 custom command를 지정하여 명령어를 수행하도록 할 수 있다. 예를 들어 node server start하거나, test를 수행하거나 또는 빌드(?)패키징을 하도록 설정이 가능하다.

“scripts”라는 엘리먼트를 사용하면 되는데, 아래 예제는 npm start를 하면 app.js 애플리케이션으로 node.js를 실행하고, npm test를 하면, mocha 테스트 프레임웍을 수행하여,테스트를 수행하도록 하는 스크립트이다.

:중략

"redis": "*"

}

“scripts”: {“start”:”node app.js”,

             “test”:”mocha”}

}

참고

Npm 메뉴얼을 보면, “config’ 엘리먼트를 정의해놓고, 여기에 환경 변수를 설정할 수 있다.  db 접속 정보나, http listen 포트와 같이 환경에 따라서 변경이 되는 부분은 코드 상에 직접 넣지 않고, package.json 안에 설정해서, 이 파일만 변경을 하면 되도록 한다. 이를 통해서 개발,테스트 환경에 대해서package.json만 다르게 운영하거나, 운영 환경으로 배포시 간략하게 package.json만 변경하도록 한다.

예를 들어서 package.json

{ “config”:{“dbport”:”3306’,”dbuser”:”terry”}, ..} 라고 정해 놓으면

코드내에서 http.createServer(...).listen(process.env.npm_package_config_dbport); 라고 하면, package.json에서 지정한 환경 변수를 가져다 쓸 수 있다고 한다.

https://www.npmjs.org/doc/misc/npm-config.html 문서 참고

근데, 직접해보니 안된다.

 

그래서 사용하는 방법이 별도의 config 파일을 만들고 예를 들어 config.json으로 만들고

파일내에

{ “dbport”:”3306”,

“dbuser”:”terry”}

라고 해놓고

var config = require(“./config.json”); 으로 부르면 바로 json 객체로 나온다.

다음으로 값을 참고하려면

console.log(config.dbport);

console.log(config.dbuser);

식으로 사용하면 된다.

 

버전 Semantics

Module의 버전Semantics를 살펴보자. 보통 3자리로 구성되는데 1.2.3일 경우

Ÿ   1: major version

Ÿ   2: minor version

Ÿ   3:patch level

이다.package.json에서 dependency에 대한 버전을 정의할 수 있는데,

"dependencies" : {

   "mymodule" : "1.8.1

}

1.8.1 버전에 대한 의존성을

"dependencies" : {

   "mymodule" : "~1"

}

이 의미는 : >= 1.0.0 <2.0.0

"dependencies" : {

   "mymodule" : "~1.8”

}

이 의미는 : >=1.8 <2.0.0 까지

을 정하는 것인데, 설명은 했지만, node.js의 경우 한참 개발되고 있는 신생 에코 시스템이기 때문에 모듈 버전간의 변화가 심할 수 있기 때문에 되도록이면 range 방식은 사용하지 않는 것이 바람직하다.

회사 같은 곳에서 HTTP proxy를 사용하는 경우 해결 방법

npm install 인스톨시 회사 내부 네트워크에서 사용할 경우, 회사에서 proxy를 사용하면, npm install proxy 를 타지 않아서 설치가 npm 설치가 제대로 동작하지 않는 경우가 있다. 이런 문제를 해결 하려면,npm 환경 변수 세팅에 http proxy 서버를 지정해주면 되는데, 지정 방법은 다음과 같다.

Ÿ   npm config set proxy http://proxy.company.com:8080

Ÿ   npm config set https-proxy http://proxy.company.com:8080


다음 연재에서는 node.js의 웹 프레임웍인 Express에 대해서 소개하겠다.


#1 – node.js의 소개와 내부 구조 http://bcho.tistory.com/881

#2 - 설치와 개발환경 구축 http://bcho.tistory.com/884

#3 - Event,Module,NPM  http://bcho.tistory.com/885

#4 - 웹 개발 프레임웍 Express 1/2 - http://bcho.tistory.com/887




본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요

  1. EsJoo 2014.05.25 12:58 신고  댓글주소  수정/삭제  댓글쓰기

    노드 공부하는데 정말 많은 도움이 된 것 같아요 감사합니다~

  2. ted 2014.05.30 18:32  댓글주소  수정/삭제  댓글쓰기

    감사히 잘보고있습니다 =)

  3. ikspres 2014.12.30 15:29  댓글주소  수정/삭제  댓글쓰기

    회사 proxy를 쓰고 있는 환경인 경우, NPM으로 패키지 설치 시에 SSL cert 인증문제로 에러가 날 수 있습니다.
    이 경우에는 npm config set strict-ssl false 를 해두면 해결될 수 있습니다.

  4. 김종진 2015.08.11 16:16  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 nodejs를 이제 막 공부하고 있는 학생입니다. 좋은 글 감사합니다. 제가 npm설치도중 갑자기 이런에러가 발생해 아무것도 못하고 있는데 에러로그는 아래와 같습니다
    0 info it worked if it ends with ok
    1 verbose cli [ 'C:\\Program Files\\nodejs\\\\node.exe',
    1 verbose cli 'C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js',
    1 verbose cli 'install',
    1 verbose cli '-d' ]
    2 info using npm@2.11.3
    3 info using node@v0.12.7
    4 verbose readDependencies loading dependencies from C:\Program Files\nodejs\package.json
    5 error install Couldn't read dependencies
    6 verbose stack Error: ENOENT, open 'C:\Program Files\nodejs\package.json'
    6 verbose stack at Error (native)
    7 verbose cwd C:\Program Files\nodejs\aaa
    8 error Windows_NT 6.3.9600
    9 error argv "C:\\Program Files\\nodejs\\\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "-d"
    10 error node v0.12.7
    11 error npm v2.11.3
    12 error path C:\Program Files\nodejs\package.json
    13 error code ENOPACKAGEJSON
    14 error errno -4058
    15 error package.json ENOENT, open 'C:\Program Files\nodejs\package.json'
    15 error package.json This is most likely not a problem with npm itself.
    15 error package.json npm can't find a package.json file in your current directory.
    16 verbose exit [ -4058, true ]

  5. 신입생 2016.03.13 20:44  댓글주소  수정/삭제  댓글쓰기

    정말 도움이 많이 됩니다. 감사해요!!!