forked from MinePing/Final
-
Notifications
You must be signed in to change notification settings - Fork 0
spring eventListener
qbsb147 edited this page Mar 21, 2026
·
2 revisions
메시지 조회 시 읽음 처리 작업을 비동기로 분리하여 빠른 응답 시간(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배로 증가 (지수적 백오프)
- 인터럽트 발생 시 현재 스레드에 신호 전달
- 클라이언트 요청 → 메시지 조회
- 읽음 처리 이벤트 발행 (ChatReadEvent)
- 이벤트 리스너 handleChatReadEvent 비동기 실행
- 읽음 처리 성공 시 로그 기록
- 실패 시 최대 재시도 횟수까지 재시도, 실패하면 종료