Skip to content

spring eventListener

qbsb147 edited this page Mar 21, 2026 · 2 revisions

⚡ Chat 메시지 읽음 처리 이벤트

📌 목적

메시지 조회 시 읽음 처리 작업을 비동기로 분리하여 빠른 응답 시간(Latency) 제공

🧩 사용되는 매개변수

public record ChatReadEvent(Long roomNo, UUID publicUuid) {}
불변 객체 record 활용

  • roomNo : 채팅방 식별 번호
  • publicUuid : 사용자 식별 UUID

🔹 메시지 조회 시 이벤트 발행

@Override
public Page<MessageDto.Response> getMessages(Long roomNo, Pageable pageable) {
    ChatRoom chatRoom = chatRoomRepository.findById(roomNo)
            .orElseThrow(() -> new NotFoundException("해당 채팅방을 찾을 수가 없습니다."));
    Page<MessageProjection> chatMessages = chatMessageRepository.findMessagesWithUnreadAndMember(roomNo, pageable);

    UUID publicUuid = jwtTokenProvider.getPublicUuidFromToken();

    // 스프링 이벤트 발행
👉  applicationEventPublisher.publishEvent(new ChatEvent.ChatReadEvent(roomNo, publicUuid));

    return MessageDto.Response(chatMessage);
}
  • applicationEventPublisher.publishEvent() : 전달된 매개변수를 기준으로 등록된 이벤트 리스너 호출
  • 메시지 조회 응답은 즉시 반환, 읽음 처리는 비동기 수행

🏗️ 이벤트 리스너 구현

@Async
@EventListener
public void handleChatReadEvent(ChatEvent.ChatReadEvent event) {

    int retryCount = 0;
    long waitTime = 1000L; // 1초

    while(retryCount < MAX_RETRY){
        try {
            messageReadStatusRepository.markMessageRead(event.roomNo(), event.publicUuid());

            log.info("읽음 처리 완료: room={}, user={}", event.roomNo(), event.publicUuid());
            return;

        } catch (Exception e) {
            retryCount++;
            log.error("읽음 처리 실패: room={}, user={}", event.roomNo(), event.publicUuid(), e);

            if(retryCount < MAX_RETRY){
                try {
                    Thread.sleep(waitTime);
                    waitTime *= 2; // 지수적 백오프
                } catch (InterruptedException ex){
                    Thread.currentThread().interrupt();
                    log.error("재시도 대기 중 인터럽트 발생", ex);
                    return;
                }
            }
        }
    }
}

🔹 주요 특징

  • @Async : 이벤트 처리 메서드를 비동기로 실행
  • @EventListener : 이벤트를 수신하는 리스너 정의
  • 재처리 정책 (Pull 방식)
    • retryCount : 최대 재시도 횟수(MAX_RETRY)
    • waitTime : 재시도 대기 시간, 실패 시 2배로 증가 (지수적 백오프)
    • 인터럽트 발생 시 현재 스레드에 신호 전달

⚙️ 동작 흐름

  1. 클라이언트 요청 → 메시지 조회
  2. 읽음 처리 이벤트 발행 (ChatReadEvent)
  3. 이벤트 리스너 handleChatReadEvent 비동기 실행
  4. 읽음 처리 성공 시 로그 기록
  5. 실패 시 최대 재시도 횟수까지 재시도, 실패하면 종료

📡 이벤트 스트리밍

⚙️ 개발 환경 구축

Websocket

Kafka

Redis

Debezium

🧩 기능 구현

이벤트리스너(Spring)

Pub/Sub 기반 메세지 처리(Redis)

이벤트 스트리밍 처리(Kafka)

CDC(Debezium)

🧠 개념

🎯 설계 패턴

🏗️ 아키텍처

📚 기술 스택

Clone this wiki locally