[Feat] FCM 푸시 알림 구현#12
Conversation
howooyeon
left a comment
There was a problem hiding this comment.
수고하셨슴다 😁
머지 전에 미사용 import 이런거 함 싹 확인해주심 될듯요
| // 6. 비동기 발송 위임 | ||
| dispatcher.dispatch(new NotificationDispatchCommand( | ||
| notification.getId(), | ||
| receiverId, | ||
| type, | ||
| title, | ||
| body, | ||
| params, | ||
| tokens | ||
| )); |
There was a problem hiding this comment.
@transactional 메서드 안에서 dispatcher.dispatch()를 호출하는 구조인데 DB 커밋 전에 비동기 스레드가 FCM을 발송할 수 있습니다. 트랜잭션이 롤백되면 Notification 이력은 없는데 알림이 보내진 상태가 될거같습니다.
There was a problem hiding this comment.
TransactionSynchronization 사용해서 aftercommit 하거나 TransactionalEventListener 패턴은 어떠신가요~
There was a problem hiding this comment.
맞네요! TransactionalEventLisener로 커밋 후에만 푸시 발송되도록 수정하겠슴다
| @Transactional | ||
| void deleteByToken(String token); |
There was a problem hiding this comment.
derived delete 쿼리는 내부적으로 findByToken과 delete를 실행하고 Repo에서 직접 Transactional 선언할 시에 서비스 레이어의 트랜젝션과는 별도 동작할 수 있기 때문에 문제가 될 거 같습니다.
서비스 레이어에서 관리를 하거나 @Modifying @Query("DELETE FROM FcmToken f WHERE f.token = :token")로 벌크 삭제 어떤가요?!
There was a problem hiding this comment.
좋습니다! 레포지토리의 @transactional 제거 + 트랜잭션은 서비스에서만 관리하게 하고,
@Modifyin @query로 변경해서 SELECT 없이 DELETE 되게 수정하겠슴다
| @Configuration | ||
| public class FirebaseConfig { | ||
|
|
||
| private static final String SERVICE_ACCOUNT_PATH = "firebase/semosan-b2593-firebase-adminsdk-fbsvc-0d55d3b4e9.json"; |
There was a problem hiding this comment.
좋습니다! 우선 yaml로 뺴두는걸로 수정해두겠습니당
| @DeleteMapping("/tokens") | ||
| public ResponseEntity<ApiResponse<Void>> delete( | ||
| @Valid @RequestBody FcmTokenDeleteRequest request | ||
| ) { | ||
| fcmTokenService.delete(request.token()); | ||
| return ApiResponse.success(SuccessStatus.FCM_TOKEN_DELETE_SUCCESS); |
There was a problem hiding this comment.
이거 인증없이 토큰 삭제할 수 있을 거 같아서
@AuthenticationPrinciapl 이거 추가해야 할 거 같아요
| // 이거 삭제해야할 듯 -> fcm_tokens 테이블을 따로 뺌 | ||
| // 충돌날까봐 아직 건드리진 않음 |
| executor.setThreadNamePrefix("notif-async-"); | ||
| // 큐 가득 차면 호출 스레드가 직접 실행 → 메시지 유실 방지 | ||
| executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); | ||
| executor.initialize(); |
There was a problem hiding this comment.
스프링 빈이 알아서 초기화 해준다고는 해서 수정 안해도 되긴 하지만
이중 호출이래요
There was a problem hiding this comment.
그러면 initialize는 뺴는게 좋겠네요 수정해두겠슴다
pooreumjung
left a comment
There was a problem hiding this comment.
고생하셨습니당~
호연 누나가 리뷰한것들만 고쳐서 머지하면 될 것 같아요
🧾 요약
notificationService.send()한 줄로 알림을 발송할 수 있고, 한 유저의 여러 기기에 동시 발송됩니다.NotificationDispatcher인터페이스로 추상화했습니다.🔗 이슈
✨ 변경 내용
인프라
firebase-admin:9.8.0의존성 추가FirebaseConfig: 앱 시작 시 Firebase 초기화 (서비스 계정 JSON 로드)AsyncConfig: 알림 전용 스레드풀 (notificationTaskExecutor) +@EnableAsyncFcmService:FirebaseMessaging.send()래퍼 (notification + data 페이로드 지원)도메인
NotificationTypeenum: 알림 종류별 title/body 템플릿 + 필수 파라미터 검증COMMUNITY_COMMENT테스트 이넘 넣어둠FcmToken엔티티: 한 유저당 여러 기기 토큰 저장 (token unique)Notification엔티티: 알림 이력 (extras는 jsonb)발송 시스템
NotificationDispatcher인터페이스 +NotificationDispatchCommand(record)AsyncNotificationDispatcher:@Async기반 구현체. 만료 토큰(UNREGISTERED/INVALID_ARGUMENT) 자동 삭제NotificationService: 팀원 입구. 받는 사람/파라미터 검증 → 이력 저장 → Dispatcher 위임API
POST /api/fcm/tokens— 토큰 등록 (JWT 인증, 같은 토큰이면 소유자/디바이스 갱신)DELETE /api/fcm/tokens— 토큰 삭제 (로그아웃 시)POST /api/notifications/test— 테스트 발송 (@Profile("local")로 운영 자동 비활성화)응답 코드
FCM_200_1/FCM_200_2/NOTIF_200_1— 성공USER_404_1— 받는 사람 없음NOTIF_400_1— 필수 파라미터 누락🧪 테스트 방법
테스트 로그인으로 JWT 발급
POST /api/auth/test/login클라이언트(웹/앱)에서 FCM 토큰 발급
토큰 등록
POST /api/fcm/tokens알림 발송 (본인에게 푸시 도착 확인)
POST /api/notifications/test📌 사용 예시
도메인 서비스에서 아래 함수로 전송 가능
✅ 확인
📝 참고