Node.js 인터뷰 질문 93

질문: Node.js의 이벤트 기반 프로그래밍과 이벤트 루프의 동작 방식에 대해 설명해주세요.

답변:

Node.js의 이벤트 기반 프로그래밍은 비동기 작업을 효율적으로 처리하는 핵심 메커니즘입니다. 이벤트 루프와 이벤트 기반 프로그래밍의 구현 방법을 살펴보겠습니다.

1. 이벤트 이미터 구현

// custom-event-emitter.js
class CustomEventEmitter {
  constructor() {
    this.events = new Map();
  }

  // 이벤트 리스너 등록
  on(eventName, callback) {
    if (!this.events.has(eventName)) {
      this.events.set(eventName, []);
    }
    this.events.get(eventName).push(callback);
  }

  // 이벤트 한 번만 실행
  once(eventName, callback) {
    const wrapper = (...args) => {
      callback(...args);
      this.off(eventName, wrapper);
    };
    this.on(eventName, wrapper);
  }

  // 이벤트 리스너 제거
  off(eventName, callback) {
    if (!this.events.has(eventName)) return;

    if (!callback) {
      this.events.delete(eventName);
      return;
    }

    const listeners = this.events.get(eventName);
    this.events.set(
      eventName,
      listeners.filter((listener) => listener !== callback)
    );
  }

  // 이벤트 발생
  emit(eventName, ...args) {
    if (!this.events.has(eventName)) return;

    const listeners = this.events.get(eventName);
    listeners.forEach((listener) => {
      try {
        listener(...args);
      } catch (error) {
        console.error(`이벤트 처리 중 오류 발생: ${error.message}`);
      }
    });
  }
}

// 사용 예시
class OrderProcessor extends CustomEventEmitter {
  constructor() {
    super();
    this.orders = new Map();
  }

  processOrder(order) {
    // 주문 접수 이벤트 발생
    this.emit("orderReceived", order);

    // 주문 처리
    setTimeout(() => {
      this.orders.set(order.id, { ...order, status: "processing" });
      this.emit("orderProcessing", order);

      // 주문 완료
      setTimeout(() => {
        this.orders.set(order.id, { ...order, status: "completed" });
        this.emit("orderCompleted", order);
      }, 2000);
    }, 1000);
  }
}

const processor = new OrderProcessor();

// 이벤트 리스너 등록
processor.on("orderReceived", (order) => {
  console.log(`주문 접수: ${order.id}`);
});

processor.on("orderProcessing", (order) => {
  console.log(`주문 처리 중: ${order.id}`);
});

processor.on("orderCompleted", (order) => {
  console.log(`주문 완료: ${order.id}`);
});

// 주문 처리 시작
processor.processOrder({ id: "ORDER-123", items: ["item1", "item2"] });

2. 이벤트 루프 이해

// event-loop-example.js
console.log("1. 스크립트 시작");

// 타이머 큐
setTimeout(() => {
  console.log("2. 타이머 콜백");
}, 0);

// 프로미스 마이크로태스크
Promise.resolve().then(() => {
  console.log("3. 프로미스 콜백");
});

// I/O 큐
const fs = require("fs");
fs.readFile("example.txt", "utf8", (err, data) => {
  if (err) {
    console.error("4. 파일 읽기 오류:", err);
    return;
  }
  console.log("4. 파일 내용:", data);
});

// setImmediate 큐
setImmediate(() => {
  console.log("5. immediate 콜백");
});

// process.nextTick 마이크로태스크
process.nextTick(() => {
  console.log("6. nextTick 콜백");
});

console.log("7. 스크립트 종료");

// 출력 순서:
// 1. 스크립트 시작
// 7. 스크립트 종료
// 6. nextTick 콜백
// 3. 프로미스 콜백
// 2. 타이머 콜백
// 5. immediate 콜백
// 4. 파일 내용: ...

3. 비동기 이벤트 처리

// async-event-handler.js
class AsyncEventEmitter extends CustomEventEmitter {
  // 비동기 이벤트 리스너 등록
  async onAsync(eventName, callback) {
    this.on(eventName, async (...args) => {
      try {
        await callback(...args);
      } catch (error) {
        this.emit("error", error);
      }
    });
  }

  // 비동기 이벤트 발생
  async emitAsync(eventName, ...args) {
    if (!this.events.has(eventName)) return;

    const listeners = this.events.get(eventName);
    await Promise.all(
      listeners.map((listener) => Promise.resolve(listener(...args)))
    );
  }
}

// 사용 예시
class DataProcessor extends AsyncEventEmitter {
  async processData(data) {
    // 데이터 처리 시작 이벤트
    await this.emitAsync("processingStart", data);

    // 데이터 처리
    const processedData = await this.transform(data);

    // 데이터 처리 완료 이벤트
    await this.emitAsync("processingComplete", processedData);

    return processedData;
  }

  async transform(data) {
    // 데이터 변환 로직
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(data.map((item) => item * 2));
      }, 1000);
    });
  }
}

const processor = new DataProcessor();

// 비동기 이벤트 리스너 등록
processor.onAsync("processingStart", async (data) => {
  console.log("데이터 처리 시작:", data);
  await someAsyncOperation();
});

processor.onAsync("processingComplete", async (data) => {
  console.log("데이터 처리 완료:", data);
  await saveToDatabase(data);
});

// 에러 처리
processor.on("error", (error) => {
  console.error("처리 중 오류 발생:", error);
});

4. 이벤트 기반 스트림 처리

// event-stream.js
const { Transform } = require("stream");

class DataTransformStream extends Transform {
  constructor(options = {}) {
    super({ ...options, objectMode: true });
    this.count = 0;
  }

  _transform(chunk, encoding, callback) {
    try {
      // 데이터 변환 처리
      const transformed = this.processChunk(chunk);
      this.count++;

      // 진행 상황 이벤트 발생
      this.emit("progress", {
        processed: this.count,
        data: transformed,
      });

      callback(null, transformed);
    } catch (error) {
      callback(error);
    }
  }

  _flush(callback) {
    // 스트림 종료 시 처리
    this.emit("summary", {
      totalProcessed: this.count,
    });
    callback();
  }

  processChunk(chunk) {
    // 실제 데이터 처리 로직
    return typeof chunk === "object" ? { ...chunk, processed: true } : chunk;
  }
}

// 사용 예시
const transformStream = new DataTransformStream();

transformStream.on("progress", ({ processed, data }) => {
  console.log(`처리된 항목: ${processed}`, data);
});

transformStream.on("summary", ({ totalProcessed }) => {
  console.log(`총 처리된 항목: ${totalProcessed}`);
});

transformStream.on("error", (error) => {
  console.error("스트림 처리 중 오류:", error);
});

// 데이터 스트리밍
const data = [
  { id: 1, value: "a" },
  { id: 2, value: "b" },
  { id: 3, value: "c" },
];

data.forEach((item) => transformStream.write(item));
transformStream.end();

요약

Node.js 이벤트 기반 프로그래밍의 주요 특징:

  1. 이벤트 이미터

    • 이벤트 등록과 발생
    • 이벤트 리스너 관리
    • 에러 처리
  2. 이벤트 루프

    • 페이즈 이해
    • 실행 순서
    • 마이크로태스크
  3. 비동기 이벤트

    • Promise 통합
    • 비동기 리스너
    • 병렬 처리
  4. 스트림 이벤트

    • 데이터 스트리밍
    • 진행 상황 모니터링
    • 에러 처리
  5. 성능 고려사항

    • 메모리 관리
    • 이벤트 리스너 제한
    • 에러 전파

results matching ""

    No results matching ""