상세 컨텐츠

본문 제목

Node.js | EventEmitter란? (feat. Promise와의 차이점)

Development/BackEnd

by 유후(yufu) 2023. 2. 16. 03:22

본문

반응형

1. EventEmitter란?

Node.js에서 이벤트 기반(Event-driven) 프로그래밍은 이벤트를 발생시키고 처리하는 데 사용되는 EventEmitter 클래스를 사용합니다. EventEmitter 클래스는 Node.js에서 가장 기본적인 이벤트 처리 방식 중 하나입니다.

EventEmitter 클래스를 상속한 객체를 만들고, on() 메서드를 사용하여 이벤트 리스너를 등록하면, 이벤트가 발생할 때마다 등록된 콜백 함수가 실행됩니다. 이벤트는 문자열 형태의 이름과 함께 발생하며, 이벤트에 대한 데이터를 선택적으로 전달할 수도 있습니다.

예를 들어, EventEmitter 클래스를 사용하여 간단한 이벤트 처리를 구현할 수 있습니다. 아래는 EventEmitter 클래스를 사용한 간단한 예제 코드입니다.

const EventEmitter = require('events');

// CustomEventEmitter 클래스를 생성합니다.
class CustomEventEmitter extends EventEmitter {}

// CustomEventEmitter 클래스의 인스턴스를 만듭니다.
const customEmitter = new CustomEventEmitter();

// 이벤트 리스너를 등록합니다.
customEmitter.on('customEvent', (arg1, arg2) => {
  console.log('이벤트가 발생했습니다.', arg1, arg2);
});

// 이벤트를 발생시킵니다.
customEmitter.emit('customEvent', 'hello', 'world');

위의 코드에서 CustomEventEmitter 클래스를 만들고, on() 메서드를 사용하여 customEvent 이벤트를 처리하는 이벤트 리스너를 등록합니다. 그리고 emit() 메서드를 사용하여 customEvent 이벤트를 발생시킵니다. emit() 메서드의 두 번째 인수 이후의 인수들은 이벤트 리스너에 전달되는 인수입니다.

참고 : EventEmitter 클래스를 사용하면 Node.js에서 이벤트 기반 프로그래밍을 쉽게 구현할 수 있습니다. 예를 들어, HTTP 요청이나 데이터베이스 쿼리 결과 등의 비동기 이벤트를 처리하는 데 유용합니다.

2. HTTP 요청? 비동기 이벤트 처리? 그렇다면 Promise와 EventEmitter는 무슨 차이가 있는걸까? 🤔

Node.js에서 PromiseEventEmitter는 모두 비동기 프로그래밍을 지원하는 중요한 기능입니다. 그러나 두 기능은 목적과 사용 방법이 다릅니다.

Promise

Promise는 비동기 작업이 완료될 때 실행할 콜백 함수를 등록하는 방식보다 더 깔끔하고 직관적인 방식으로 비동기 작업을 처리할 수 있도록 하는 객체입니다. Promise는 비동기 작업을 수행하는 함수가 반환하는 객체로, 성공하면 결과 값을 반환하고 실패하면 에러를 반환합니다. 이러한 특성으로 Promise는 비동기 작업의 성공 또는 실패 상태를 쉽게 확인하고 처리할 수 있습니다.

EventEmitter

반면, EventEmitter는 이벤트 기반 프로그래밍에서 사용되는 객체입니다. EventEmitter 객체는 이벤트를 발생시키고, 이벤트가 발생했을 때 처리할 콜백 함수를 등록합니다. 이벤트는 이름과 함께 발생하며, 발생한 이벤트를 처리할 수 있는 모든 리스너에게 알립니다. 이를 통해 여러 개의 콜백 함수를 동시에 처리할 수 있습니다.

요약 : Promise와 EventEmitter는 각각 다른 목적을 가지고 있으며, 사용 방법도 다릅니다. Promise는 비동기 작업의 결과를 처리하고, EventEmitter는 이벤트 기반 프로그래밍에서 이벤트를 처리합니다.

참고 : 하지만 두 기능을 함께 사용할 수도 있습니다. 예를 들어, 비동기 작업의 결과를 Promise 객체로 반환하면, Promise 객체가 성공 또는 실패 상태가 되면, EventEmitter를 사용하여 이를 처리할 수 있습니다. 이렇게 함께 사용하면 더욱 효율적으로 비동기 작업을 처리할 수 있습니다.

Promise vs EventEmitter 예시 코드

예를 들어, 파일을 비동기적으로 읽어와서 해당 내용을 출력하는 코드를 작성한다고 가정해봅시다. 이를 PromiseEventEmitter를 사용하여 구현해보겠습니다.

먼저, Promise를 사용한 코드는 다음과 같습니다.

const fs = require('fs');

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
}

readFilePromise('file.txt')
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

위 코드에서는 readFilePromise() 함수를 정의하여, fs.readFile() 함수를 Promise 객체로 래핑합니다. 이렇게 Promise 객체를 반환하는 함수를 작성한 후, then() 메소드와 catch() 메소드를 사용하여 Promise 객체의 성공 상태와 실패 상태를 처리합니다.

다음으로, EventEmitter를 사용한 코드는 다음과 같습니다.

const fs = require('fs');
const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

myEmitter.on('readFile', (path) => {
  fs.readFile(path, 'utf8', (err, data) => {
    if (err) {
      myEmitter.emit('error', err);
    } else {
      myEmitter.emit('data', data);
    }
  });
});

