EventEmitter란 ?

GonnabeAlright·2022년 2월 1일
11
post-thumbnail

Nodejs EventEmitter는 nodejs 애플리케이션을 만들다보면 많이 사용하게 되는 라이브러리다. 그러나 이름만 비슷할 뿐, EventEmitterEventLoop 사이에는 아무런 관계가 없다.

Nodejs의 EventLoop는 Nodejs의 비동기, 논블로킹 I/O 매커니즘을 처리하는 핵심적인 부분이다. EventLoop는 다양한 유형의 비동기 이벤트를 특정 순서대로 처리한다.

반면에 Nodejs의 EventEmitter는 특정 이벤트에 리스너 함수를 달아서, 이벤트가 발생했을 때 이를 캐치할 수 있도록 만들어진 api이다. 이 동작은 일반적으로 이벤트 리스너가 원래 등록된 이벤트 핸들러보다 나중에 호출되기 때문에 비동기처럼 보인다.

그러나 EventEmitter의 인스턴스는 EventEmitter 인스턴스 자체 내에서 이벤트와 연결된 모든 이벤트와 리스너를 추적한다. 따라서, EventLoop의 큐를 사용하는 것이 아니다. 이 정보가 저장되는 데이터 구조는, 단순히 이벤트 이름이 있는 이벤트 객체일 뿐이다. 그리고 그 값은 이벤트 리스너 함수들이 들어가 있는 배열일 뿐이다.

Example:

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

myEmitter.on('myevent', () => console.log('handler1: myevent was fired!'));
myEmitter.on('myevent', () => console.log('handler2: myevent was fired!'));
myEmitter.on('myevent', () => console.log('handler3: myevent was fired!'));

myEmitter.emit('myevent');
console.log('I am the last log line');

Output:

handler1: myevent was fired!
handler2: myevent was fired!
handler3: myevent was fired!
I am the last log line

EventEmitter동기적으로 모든 이벤트 핸들러를 호출하기 때문에, I am the last log line은 모든 리스너 함수가 실행되기 전까지 출력되지 않는다.

순수 자바스크립트는 동기적으로 작동한다. 즉, 한 번에 하나의 프로세스만 실행시킬 수 있다. 자바스크립트를 비동기적으로 작동시키는 것은 자바스크립트 엔진 바깥을 감싸고 있는 호스팅 환경이다. 전통적으로는 브라우저가 유일한 호스팅 환경이었지만 자바스크립트 세계관이 확장되면서 이제는 Node.js 또한 호스팅 환경 중의 하나가 되었다.

Node.js가 비동기적으로 작동하는 것은 그 내부에 비동기 이벤트를 소화하기 위한 라이브러리(libuv)를 지니고 있기 때문이다. Node.js의 이벤트 루프는 libuv를 이용해 구현되었으며, 크게 여섯 단계의 페이즈를 순환하고 있다. 각 페이즈는 libuv를 통해 커널 혹은 쓰레드 풀에 인계했던 콜백 작업을 실행한다.

setTimeout, setInterval 등 타이머 작업 혹은 DB 연결, 파일 읽기/쓰기, HTTP 요청모든 비동기 작업들은 libuv를 통해 스케줄링 되었다가 이벤트 루프의 순환 주기에 따라 호출된다.

Event

이벤트란 '어플리케이션 내에서 발생한 응답 가능한 사건'이라고 표현할 수 있다. 이벤트는 Node.js에서만 사용되는 개념은 아니지만 Node에서는 특히 아키텍쳐의 근간을 이루는 개념이기에 중요하게 다뤄야 한다.

Node.js에서 발생하는 이벤트는 두 종류로 나눌 수 있다. 먼저 시스템 이벤트가 있는데, 이것은 libuv 라이브러리가 작용된 C++코어에서 처리하게 된다. 파일을 열고 닫거나 인터넷이 연결되거나 하는 영역이다. 자바스크립트 코어에서 처리되는 보다 상위 단계의 이벤트는 Node.js의 Event Emitter에서 관리된다.

자바스크립트 자체는 이벤트와 관련된 객체가 없다. Node.js가 그와 관련된 컨셉을 구현한 것이다.

events 객체의 메소드

  • emitter.addListener(event, listener): on() 메소드와 같습니다. 이벤트를 생성하는 메소드입니다.
  • emitter.on(event, listener): addListener()과 동일합니다. 이벤트를 생성하는 메소드입니다.
  • emitter.once(event, listener): 이벤트를 한 번만 연결한 후 제거합니다.
  • emitter.removelistener(event, listener): 특정 이벤트의 특정 이벤트 핸들러를 제거합니다. 이 메소드를 이용해 리스너를 삭제하면 리스너 배열의 인덱스가 갱신되니 주의해야 합니다.
  • emitter.removeAllListeners([event]): 모든 이벤트 핸들러를 제거합니다.
  • emitter.setMaxListeners(n): n으로 한 이벤트에 최대허용 개수를 지정합니다. node.js는 기본값으로 한 이벤트에 10개의 이벤트 핸들러를 작성할 수 있는데, 11개 이상을 사용하고 싶다면 n값을 넘겨주면 됩니다. n값으로 0을 넘겨주면 연결 개수 제한이 사라집니다.
  • emitter.emit(eventName[, ...args]): 이벤트를 발생시킵니다.


Emitter 함수 생성자는 this.events라는 객체를 초기화한다. 그리고 프로토타입에 on이라는 함수를 추가하는데 이 함수는 이벤트의 타입과 리스너를 인자로 받아 Emitter 객체에 추가하는 역할을 한다. 함수가 실행되면 객체 안에는 타입을 키로 받고 리스너의 배열을 값으로 받는 페어가 저장될 것이다.


이렇듯 on 함수는 특정 상황에 실행시킬 리스너 함수를 Emitter 안에 등록한다는 의미를 갖고 있다. 그러면 이제 등록할 리스너를 호출할 emit 함수를 작성해보자.


Emitter 객체 안에 담겨 있는 타입-리스너 배열을 순회하며 리스너를 실행한다.

1개의 댓글

comment-user-thumbnail
2022년 10월 22일

잘 쓰인 포스팅 읽고 갑니다~! 덕분에 eventEmitter 이해 실마리를 얻었어요.

답글 달기