프론트엔드

Node.js 기초부터 튼튼히 (3) 이벤트

CyberI 2018. 1. 8. 14:21

 

 

Node.js 를 공부하고 있다면 비동기, 이벤트 루프, 단일 Thread 에 대해 많이 들어보셨을겁니다.

Node.js 는 기존의 웹 모델의 multi-thread 방식의 문제점을 보완해주는 이벤트 기반의 비동기 방식 프레임워크로  API 실행시, 하나의 Thread로 데이터 반환까지 기다리지 않고 다음 API를 실행시켜주는 뛰어난 처리 성능을 자랑하죠.

기존의 웹 모델이 서버에 요청이 오면 Thread를 생성하여 작업하는 멀티-Thread 방식이지만,
단일한 Thread로 짧은시간에 많은 요청을 처리하기 위해서 Event Callback 방식을 사용합니다.

어떻게 돌아간다는 것이죠?

하나의 Thread에서 이벤트 루프를 돌려, 이벤트가 발생하면 해당 되는 함수를 실행시키기 때문에 단일한 Thread로 여러 요청 처리가 가능합니다.

다시 설명해드리자면, 

요청이 들어오면 요청 메세지를 Event Queue에 넣어주고, 들어와 있는 작업들을 Event Loop가 순차적으로 Thread Pool에 있는 Thread에 작업을 할당해주고 작업이 끝난 이벤트들을 감지하여 해당되는 Callback함수를 다시 Event Queue에 집어넣습니다.

실제로 요청을 처리하는 Event Loop는 단일 Thread지만, Thread Pool 내부는 여러개의 Thread를 사용하여 Event Loop가 Callback함수를 내부에 여러 Thread들에게 요청을 할당해줍니다.

 

 

비동기 방식이다 보니 요청된 작업이 언제 끝날지, Callback함수를 호출하는 시점은 언제인지 알 수 없어 Callback함수에 Callback.. callback.. callback.. callback...  Callback Hell 에 빠질 위험이 있습니다.


EventEmitter는 객체의 상태 변화를 관찰하고 상태 변화가 있을때 메소드를 실행시켜주는 옵서버 패턴의 구현으로 이벤트를 추가할 수 있는 객체들은 EventEmitter 객체의 상속을 받는다. 즉, 상속을 받게 되면 EventEmitter가 될 수 있다.
이벤트를 새롭게 등록할 때는 EventEmitter 객체를 생성해주고 진행하면 된다. 

const EventEmitter = require('events');

process 객체로도  EventEmitter 객체를 생성할 수 있다.

var myEvent = new process.EventEmitter();
 

이벤트 연결하기 (리스너 등록)

이벤트를 추가하려면, emitter.addListener(eventName, listener) 로도 등록 가능하지만, jQuery를 사용한다면 익숙할 emitter.on(eventName, listener)를 사용하면 된다.

노드 API문서의 예를 활용하면, 서버가 연결되면 콘솔창에 누군가 접속했음을 알리는 로그가 기록된다.

server.on('connection', (stream) => {
  console.log('someone connected!');
});
// 서버가 연결되면 console창에 'someone connected' 출력

마찬가지로, 최초 한번만 실행되고 이벤트를 제거할때는 .once(event, listener) 를 사용하면 된다.

server.once('connection', (stream) => {
  console.log('Ah, we have our first user!');
});
// 서버가 최초로 연결되면 console창에 'Ah, we have our first user!' 출력

기본적으로 EventEmmiters는 10개 이상의 리스너가 특정한 이벤트에 붙으면 메모리 누수를 막는것을 돕기위해 경고 메세지를 프린트해준다. 

특정개수의 리스너를 붙일 경우 emitter.setMaxListenrs(n) 매개변수에 개수를 입력해주면 된다. (무한개로 지정할 때는 매개변수로 0 또는 Infinity 작성)

연결된 리스너의 개수를 알고 싶을때는 emitter.listenerCount(eventName) 로 해당 이벤트에 등록된 리스너 개수를 반환해준다. 개수 말고 리스너 배열 자체를 반환할 때는 emitter.listener(eventName) 사용하면 된다.

 

이벤트 삭제하기

이벤트 삭제할때는 emitter.removeListener(eventName, listener) 메서드를 사용하면 특정 이벤트의 이벤트리스너를 제거할 수 있다. 

const callback = (stream) => {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
// 연결되었던 callback함수가 삭제

특정 리스너를 지울 수 도 있고 emitter.removeAllListeners([eventNam]) 메소드로 해당 이벤트에 모든 리스너를 제거할 수도 있다.

 

이벤트 호출하기

 emitter.emit(eventNam[, ...args]) 순서대로 각 리스너를 등록된 이벤트에 맞게 호출한다. 또한, 메소드가 존재하면 true, 존재하지 않으면 false를 반환한다. emit 메소드로 이벤트를 강제로 호출하게 되는 경우 리스너는 실행되지만 실제 이벤트가 발생하는것은 아니라는 것은 주의해야한다.

다음번에는 외부모듈을 집중적으로 알아보겠습니다. 

Node.js의 대부분이 외부모듈 형태로 동작하는데 멋진 플러그인들이 많이 준비되있습니다. 웹 관련해서 EJS 모듈과 Jade 모듈과 다른 개발자들이 배포한 모듈 몇가지를 중점으로 살펴보도록 하겠습니다.

참조 - https://nodejs.org/docs/latest-v7.x/api/events.html#events_emitter_emit_eventname_args


Node.js 시리즈 1,2편에 대한 내용을 살펴보고 싶다면, 아래 링크를 클릭해주세요.

▶ Node.js 기초부터 튼튼히 (1) 시작하기

▶ Node.js 기초부터 튼튼히 (2) 내장 모듈