Node.js 인터뷰 질문 17

질문: Node.js에서 EventEmitter를 어떻게 사용하나요?

답변:

Node.js의 EventEmitter는 이벤트 기반 아키텍처의 핵심으로, 이벤트를 발생시키고 수신하는 메커니즘을 제공합니다. 이 클래스는 events 모듈에 있으며, Node.js의 많은 내장 모듈들이 이를 상속하여 사용합니다.

기본 사용법

const EventEmitter = require("events");

// EventEmitter 인스턴스 생성
const myEmitter = new EventEmitter();

// 이벤트 리스너 등록
myEmitter.on("event", function (a, b) {
  console.log("이벤트가 발생했습니다!", a, b);
});

// 이벤트 발생시키기
myEmitter.emit("event", "arg1", "arg2");

클래스 상속을 통한 사용

const EventEmitter = require("events");

// EventEmitter를 상속받는 클래스 정의
class MyEmitter extends EventEmitter {
  constructor() {
    super();
    this.name = "MyEmitter";
  }

  process() {
    // 작업 수행
    console.log("처리 중...");
    // 작업이 완료되면 이벤트 발생
    this.emit("processed", { status: "success", time: new Date() });
  }
}

// 인스턴스 생성
const myEmitter = new MyEmitter();

// 이벤트 리스너 등록
myEmitter.on("processed", (data) => {
  console.log(`처리 완료! 상태: ${data.status}, 시간: ${data.time}`);
});

// 처리 실행
myEmitter.process();

주요 메서드

  1. on(eventName, listener): 이벤트 리스너 등록

    myEmitter.on("connection", (stream) => {
      console.log("연결됨!");
    });
    
  2. once(eventName, listener): 한 번만 호출되는 이벤트 리스너 등록

    myEmitter.once("connection", (stream) => {
      console.log("연결됨! (한 번만 호출됨)");
    });
    
  3. emit(eventName[, ...args]): 이벤트 발생 및 인자 전달

    myEmitter.emit("connection", { id: 1, name: "socket" });
    
  4. removeListener(eventName, listener): 특정 이벤트의 특정 리스너 제거

    const callback = () => console.log("이벤트 발생!");
    myEmitter.on("event", callback);
    // 나중에 리스너 제거
    myEmitter.removeListener("event", callback);
    
  5. removeAllListeners([eventName]): 모든 이벤트 리스너 또는 특정 이벤트의 모든 리스너 제거

    myEmitter.removeAllListeners("event"); // 'event'의 모든 리스너 제거
    myEmitter.removeAllListeners(); // 모든 이벤트의 모든 리스너 제거
    
  6. setMaxListeners(n): 최대 리스너 수 설정 (기본값: 10)

    myEmitter.setMaxListeners(20); // 최대 리스너 수를 20으로 설정
    
  7. listeners(eventName): 특정 이벤트의 리스너 배열 반환

    const listeners = myEmitter.listeners("connection");
    console.log(listeners);
    

에러 이벤트 처리

error 이벤트는 특별하게 처리됩니다. error 이벤트 발생 시 리스너가 없으면 예외가 발생하고 프로그램이 종료됩니다:

const myEmitter = new EventEmitter();

// 'error' 이벤트에 대한 리스너를 추가하는 것이 좋습니다
myEmitter.on("error", (err) => {
  console.error("에러 발생:", err);
});

// 이제 에러 이벤트를 발생시켜도 프로그램이 종료되지 않습니다
myEmitter.emit("error", new Error("오류가 발생했습니다!"));

비동기 처리와 EventEmitter

Node.js의 EventEmitter는 비동기 작업과 함께 자주 사용됩니다:

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

class FileProcessor extends EventEmitter {
  processFile(filePath) {
    this.emit("start", filePath);

    fs.readFile(filePath, "utf8", (err, data) => {
      if (err) {
        this.emit("error", err);
        return;
      }

      // 데이터 처리
      const processedData = data.toUpperCase();
      this.emit("data", processedData);

      // 처리 완료
      this.emit("end", filePath);
    });

    return this; // 메서드 체이닝 가능하도록 this 반환
  }
}

const processor = new FileProcessor();

processor
  .on("start", (file) => console.log(`파일 처리 시작: ${file}`))
  .on("data", (data) =>
    console.log(`처리된 데이터: ${data.substring(0, 20)}...`)
  )
  .on("end", (file) => console.log(`파일 처리 완료: ${file}`))
  .on("error", (err) => console.error(`오류 발생: ${err.message}`))
  .processFile("example.txt");

모범 사례

  1. 메모리 누수 방지: 더 이상 필요하지 않은 리스너는 제거합니다.
  2. 에러 처리: error 이벤트에 대한 리스너를 항상 추가합니다.
  3. 메서드 체이닝: 가독성을 위해 .on() 메서드를 체이닝하여 사용합니다.
  4. 이벤트 네이밍: 명확하고 일관된 이벤트 이름을 사용합니다.
  5. 리스너 제한: 메모리 사용량 관리를 위해 필요한 경우 setMaxListeners()를 사용합니다.

EventEmitter는 Node.js에서 비동기 이벤트 기반 프로그래밍의 기초가 되며, 복잡한 비동기 워크플로우를 간단하고 모듈식으로 관리할 수 있게 해줍니다.

results matching ""

    No results matching ""