NestJS Event Driven Pattern

이준영·2022년 5월 12일
2

우아한형제들 기술블로그에 회원시스템 이벤트기반 아키텍처 구축하기글이 올라왔습니다.링크
해당 글을 읽으면서 마이크로 서비스가 어떻게 동작하는지와 Event Driven Pattern의 강력한 이점을 알게되었습니다.
해당 글에서는 spring 기반으로 소개하고 있는데 간략하게 NestJS에서 Event Driven Pattern을 사용하는 법과 왜 사용하는지를 작성해보겠습니다.

NestJS에서는 EventEmitterModule 모듈을 제공하고 있습니다. 링크

Event 적용

Event 적용전

@Injectable()
export class UserService {
    constructor(
        private readonly userRepository: UserRepository,
    ) {}
    async createUser(username: string, password: string, email: string) {
        const user = new User()
        user.id = randomUUID()
        user.username = username
        user.password = password // 실제로는 암호화된 비밀번호를 저장해야 합니다.
        user.email = email
        await this.userRepository.save(user)
      	// 유저에게 이메일 발송등 그외의 여러 회사 정책 비즈니스 로직을 수행합니다.
        // ....
        // ....
    }
}

많은 예제로 볼 수 있는 형태의 코드입니다.

Event 적용후

...
...
    async createUser(username: string, password: string, email: string) {
        const user = new User()
        user.id = randomUUID()
        user.username = username
        user.password = password // 실제로는 암호화된 비밀번호를 저장해야 합니다.
        user.email = email 
        await this.userRepository.save(user)
      	// User 생성후 이벤트를 발생시킨다
      	this.eventEmitter.emit(
            UserCreatedEvent.EVENT_NAME,
            new UserCreatedEvent(user),
        )
    }
}

유저를 생성 하고 UserCreatedEvent를 발생 시킵니다.
이제 유저에게 메일 등을 보내는 기능이 필요하다면 아래와 같이 추가 할 수 있습니다

@Injectable()
export class UserCreatedListener {
    @OnEvent(UserCreatedEvent.EVENT_NAME, { async: true })
    async handleSendEmail(event: UserCreatedEvent) {
        // 유저에게 메일을 보내는 코드를 작성합니다.
    }
}

Event 장점

유저생성시 포인트를 지급하는 정책이 새로 생겼다고 가정하겠습니다.

Event 미적용

async createUser(username: string, password: string, email: string) {
  ...
  ...
  await this.userRepository.save(user)
  // 유저에게 이메일 발송등 그외의 여러 회사 정책 비즈니스 로직을 수행합니다.
  // ....
  // ....
  // 유저에게 포인트를 지급한다.
}

정책이 변경될때마다 createUser가 변경되는 약점을 가지고 있습니다.
이는 버그를 유발할 수 있고 테스트 코드 또한 수정을 해야하는 번거로움이 생기게 됩니다.

Event 적용

@Injectable()
export class UserCreatedListener {
    OnEvent(UserCreatedEvent.EVENT_NAME, { async: true })
    async handleSendEmail(event: UserCreatedEvent) {
        // 유저에게 메일을 보내는 코드를 작성합니다.
    }
  	@OnEvent(UserCreatedEvent.EVENT_NAME, { async: true })
    async handleWelcomePoint(event: UserCreatedEvent) {
        // 유저에게 포인트를 지급한다.
    }
}

createUser로직의 변화 없이 새로운 비즈니스 로직을 추가할 수 있습니다.

Event 주의사항

@OnEvent에서 처리되는 동작은 동작을 보장하지 않아 동작을 보장하게하는 추가적인 작업이 필요합니다.

마무리

Event Driven Pattern이 가지고 있는 강력한 이점이 보이시나요?
실제 회사 내에서는 수많은 정책 변경이 일어나고 이에 따라 개발자는 다양한 로직을 추가하고 변경해야 합니다. 이러한 상황에서 개발자는 상황에 따라 프로젝트에 Event 패턴을 적용한다면 개방폐쇄의 원칙을 지키게 되고 이는 프로그램을 더욱더 견고하게 확장할 수 있다고 생각합니다.

profile
NodeJS 개발자

0개의 댓글