NestJS + Kafka를 이용한 RPC 응답 처리 구조

isntkyu·2024년 6월 10일
0

Back-End

목록 보기
6/10
post-thumbnail

NestJS기반 백엔드 서버에 카프카를 도입하고자 할 때,
kafkajs + @nestjs/microservices 를 조합해서 손쉽게 카프카를 사용할 수 있었습니다.

사용법


개요

외부 채널링을 구현하며 기존 예약 서버의 로직을 재활용할 필요가 있었습니다.
신규 서버는 리소스가 한정된 상태였고, Cron, 예약 API 등 여러 기능을 모놀리식하게 처리해야 했습니다.

이에 따라, 단일 서버 내 자원을 효율적으로 사용하기 위해 Non-Blocking한 동기 패턴을 Kafka를 통해 구현했습니다.


Usage

Producer 구성

OnModuleInit 인터페이스를 활용하여 응답 토픽을 미리 구독합니다.

export class Producer implements OnModuleInit {
  constructor(
    @Inject(KAFKA_CLIENT)
    private readonly clientKafka: ClientKafka
  ) {}
  
  async onModuleInit(): Promise<void> {
    this.clientKafka.subscribeToResponseOf(
      `foo.get`
    )
  }
}

foo.get.reply라는 응답 토픽도 사전에 생성해 두어야 합니다.

이후 send() 메서드를 통해 메시지를 전송하면 RPC 구조로 응답을 받을 수 있습니다.

const result = await lastValueFrom(
        this.clientKafka
          .send<
            KafkaReplyDto,
            KafkaProduceDto<FooGetDto>
          >(`foo.get`, {})
          .pipe(timeout(3000))
      )

이후 send() 메서드를 통해 메시지를 전송하면 RPC 구조로 응답을 받을 수 있습니다.

Consumer

  @MessagePattern(`foo.get`)
  async consumer(
    @Payload() data:  KafkaProduceDto<FooGetDto>,
    @Ctx() context: KafkaContext
  ) {
    return 'producer result에 들어갈 값'
  }

NestJS는 Kafka의 헤더를 활용하여 foo.get.reply에 자동 응답을 반환합니다.

✅ 헤더에는 응답 토픽명과 파티션 정보가 포함되어 있어, 정확한 응답을 보장합니다.


장점

•	비동기 구조를 유지하면서도 RPC 패턴으로 동기적 응답을 받을 수 있음
•	예약 로직에서 응답을 기다리는 동안 서버가 Block되지 않아 자원 활용도가 높아짐

한계점

멱등성

비동기 통신에서는 네트워크 이슈에 따른 중복 호출이 발생할 수 있습니다.
→ Producer / Consumer 모두 멱등성을 보장해야 합니다.

파티션 개수 제한

응답 토픽의 구독자는 consumer group 내 파티션 수만큼만 활성화됩니다.
→ 소비자 인스턴스 수 > 파티션 수이면 일부 인스턴스가 간헐적으로 메시지 유실이 생길 수 있습니다.

Blue/green 배포

ECS의 블루/그린 배포는 일시적으로 인스턴스를 2배로 늘립니다.
→ 블루, 그린이 동시에 떠있는 시점에 인스턴스 수가 파티션 수를 초과할 수 있습니다.

정리

NestJS의 Kafka RPC 구조는:
• 기존 로직을 재활용하며 확장성이 높고
• 서버 자원을 효율적으로 사용 가능하다는 장점이 있지만 복잡성이 높습니다.
• 응답 패턴 특성상 파티션-인스턴스 매핑, 배포 전략, 멱등성에 대한 설계가 반드시 필요합니다.

상황에 맞게 잘 활용하면 MSA 구조에서 유용한 선택지가 될 수 있습니다.

profile
backend

0개의 댓글