Conversation
There was a problem hiding this comment.
Code Review
이번 풀 리퀘스트는 리뷰가 등록되었을 때 인앱 알림 및 FCM 푸시 알림을 전송하는 기능을 ReviewService에 추가하고 관련 통합 테스트를 업데이트합니다. 리뷰어는 데이터베이스 트랜잭션 내에서 동기적으로 외부 FCM API를 호출하는 방식이 커넥션 풀 고갈 및 성능 저하를 유발할 수 있음을 지적했습니다. 이를 해결하기 위해 Spring의 ApplicationEventPublisher와 @Async, @TransactionalEventListener를 활용하여 알림 발송 로직을 비동기 이벤트 기반으로 분리할 것을 권장하는 유용한 피드백을 제공했습니다.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| try { | ||
| int sentCount = fcmNotificationService.sendToMember( | ||
| review.getRevieweeId(), | ||
| title, | ||
| content, | ||
| Map.of( | ||
| "type", "REVIEW_RECEIVED", | ||
| "reviewId", String.valueOf(review.getReviewId()), | ||
| "productId", String.valueOf(review.getProductId()), | ||
| "targetUrl", targetUrl | ||
| ) | ||
| ); | ||
| log.debug("Sent review push notification. reviewId={}, revieweeId={}, sentCount={}", | ||
| review.getReviewId(), review.getRevieweeId(), sentCount); | ||
| } catch (RuntimeException exception) { | ||
| log.warn("Failed to send review push notification. reviewId={}, revieweeId={}", | ||
| review.getReviewId(), review.getRevieweeId(), exception); | ||
| } |
There was a problem hiding this comment.
데이터베이스 트랜잭션(@Transactional) 범위 내에서 FCM 전송과 같은 외부 네트워크 I/O 작업을 동기적으로 수행하는 것은 심각한 성능 저하 및 커넥션 풀 고갈(Connection Pool Exhaustion)을 유발할 수 있습니다.
특히 외부 FCM API 호출이 지연되거나 장애가 발생할 경우, 데이터베이스 커넥션을 계속 점유하고 있어 전체 시스템의 장애로 이어질 수 있습니다. 또한, 트랜잭션이 최종적으로 롤백되더라도 이미 푸시 알림은 발송되어 데이터 일관성이 깨질 수 있습니다.
추가적으로, notifyReviewReceived 메서드가 인앱 알림 생성과 FCM 푸시 전송이라는 두 가지 책임을 모두 가지고 있어 **단일 책임 원칙(SRP)**을 위배하고 있습니다.
개선 제안:
Spring의 ApplicationEventPublisher를 사용하여 리뷰 등록 이벤트를 발행하고, 이를 @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)와 @Async를 조합하여 트랜잭션이 성공적으로 커밋된 이후에 비동기적으로 FCM 알림을 전송하도록 분리하는 것을 권장합니다.
예시:
// 1. 이벤트 클래스 정의
public record ReviewCreatedEvent(Review review, String targetUrl) {}
// 2. 서비스에서 이벤트 발행
eventPublisher.publishEvent(new ReviewCreatedEvent(review, targetUrl));
// 3. 비동기 이벤트 리스너 구현
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleReviewCreatedEvent(ReviewCreatedEvent event) {
// FCM 전송 로직 수행
}
🚀 Summary
notificationpayload가 있는 푸시는 브라우저/FCM 자동 표시만 사용하고,data-only푸시일 때만서비스워커가 직접 알림을 표시하도록 변경했습니다.
업데이트 확인을 강화했습니다.
✨ Description
변경 내용
푸시 알림 중복 표시 방지
기존에는 백그라운드 FCM 메시지를 받을 때 서비스워커가 항상
showNotification()을 호출했습니다.FCM 메시지에
notificationpayload가 포함된 경우 브라우저/FCM이 이미 알림을 자동 표시할 수 있기때문에, 서비스워커가 다시 알림을 띄우면 같은 푸시가 두 번 표시될 수 있었습니다.
이제는
payload.notification이 있으면 서비스워커가 직접 표시하지 않고 종료합니다.대신
data-only메시지처럼 자동 표시가 되지 않는 경우에만showNotification()을 fallback으로 호출합니다.
서비스워커 최신화 강화
FCM 토큰 등록 시 서비스워커를 등록할 때 다음 옵션과 업데이트 호출을 추가했습니다.
updateViaCache: "none"registration.update()이를 통해 브라우저가 캐시된 예전 서비스워커를 계속 사용하는 상황을 줄였습니다.
리뷰 작성 알림 추가
구매자가 거래 리뷰를 작성하면 판매자에게 인앱 알림과 FCM 푸시가 전송되도록 추가했습니다.
알림 클릭 대상은
/mypage/review입니다.🎲 Issue Number
close #122