myEmitter.on('data', (data) => console.log(data));
myEmitter.on('error', (err) => console.error(err));

myEmitter.emit('readFile', 'file.txt');

위 코드에서는 EventEmitter 클래스를 상속하여 MyEmitter 클래스를 정의합니다. 그리고 myEmitter 객체를 생성하고, on() 메소드를 사용하여 readFile, data, error 이벤트를 처리하는 이벤트 리스너를 등록합니다. 마지막으로, myEmitter.emit() 메소드를 사용하여 readFile 이벤트를 발생시킵니다.

위 두 코드는 모두 파일을 비동기적으로 읽어와서 출력하는 동일한 작업을 수행합니다. 그러나 Promise는 비동기 작업의 성공 또는 실패 상태를 처리하고, EventEmitter는 이벤트를 발생시키고 이벤트를 처리합니다. 이러한 차이점으로 인해, 코드 작성 방식과 목적이 다르며, 어떤 상황에 적합한지 선택하는 것이 중요합니다.

참고 : EventEmitterPromise는 모두 비동기 프로그래밍에서 중요한 기능이며, 각각의 장단점이 있습니다. 그러나 EventEmitterPromise보다 더욱 유연하고 다양한 상황에 적합한 기능을 제공합니다. 반면 EventEmitter를 사용할 때 유의할 점도 있습니다. 다음은 EventEmitter Promise와 비교하여 정리한 장단점입니다.

3. EventEmitter의 장점 🤗

  1. EventEmitter는 이벤트에 여러 개의 리스너를 등록할 수 있습니다. 이렇게 함께 등록된 리스너들은 모두 이벤트가 발생하면 실행되며, 이를 통해 이벤트를 처리하는 여러 개의 콜백 함수를 동시에 실행할 수 있습니다.
  2. EventEmitter는 다른 객체들과 연동하여 사용할 수 있습니다. 예를 들어, EventEmitter를 사용하여 이벤트를 발생시키면, 이벤트를 다른 서비스로 전달하여 처리하거나, 이벤트를 외부로 전송할 수 있습니다.
  3. EventEmitter는 Promise에 비해 더욱 유연합니다. EventEmitter를 사용하면 필요에 따라 이벤트를 등록하고 제거할 수 있습니다. 이렇게 함께 등록된 이벤트 리스너들은 언제든지 제거될 수 있으며, 필요에 따라 동적으로 추가 및 제거할 수 있습니다.
  4. EventEmitter는 Promise와는 달리 비동기 작업의 결과를 처리하는 것 외에도, 이벤트를 사용하여 다양한 작업을 처리할 수 있습니다. 예를 들어, EventEmitter를 사용하여 이벤트를 발생시켜 로그를 기록하거나, 웹소켓에서 데이터를 전송하는 등의 다양한 작업을 처리할 수 있습니다.

따라서, EventEmitter는 Promise보다 더욱 유연하고 다양한 상황에 적합한 기능을 제공합니다. 그러나 Promise는 콜백 함수를 사용하는 기존의 비동기 코드 작성 방식을 개선하여, 코드의 가독성을 높이고 에러 처리를 쉽게 해준다는 장점이 있습니다. 두 기능을 조합하여 사용하는 것도 가능하며, 어떤 상황에서 사용할지는 개발자의 선택에 따라 다릅니다.

4. EventEmitter의 단점 🤧

  1. EventEmitter의 주요 단점은, 이벤트 리스너에 등록된 콜백 함수들이 처리되지 않는다면 메모리 누수가 발생할 수 있다는 것입니다. 즉, 이벤트 리스너에 등록된 콜백 함수가 처리되지 않는 상황에서는 이벤트에 대한 처리가 계속 남아있어 메모리를 계속 차지하게 됩니다.
  2. EventEmitter는 자체적인 동기화 메커니즘이 없습니다. 따라서, 여러 개의 이벤트 리스너가 동시에 실행될 경우, 이벤트 처리 순서가 보장되지 않을 수 있습니다. 이 경우 이벤트 처리 순서를 명시적으로 조정해주어야 하며, 이를 처리하는 로직을 개발해야 합니다.
  3. EventEmitter는 Promise보다는 복잡한 기능을 제공하기 때문에, 처음에는 사용 방법을 익히는 데 시간이 걸릴 수 있습니다. 이를 위해서는 Node.js 문서나 레퍼런스를 참고하여 사용 방법을 익히는 것이 좋습니다.
  4. EventEmitter는 Promise와 달리 async/await와 같은 비동기 작업을 보다 쉽게 처리하는 최신의 기능을 지원하지 않습니다.

따라서, EventEmitter를 사용하면서 비동기 작업을 처리하기 위해서는 콜백 함수나 Promise를 사용해야 합니다.

요약 : EventEmitter는 여러 개의 이벤트 리스너를 등록할 수 있고, 다른 객체들과 연동하여 사용할 수 있으며, 필요에 따라 이벤트를 동적으로 추가 및 제거할 수 있습니다. 그러나 EventEmitter는 Promise나 async/await에 비해 코드의 가독성이 낮을 수 있고, 명시적인 에러 처리가 필요합니다.

 

반응형

'Development > BackEnd' 카테고리의 다른 글

Node.js | Stream API 개념과 4가지 유형  (0) 2023.02.16

관련글 더보기