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();
주요 메서드
on(eventName, listener): 이벤트 리스너 등록
myEmitter.on("connection", (stream) => { console.log("연결됨!"); });
once(eventName, listener): 한 번만 호출되는 이벤트 리스너 등록
myEmitter.once("connection", (stream) => { console.log("연결됨! (한 번만 호출됨)"); });
emit(eventName[, ...args]): 이벤트 발생 및 인자 전달
myEmitter.emit("connection", { id: 1, name: "socket" });
removeListener(eventName, listener): 특정 이벤트의 특정 리스너 제거
const callback = () => console.log("이벤트 발생!"); myEmitter.on("event", callback); // 나중에 리스너 제거 myEmitter.removeListener("event", callback);
removeAllListeners([eventName]): 모든 이벤트 리스너 또는 특정 이벤트의 모든 리스너 제거
myEmitter.removeAllListeners("event"); // 'event'의 모든 리스너 제거 myEmitter.removeAllListeners(); // 모든 이벤트의 모든 리스너 제거
setMaxListeners(n): 최대 리스너 수 설정 (기본값: 10)
myEmitter.setMaxListeners(20); // 최대 리스너 수를 20으로 설정
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");
모범 사례
- 메모리 누수 방지: 더 이상 필요하지 않은 리스너는 제거합니다.
- 에러 처리:
error
이벤트에 대한 리스너를 항상 추가합니다. - 메서드 체이닝: 가독성을 위해
.on()
메서드를 체이닝하여 사용합니다. - 이벤트 네이밍: 명확하고 일관된 이벤트 이름을 사용합니다.
- 리스너 제한: 메모리 사용량 관리를 위해 필요한 경우
setMaxListeners()
를 사용합니다.
EventEmitter는 Node.js에서 비동기 이벤트 기반 프로그래밍의 기초가 되며, 복잡한 비동기 워크플로우를 간단하고 모듈식으로 관리할 수 있게 해줍니다.