Tech Blog of Pinomaker
article thumbnail

NestJS에서는 Spring boot의 Events의 기능과 마찬가지로 이벤트를 발행하고 구독하는 기능을 @nestjs/event-emiiter를 이용하여 기본적으로 제공한다.

 

해당 기능을 이용하면 여러가지 기능을 로직에서 분리하여 개발할 수 있다는 장점이 있다.

 

 

오늘은 Nestjs에서 이벤트를 발행하고 구독하는 기능을 정리하고자한다!

 

00. 들어가기 전(프로젝트 셋팅)

해당 포스팅을 위해 새로운 프로젝트를 생성한다. 아래의 명령어를 이용하면 패키지 관리자로 npm, yarn, pnpm을 선택하라고 나올텐데 필자는 pnpm을 사용한다.

npx nest new [project name]

 

Project가 생성되면 아래와 같이 나오면 성공이다.

 

 

01. Event-Emiiter 설정

먼저 @nestjs/event-emiiter를 설치하자.

pnpm install @nestjs/event-emiiter

 

설치를 한 후에는 AppModule에 해당 모듈을 import를 하여 사용 준비를 한다.

// ** Nest Imports
import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';

// ** Custom Module Imports
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [EventEmitterModule.forRoot()],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

현재는 아무런 옵션을 주지 않았지만 실제로는 다양한 옵션을 줄 수 있다고 한다. 이 중에서는 message 이름을 wildcard로 처리할 수 있는 기능등이 존재한다.

EventEmitterModule.forRoot({
  // set this to `true` to use wildcards
  wildcard: false,
  // the delimiter used to segment namespaces
  delimiter: '.',
  // set this to `true` if you want to emit the newListener event
  newListener: false,
  // set this to `true` if you want to emit the removeListener event
  removeListener: false,
  // the maximum amount of listeners that can be assigned to an event
  maxListeners: 10,
  // show event name in memory leak message when more than maximum amount of listeners is assigned
  verboseMemoryLeak: false,
  // disable throwing uncaughtException if an error event is emitted and it has no listeners
  ignoreErrors: false,
})

 

02. Event Listener 설정

자 이제 Event를 구독하는 Class인 AppListener를 생성할 것이다. 생성하기에 앞서서 해당 Event가 주고 받은 데이터인 EventDto를 먼저 생성을 해주자.

export class EventDto {
  message: string;
}

 

그리고 나서는 AppListener를 아래와 같이 선언하여 Event를 구독을 가능하게 해두자.

 

아래 코드를 자세히 보면 @OnEvent() 메서드를 이용하여 app.event의 이름은 가진 event를 구독하는 것을 알 수 있고, 그에 대한 데이터로 EventDto를 받는다. 

 

사실 @OnEvent는 Provider나 Controller면 어디에 달아도 상관 없지만 나는 Event를 구독하는 Class인 Event Listener로 구분하여 작성하는 것을 선호한다.

// ** Nest Imports
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { EventDto } from './event.dto';

@Injectable()
export class AppListener {
  constructor() {}

  @OnEvent('app.event')
  handleEvent(event: EventDto) {
    console.log(event);
  }
}

 

자 이제 생성한 EventListener를 AppModule의 Provider로 등록하자.

 

// ** Nest Imports
import { Module } from '@nestjs/common';
import { EventEmitterModule } from '@nestjs/event-emitter';

// ** Custom Module Imports
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AppListener } from './app.listener';

@Module({
  imports: [EventEmitterModule.forRoot({})],
  controllers: [AppController],
  providers: [AppService, AppListener],
})
export class AppModule {}

 

 

 03. Event 발행

AppService에서 기존에 있던 getHello 메서드를 제거하고 Event를 발행하는 새로운 메서드를 생성한다.

 

우선 생성자를 이용하여 EventEmitter2를 DI한다. 이 클래스의 emit 메서드를 이용하여 Event를 발행할 수 있다.

 

EventEmiiter2의 emit 메서드는 @OnEvent에서 수신할 메세지 키를 이용하여 Event를 발행한다. 즉 이전에 AppListen의 handleEvent에서 작성한 app.event와 맞춰주면 된다.

 

그리고 마찬가지로 handleEvent 메서드에서 수신할 Dto를 이용하여 데이터를 보내주면 된다.

// ** Nest Imports
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';

@Injectable()
export class AppService {
  constructor(private readonly eventEmitter: EventEmitter2) {}

  /**
   * Message를 받아서 Event를 발행합니다.
   * @param message
   */
  public publishEvent(message: string): void {
    this.eventEmitter.emit('app.event', {
      message,
    });
  }
}

 

 

04. 테스트

자 이제 AppController에서 Get 요청이 올 경우에 AppService의 publishEvent 메서드를 호출하는 코드를 작성하고 해당 API를 호출한다.

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    this.appService.publishEvent('Hello world');

    return 'Hello World!';
  }
}

 

자 API를 테스트하면 handleEvent에 있는 console.log(event)의 코드로 잘 console이 찍히는 것을 알 수 있다.

 

 

05. 마무리하며

오늘은 NestJS의 Event 발행, 구독 기능에 대해 알아봤다. 해당 기능을 응용하면 좀 더 메서드에서의 관심사 분리가 좋은 것 같다고 생각한다.

 

필자는 최근 MSA로 구성된 프로젝트를 진행 중에 있는 데, 모든 request_log와 필요한 기록 데이터를 Log Server에 Rabbit MQ를 이용하여 이벤트를 발행하여 사용하는 데, 이 때 NestJS의 Event 기능과 함께 사용하여 관심사 분리에 이용하였다.

'B.E > Nest JS' 카테고리의 다른 글

NestJS V10 마이그레이션 + SWC  (0) 2023.10.11
Nestjs V10의 변화점  (0) 2023.10.11
Nestjs Swagger(1) - Swagger 설정  (0) 2023.10.09
[Nest JS] 파이프(Pipe)  (0) 2022.11.14
[Nest JS] 전화번호부 API 개발하기(2) - DTO, PIPE, Module  (0) 2022.08.24
profile

Tech Blog of Pinomaker

@pinomaker

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!