안녕하세요.
nestjs + sqs 조합으로 서비스를 개발해오면서 nestjs 생태계의 다른 sqs 패키지들을 쓰고 있었는데요.
실제 프로덕션에서 겪은 불편함들을 몇가지 가지고 있었고, 특정 패키지에는 PR도 제출하였지만, 더이상 업데이트가 되지 않고 있거나, 이를 문제라고 인지하지 않는 곳들도 있어, 이를 직접 해결하고자 모듈을 만들었습니다. 실제로 기존에 사용하던 sqs 모듈을 이걸로 교체하여 안정적으로 사용중입니다.
nestjs에서 sqs 모듈을 찾고 계신분들, 그리고 저랑 비슷한 고민이 있으셨던 분들에게 도움이 될 것 같아 공유합니다.

이 패키지는 다음과 같은 특징이 있습니다

nestjs 친화적

nestjs에서 동적으로 모듈을 생성하여 import하고, 데코레이터를 장착한 핸들러를 주입하여 쉬운 구현이 가능합니다
다른 대부분의 sqs 패키지들에서의 것과 비슷한 시그니처를 가지고 있습니다

@Module({  
  imports: [  
    ConfigModule.forRoot(),  
    SqsModule.registerAsync({  
      inject: [ConfigService],  
      useFactory: (config: ConfigService) => {  
        const region = config.getOrThrow<string>('AWS_REGION');  
        const queueUrl = config.getOrThrow<string>('ORDERS_QUEUE_URL');  
        const defaultSqsClient = new SQSClient({ region });  
  
        return {  
          defaultSqsClient,  
          consumers: [{ name: 'orders', queueUrl }],  
          producers: [{ name: 'orders', queueUrl }],  
        };  
      },  
    }),  
  ],  
})  
export class AppModule {}  
  
@Injectable()  
export class OrderQueueHandler {  
  @SqsMessageHandler('orders')  
  public async onMessage(message: Message): Promise<Message> {  
    // return message to ack/delete  
    return message;  
  }  
  
  @SqsConsumerEventHandler('orders', 'processing_error')  
  public onProcessingError(error: Error, message: Message) {  
    // report error  
  }  
}  

최신의 bbc 패키지 사용

이 패키지는 주간 130만 다운로드가 넘으며, nodejs 진영에서는 sqs 라이브러리로 가장 유명한 아래의 두개 라이브러리의 최신 버전을 기반으로 하고 있습니다

https://github.com/bbc/sqs-producer
https://github.com/bbc/sqs-consumer

sqs-consumer에는 v14에서부터 의미있는 breaking change가 존재합니다
https://github.com/bbc/sqs-consumer/discussions/584

13버전이하에서는 sqs handler가 void 를 리턴하여도 ack behaviour로 간주하였습니다
하지만 14버전부터는 명시적으로 sqs handler가 리턴한 message를 ack으로 간주합니다
이 변경은 더 명시적이고 예상가능하며, 유저에게 ack에 대한 구현에 있어 혼란을 주지 않는 의미있는 변경입니다
하지만 아직 시중의 nestjs sqs 모듈이라고 하는 대부분의 npm 패키지들은 일단 sqs-consumer 13 이하버전을 사용하고 있습니다

부트타임 검증

앱 구동시점에 잘못된 설정을 즉시 잡아냅니다 더 안전하게 사용이 가능합니다.

  • 중복 consumer/producer 이름을 감지
  • 존재하지 않는 consumer에 데코레이터를 사용하면 즉시 에러
  • 이벤트명 오타 감지
  • batch/single 핸들러 파라메터 타입 불일치 감지
  • batch/single의 반환값 타입 불일치 감지

graceful shutdown

bbc/sqs-consumer는 graceful shutdown을 위해 pollingCompleteWaitTimeMs 옵션을 제공합니다
그리고 nestjs에서는 nestjs의 lifecycle을 가지고 있습니다
nestjs의 onModuleDestroy 단계에서 내부적으로 각 consumer가 stopped 이벤트를 발행할때까지 종료를 기다립니다.
consumer가 처리중이던 메시지가 끝나야 이 이벤트가 발생하기 때문에 메시지 처리가 완료되고 안전하게 프로세스가 종료됩니다
nestjs의 생명주기와 자동으로 연결되어있기 때문에
app.enableShutdownHooks()을 켜두고,
pollingCompleteWaitTimeMs, shutdownTimeoutMs만 잘 설정해두시면 k8s 환경에서도 갑작스런 종료에도 좀더 안전하게 사용이 가능합니다

프로덕션에서 sqs를 사용하면서 이건 언제 업데이트 되는거지 하고 느낀것들이 계속 업데이트가 되지 않고 있어,
견고한 개발 문화를 만들어가는데 애로사항이 있어 해결한 결과물입니다
비슷한 환경에서 비슷한 고민을 하고 계신 분들이라면 한번 써보시고 피드백 해주시면 감사하겠습니다