강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

luxleo님의 프로필 이미지
luxleo

작성한 질문수

실전에서 바로 써먹는 Kafka 입문

[실습] 이메일 발송을 처리할 Consumer 로직 짜기

UserSignedUpEventConsumer 에서 로그 DB 저장 문의

작성

·

36

0

말씀해주신 것처럼 컨슈머 로직 안에 로그를 디비로 저장하는 부분이 비동기 처리를 하기 위해 kafka를 도입하는 것과 상충하지 않는지 의문입니다.

 

@Service
public class UserSignedUpEventConsumer {

  private EmailLogRepository emailLogRepository;

  public UserSignedUpEventConsumer(EmailLogRepository emailLogRepository) {
    this.emailLogRepository = emailLogRepository;
  }

  @KafkaListener(
      topics = "user.signed-up",
      groupId = "email-service",
      concurrency = "3"
  )
  @RetryableTopic(
      attempts = "5",
      backoff = @Backoff(delay = 1000, multiplier = 2),
      dltTopicSuffix = ".dlt"
  )
  public void consume(String message) throws InterruptedException {
    UserSignedUpEvent userSignedUpEvent = UserSignedUpEvent.fromJson(message);
    
    String receiverEmail = userSignedUpEvent.getEmail();
    String subject = userSignedUpEvent.getName() + "님, 회원 가입을 축하드립니다!";
    Thread.sleep(3000);
    System.out.println("이메일 발송 완료");

    EmailLog emailLog = new EmailLog(
        userSignedUpEvent.getUserId(),
        receiverEmail,
        subject
    );

    emailLogRepository.save(emailLog);
  }
}

이 부분을 실무레벨에서 해결한다면 어떤 방법이 있는지 궁금합니다.

답변 2

0

JSCODE 박재성님의 프로필 이미지
JSCODE 박재성
지식공유자

안녕하세요 ! 질문 잘 해주셨어요 ~

질문해주신 내용에 답변 드려볼게요 !

 

우선 컨슈머 안에서 DB 저장을 하는 것 자체가 Kafka 도입 목적과 완전히 상충하는 것은 아니지만, 컨슈머가 해야 할 일이 너무 무거워지면 Kafka를 붙인 장점이 줄어들 수는 있어요 ~

UserService는 회원가입 DB 저장 후 user.signed-up 이벤트를 발행하고, EmailService 쪽에서 이를 받아 처리하도록 분리되어 있어서, UserService와 EmailService를 비동기로 분리했다는 점 자체는 잘 살아 있어요 !

다만 EmailService 내부로 들어와 보면, @KafkaListener 안에서 이메일 발송 작업을 하고, 그 다음 EmailLog를 생성해서 emailLogRepository.save(emailLog)로 저장하고 있어요 ! 또 재시도와 DLT도 함께 붙어 있죠 !

즉 현재 구조는 “서비스 간 비동기”는 맞지만, “컨슈머 내부 처리”는 동기적으로 순차 실행되는 구조라고 보시면 돼요 ~

 

"실무레벨에서 해결한다면 어떤 방법이 있는지 궁금합니다."

-> 실무에서도 컨슈머 내부에서 이메일 발송 후 바로 DB에 로그를 저장하는 패턴을 많이 사용해요 !

다만 이메일 발송 자체가 실패했을 때에도 로그가 저장되는 문제가 생길 수 있어서, 실무에서는 이메일 발송 성공 여부를 EmailLog에 함께 기록하거나, 발송 성공 시에만 저장하도록 예외 처리를 세밀하게 가져가는 방식을 많이 써요 ~

그리고 이미 @RetryableTopic과 DLT를 활용해서 재시도 및 실패 메시지 처리 로직을 넣어두었기 때문에, 이 구조 자체가 실무에서도 충분히 탄탄한 패턴이라고 보시면 돼요 :)

추가로 궁금하신 점 있으시면 언제든 편하게 질문 남겨주세요~~

0

안녕하세요, 인프런 AI 인턴입니다. luxleo님!

Kafka 컨슈머 로직에서 로그를 DB에 저장하는 부분이 비동기 처리를 목표로 하는 Kafka의 사용 목적과 상충할 수 있다는 의문을 가지고 계신 것 같습니다. DB에 동기적으로 저장하는 연산 때문에 전체 처리 속도가 저하될 수 있기 때문입니다. 이를 실무에서 해결하기 위한 방법에는 여러 가지 접근이 가능하며, 아래에 몇 가지 방향을 제안드립니다.

1. 비동기 저장 방식 사용

  • 비동기 처리를 위한 메시지 큐 사용: 이메일 발송 로그를 직접 DB에 저장하는 대신, 별도의 메시지 큐(Kafka나 RabbitMQ 등)를 사용하여 로그 저장 요청을 큐에 쌓고, 이를 처리하는 별도의 컨슈머가 비동기적으로 DB에 저장하도록 설계할 수 있습니다. 이를 통해 처리량이 많은 경우에도 빠르게 반응할 수 있습니다.

2. 배치 처리

  • 배치로 로그 저장: 일정량의 로그가 쌓이면 한 번에 DB에 저장하는 배치 처리를 통해 성능을 최적화할 수 있습니다. 이는 DB로의 네트워크 호출을 줄여주고, 부하를 더 예측 가능한 수준으로 유지할 수 있게 됩니다.

3. 개선된 DB 설정 및 사용

  • DB 트랜잭션 및 연결 최적화: 데이터베이스 연결 설정을 최적화하거나, 고성능 DB 인스턴스를 사용하여 저장 시간을 단축할 수 있습니다. 이를 통해 이메일 로그 저장 시의 오버헤드를 줄일 수 있습니다.

4. 캐싱 사용

  • 로그 캐싱: 실제 DB에 저장하기 전에 메모리나 Redis 같은 인메모리 데이터 저장소에 임시로 저장하여 동시성 처리 능력을 높일 수 있습니다. 캐싱을 통해 실시간 조회 성능과 병목 문제를 해소할 수 있습니다.

추가 자료

위에서 제안한 방법들은 각각의 장단점이 있으므로, 실제 시스템의 요구사항과 상황에 맞춰 선택하는 것이 중요합니다. 관련된 좀 더 심화된 적용 사례는 아래 유사한 질문에서 찾을 수 있습니다:

이 정보들이 luxleo님의 시스템 설계와 성능 최적화에 도움이 되기를 바랍니다. 현재 베타 기능으로서 만족스럽지 않을 수 있습니다만, 추가적으로 궁금하신 점이 있다면 언제든지 이어서 질문해 주세요. 곧 지식공유자께서 자세히 답변해 주실 것입니다. 🙏

luxleo님의 프로필 이미지
luxleo

작성한 질문수

질문하기