[Node.js] Event Driven Architecture

Daniel Woo·2023년 2월 20일
0

node.js

목록 보기
1/1
post-thumbnail

Event Driven Architecture

  • Event Emitter

    // 0. import event listeners
    const { EventEmitter } = require('node:events');
    
    class MyEmitter extends EventEmitter {}
    
    // 1. creating event emitter
    const eventEmitter = new EventEmitter();
    const myEventEmitter = new MyEmitter();
    
    // 3. listen event that I made
    eventEmitter.on('connection', function(){
      console.log('Connection Successful!');
    });
    
    myEventEmitter.on('greeting', function(){
      console.log('Hello!');
    });
    
    // 2. firing custom event
    eventEmitter.emit('connection');
    myEventEmitter.emit('greeting');
    
    // Connection Successful!
    // Hello!
    • node.js는 이벤트 기반 아키텍처로 구성되어 있다.
    • node.js에는 다양한 이벤트 리스너가 있는데, 이는 특정 동작이 실행되는 것을 기다리고 있다가 실행되는 이벤트 함수를 말한다.
    • 이벤트 리스너는 Emitter라는 객체에서 시작된다.
    • Emitter로 부터 새로운 custom 이벤트리스너를 등록할 수 있으며, 이벤트리스너는 특정 동작을 기다리다가 조건을 만족하면, 등록 되었던 함수를 실행시킨다.
    • 파일을 다룰 때 사용하는 노드 내장 모듈인 fs의 ReadStream 이벤트 역시 Emitter 클래스를 확장시킨 이벤트 리스너이다. 파일이 열리는 것이 감지되면 실행되는 이벤트 리스너이다.
    • 이와 같이 node.js는 Emitter 객체로 부터 확장된 고유의 Emitter와 이것에서 감지할 수 있는 이벤트 리스너와 발동 함수를 실행시키는 것을 기반으로 하는 Event Driven Architecture를 갖추고 있는 것을 알 수 있다.
  • 매개변수 전달 및 ES6

    const { EventEmitter } = require('node:events');
    
    class MyEmitter extends EventEmitter {}
    
    const myEventEmitter = new MyEmitter();
    
    const a = 1;
    const b = 100;
    
    myEventEmitter.on('greeting', function (arg1, arg2) {
      console.log(this); 
      console.log(this === myEventEmitter);
      console.log(`Result is... ${arg1 + arg2}`);
    });
    
    myEventEmitter.emit('greeting', a, b);
    
    /*
    --- logs ---
    MyEmitter {
      _events: [Object: null prototype] { greeting: [Function (anonymous)] },
      _eventsCount: 1,
      _maxListeners: undefined,
      [Symbol(kCapture)]: false
    }
    true
    Result is... 101
    */
    • 커스텀 Emitter 객체에 이벤트를 등록할 때 인자를 전달할 수 있다.

    • 인자는 이벤트 리스너로 전달되어 값으로 사용할 수 있다.

    • 등록된 이벤트 리스너 함수는 Emitter 객체에서 생성된 메서드이기 때문에 this는 Emitter객체를 가르킨다. (this === myEventEmitter)

      /* 생략 */
      const a = 1;
      const b = 100;
      
      myEventEmitter.on('greeting', (arg1, arg2) => {
        console.log(this); 
        console.log(this === myEventEmitter);
        console.log(`Result is... ${arg1 + arg2}`);
      });
      
      myEventEmitter.emit('greeting', a, b);
      
      /* 
      --- logs --- 
      {}
      false
      Result is... 101
       */
    • 이벤트 리스너 전달 함수는 ES6의 arrow function을 사용할 수 있다.

    • 하지만, arrow function의 특성상 this는 함수 내부 블록에 바인딩 되므로

      this 사용시 주의해야한다.

  • 동기와 비동기

    /* 생략 */
    const a = 1;
    const b = 100;
    
    myEventEmitter.on('greeting', (arg1, arg2) => {
      setImmediate(() => {
        console.log('this happens asynchronously on setImmediate');
      });
      process.nextTick(() => {
        console.log('this happens asynchronously on nextTick');
      });
      console.log(arg1 + arg2);
    });
    
    /* 
    --- logs --- 
    101
    this happens asynchronously on nextTick
    this happens asynchronously on setImmediate
     */
    • 이벤트 리스너는 기본적으로 동기적으로 동작하여 이벤트를 등록한 순서대로 실행된다.
    • 이를 통해, 코드가 원하는 순서대로 동작할 수 있도록 하여 에러를 막을 수 있다.
    • 간혹, 비동기적인 코드 실행이 필요할 때는 setImmediate 혹은 process.nextTick의 콜백 함수를 통해 실행 로직을 호출하면 된다.
  • Fire event

    /* 생략 */
    let count = 0;
    
    myEventEmitter.on('greeting', () => {
      count += 1;
      console.log(count);
    });
    
    // firing custom event
    myEventEmitter.emit('greeting'); // 1
    myEventEmitter.emit('greeting'); // 2
    myEventEmitter.emit('greeting'); // 3
    • Emitter 객체의 emit 메서드는 이벤트를 발동시키는 역할을 하여 실행시키는 만큼 등록된 커스텀 이벤트 리스너가 발동된다.

      /* 생략 */
      let count = 0;
      
      myEventEmitter.once('greeting', () => {
        count += 1;
        console.log(count);
      });
      
      // firing custom event
      myEventEmitter.emit('greeting'); // 1
      myEventEmitter.emit('greeting'); // <-- 무시
      myEventEmitter.emit('greeting'); // <-- 무시
    • 이벤트의 발동횟수와 관계없이 한 번만 실행시켜야하는 경우라면 Emitter의 on 메서드 대신 once 메서드를 통해 콜백 함수를 등록시키면된다.

    • Emitter를 세번 발동시켰지만 실제도 동작한 것은 하나의 이벤트가 된다.

  • Error Events

    /* 생략 */
    myEventEmitter.on('error', (err) => {
      console.error(err);
    });
    
    myEventEmitter.emit('error', new Error('에러 발생!!'));
    // 에러 발생!!
    // Error logs...
    • 에러가 Emitter Class 내부에서 발생하게 되면, 에러 로그를 반환하면서 런타임 오류가 발생한다.
    • 에러 이벤트에 대해서 노드 공식 문서에서는 에러 이벤트에 대한 리스너를 추가하는 것을 권장한다. (에러 로그를 파일에 저장하는 등 에러를 파악할 수 있는 여지를 남길 수 있음)

참고

profile
모두가행복한세상을만들고싶은사람

0개의 댓글