-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[#23] PUSH 메시지 전송 기능 개발 #24
Conversation
프론트 단은 어떤 이벤트가 일어났을 때 어떤 일을 한다 정도밖에 몰라 정말 동작만 하는 데모 앱 수준이라 자세한 설명이 어렵습니다. ㅠ |
src/main/java/com/flab/just_10_minutes/message/fcm/infrastructure/FcmApiClient.java
Outdated
Show resolved
Hide resolved
...in/java/com/flab/just_10_minutes/message/fcm/infrastructure/request/FcmMessageV1Request.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/controller/FcmController.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/domain/FcmMessage.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/controller/FcmController.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/controller/FcmController.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/infrastructure/FcmApiClient.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/infrastructure/FcmApiClient.java
Outdated
Show resolved
Hide resolved
...main/java/com/flab/just_10_minutes/message/fcm/infrastructure/repository/FcmTokenMapper.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/service/FcmService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/service/FcmService.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
�## 메시지 전송 조언
- 트랜잭션 이벤트 발행 + 미처리 항목 폴링 : fcm_messages에 저장 트랜잭션이 커밋 되면 이후 이벤트로 메시지 전송을 처리 함. 폴링 패턴보다 널널한 주기로 미처리 항목 메시지 전송
일단 위 방식을 저도 생각했습니다!
스프링 이벤트를 사용하여 트랜잭션이 종료되고 난뒤에 스프링 이벤트를 subscribe하여 메시지를 전송하면 좋을듯합니다. 재시도는 여러 방식이 있을텐데 스프링 이벤트를 subscribe하는 곳에서 재시도를 10번 하셔도 되고, 아예 별도 스케줄러가 트랜잭션 아웃박스 테이블에서 재시도 대상을 순회하여 재시도하는 방법도 있습니다.
24/11/10 추가 내용Transactional Outbox 에 대해서주제 : PUSH 메시징 서비스에 아웃박스 패턴이 필요할까? 라는 내용을 고민 해보았습니다.Transactional Outbox 패턴이란?
Transactional Outbox 패턴을 왜 써야할까?
우리 서비스는 Transactional Outbox 패턴이 필요할까?(이벤트 동기화가 필요할까?)Transactional Outbox 패턴을 공부해보니 같은 DB를 사용하고 있는 서비스에서 Outbox 패턴이 필요할까? 라는 의문이 들었습니다.
VER 1 : API 호출로만 PUSH 메시지 전송을 할 때API 호출로만 PUSH 메시지를 전송하면 아래의 문제가 발생합니다.
VER 2 : 메시지 전송을 비동기 이벤트로 전환했을 때
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleCompleteOrderEvent(OrderCompleteApplicationEvent event) {
...
}
VER 3 : Transactional Outbox 패턴을 적용했을 때
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
- FcmApiClient 메시지 전송 요청 후 ObjectMapper 빈 생성
(링크 : [#23] PUSH 메시지 전송 기능 개발 #24 (comment)) - FcmApiClient 메시지 전송 요청 후 실패 시 재시도 처리
- Fcm 서버와 통신 이후 HttpStatus 코드에 따른 재시도 처리
src/main/java/com/flab/just_10_minutes/message/fcm/service/FcmService.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/util/executor/AsyncConfig.java
Outdated
Show resolved
Hide resolved
.../com/flab/just_10_minutes/message/fcm/infrastructure/fcmAPiV1/response/FcmApiV1Response.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/infrastructure/fcmAPiV1/FcmApiClient.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/util/message/fcm/FcmConfig.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/service/FcmService.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/message/fcm/service/FcmService.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
src/main/java/com/flab/just_10_minutes/util/executor/AsyncConfig.java
Outdated
Show resolved
Hide resolved
...ain/java/com/flab/just_10_minutes/message/fcm/service/eventHandler/MessageEventListener.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO
- FcmApiClient 메시지 전송 요청 후 ObjectMapper 빈 생성
(링크 : [#23] PUSH 메시지 전송 기능 개발 #24 (comment)) - FcmApiClient 메시지 전송 요청 후 실패 시 재시도 처리
- Fcm 서버와 통신 이후 HttpStatus 코드에 따른 재시도 처리
- 메시지 중복 발행 시 정확히 한 번 처리
- 미발행 이벤트 스케줄러 생성
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변경 사항
- Fcm -> Notification 으로 전체적인 구조 변경
- Fcm은 Notification의 서브 테스크로(채널) 로 변경
- 동적인 채널 추가를 위해 OOP 원리 적용
- 이벤트 분리
- 회원 정보 확인 후 NotificationEvent 발행
- 회원이 선택한 전송 방법 선택 후 전송 방법 별 전송 이벤트 발행
- 전송
@Transactional | ||
public void publishNotificationEvent(NotificationRequest notificationRequest) { | ||
User user = userDao.fetch(notificationRequest.getLoginId()); | ||
Campaign campaign = campaignDao.fetchById(notificationRequest.getCampaignId()); | ||
|
||
NotificationEvent event = notificationEventDao.save(NotificationEvent.from(issueEventId(), notificationRequest.getLoginId(), notificationRequest.getCampaignId())); | ||
eventPublisher.publishEvent(PublishNotificationEvent.builder() | ||
.eventId(event.getEventId()) | ||
.build()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바뀐 Notification 로직에 대해 설명 드리겠습니다.
제가 생각한 메시지 전송이 필요한 사례는 두 가지 입니다.
- 특정 비지니스 로직에서 메시지 전송 요청(ex. 주문 완료 후 메시지 전송)
- 재고 알림 신청, 특정 시간에 알림 신청처럼 등록한 회원들에게 동시에 메시지 요청
메시지 전송은 단시간에 많이 올수도 한 건만 올 수도 있습니다.
딜레이를 최소화 하기 위해 메시지 전송 요청이 오면 회원 정보와 캠페인(메시지 양식)만 확인하고 이벤트 저장소(RDBMS)에 저장 후 해당 이벤트를 발행해 별도의 스레드에서 처리합니다.
다른 서비스, 컨트롤러를 통한 메시지 전송 요청은 이벤트 저장소에 저장 후 바로 리턴 하므로 많은 양을 처리할 수 있는 구조라고 생각했습니다.
이벤트는 트랜잭션 종료 후 시작 되거나 미발행 스케줄러에 의해 발행해 최소 한 번 전송을 보장하려 합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코멘트 주셨던 이벤트 발행 후 subscriber에서 이벤트 처리, 저장 등 방법은
기존 비지니스(ex. 주문)와 메시지 전송 비지니스의 결합을 분리하기 위해 이벤트 발행 후 subscriber에서 처리하는 것으로 이해 했습니다.(메시지 전송이 주요 비지니스가 아니므로 결합을 분리)
하지만 메시지 전송이 요청 되었으면 최소 한 번 전송을 보장하기 위해 이벤트 저장 -> 이벤트 발행 구조로 만들었는데(이벤트 유실을 막기 위해) 의도하신 바와 조금 다르지만 이렇게 구현해도 괜찮을지? 피드백 부탁드립니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 이벤트 발행 로직이 최대한 기존 비즈니스 로직과 결합이 없다면 어떤 방식이든 상관은 없습니다.
다만, 위 코드에서 notificationEventDao.save() 로직도 subscribe 쪽에서 할 수 있지 않을까요? PublishNotificationEvent가 eventId만 갖는 것이 아니라 알림의 필요한 정보를 가지고 있는 패턴을 기대했습니다.
이렇게 해도 되는 이유는 추후 publish 로직은 메시지 큐로 send하는 로직으로만 바꿔칠 예정이기 때문이에요.
일반적으로 이벤트 유실은 produce 실패가 아니라 consume에서 읽다 실패했을 때를 유실이라고 합니다. 그래서 consume에서 DB에 넣거나 로깅을 하거나 재시도를 잘하면 됩니다. (메시지 큐는 consume에서 실패해도 dlq 라는 곳에 저장시키므로 유실 방지한다고 함.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음에는 publisher, subscriber 측 모두 고민했는데, 피드백 주신 내용을 몇 번 읽어보니 이벤트 유실에 대해 잘 이해하게 되었습니다.
말씀해주신 내용은 적용했고, 좀 더 단순한 구조로 변경했습니다. 밑에서 말씀드릴게요!
@Async(EVENT_HANDLER_TASK_EXECUTOR) | ||
@Transactional(propagation = Propagation.REQUIRES_NEW) | ||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void handlePublishNotificationEvent(PublishNotificationEvent event) { | ||
NotificationEvent notificationEvent = notificationEventDao.fetchByEventId(event.getEventId()); | ||
Campaign campaign = fcmCampaignDao.fetchById(notificationEvent.getCampaignId()); | ||
User user = userDao.fetch(notificationEvent.getReceiverId()); | ||
|
||
for(NotificationChannel channel : notificationChannels) { | ||
if (channel.isApplicable(user)) { | ||
channel.createNotification(user, notificationEvent, campaign); | ||
|
||
eventPublisher.publishEvent(channel.createChannelEvent(event.getEventId())); | ||
} | ||
} | ||
|
||
notificationEventDao.patch(notificationEvent.update(true)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
첫 번째 이벤트 구독 계층입니다.
앞서 말씀드린대로 기존 트랜잭션을 여전히 보유하고 있지만 추가로 DB 작업이 필요하다면 새로운 트랜잭션을 시작해야 합니다.
이벤트를 복구하고 사용자가 메시지를 받을 수 있는 수단(ex. PUSH 알림, 카카오, 메일 등)이 있다면 채널을 통해 새로운 이벤트를 발행합니다.
각각의 채널은 다형성의 원리를 적용해 쉽게 추가할 수 있도록 구성 했습니다.
구현체로 FcmChannel과 MockChannel이 있습니다.(MockChannel은 다형성을 적용한 채널 예시로 넣었습니다. 다음 PR에 삭제 하겠습니다.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notification notification = notificationDao.fetchByEventIdAndChannelType(event.getEventId(), event.getChannelType()); | ||
Campaign campaign = fcmCampaignDao.fetchById(notification.getCampaignId()); | ||
|
||
FcmApiV1Response fcmApiV1Response = fcmApiClient.sendMessage(notification, campaign); | ||
if (fcmApiV1Response.getCode() != 200) { | ||
/*TODO : error status에 따른 재시도 처리 | ||
|
||
401 : Google Credential error | ||
400 : invalid argument | ||
403 : permission denied | ||
429 : quota exceeded(메시지 대상에 대한 전송 제한 초과) | ||
504 : unavailable(서버 과부하) | ||
500 : internal | ||
*/ | ||
throw new BusinessException("Failed to reason : " + fcmApiV1Response.getMessage()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 번째 이벤트 구독 계층 입니다.
첫 번째 계층에서 발행한 각각의 전송 채널을 통해 메시지 전송이 시작 됩니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각각의 채널에서 생성된 Notification은 담당 이벤트핸들러가 받아서 처리하며, 이 역시 NotificationEventListener처럼 비동기, 별도의 트랜잭션으로 동작합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이게 어떤 기준으로 2차로 가는건가요?
PushNotification만 보면 1차로 해도 되는 것 같은데, PushNotification에서 왜 다시 fcmEvent으로 들어가야하는지 잘 이해가 안갑니다..!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 저의 두 가지 착각으로 인해 이렇게 설계했습니다.ㅠㅠ
- Notification 도메인 측에서 다양한 방법(ex. PUSH, 메일, SMS 등등)을 모두 보내야 한다.
- 사용자가 알림 받을 방법으로 PUSH, SMS, 카카오 등을 모두 선택했다면 알림 서비스에서 모두 자동으로 처리해야한다.
이렇게 생각하고 1차에서는 사용자 여부만 확인하고 사용자가 동의한 알림 방법에 따라 병렬적으로 이벤트(PUSH, SMS, 카카오)를 병렬적으로 전송 이벤트를 발행하고자 했습니다.
지금 하고있는 타임딜 서비스에서는 필요 없어보이기에 다시 FCM만 하도록 수정 했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토요일에도 잠깐 말씀 드렸지만 만약 위와 같이 알림 전용 서비스를 구현하고자 한다면,
별도의 서비스에서 각각의 전송 방법을 트리거 할 수 있고, 알림 서비스는 단순하게 전송을 잘 한다로 설계하는 것이 좋을 것 같습니다. :)
src/main/java/com/flab/just_10_minutes/notification/domain/Notification.java
Outdated
Show resolved
Hide resolved
@PostMapping("/publish") | ||
public void publishMessage(@RequestBody NotificationRequest notificationRequest) { | ||
notificationService.publishNotificationEvent(notificationRequest); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요건 언제 호출되나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요건 알림 API 실행용으로 만들었습니다.
타임딜 서비스에서 알림은 주문 로직이 완료된 이후, 재입고 알림을 신청했을 때 사용자들에 보낼 경우가 있을 듯 한데
후자의 경우 트리거가 호출할 수 있는 API가 필요하다고 생각해서 만들었습니다.
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class FcmNotificationChannel implements NotificationChannel{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이름만 보면 Domain으로 느껴지네요. 이건 무슨 계층이라고 봐야할까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Channel은 채널대로의 도메인으로 정의하고, dao 로직은 적절히 서비스에서 처리하도록 구현해 보면 좋겠어요 :)
@@ -0,0 +1,5 @@ | |||
package com.flab.just_10_minutes.notification.service.event; | |||
|
|||
public interface ChannelEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 왜 서비스 레이어에 정의되어있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다시 수정한 내용 밑에서 말씀드리겠습니다.
Notification notification = notificationDao.fetchByEventIdAndChannelType(event.getEventId(), event.getChannelType()); | ||
Campaign campaign = fcmCampaignDao.fetchById(notification.getCampaignId()); | ||
|
||
FcmApiV1Response fcmApiV1Response = fcmApiClient.sendMessage(notification, campaign); | ||
if (fcmApiV1Response.getCode() != 200) { | ||
/*TODO : error status에 따른 재시도 처리 | ||
|
||
401 : Google Credential error | ||
400 : invalid argument | ||
403 : permission denied | ||
429 : quota exceeded(메시지 대상에 대한 전송 제한 초과) | ||
504 : unavailable(서버 과부하) | ||
500 : internal | ||
*/ | ||
throw new BusinessException("Failed to reason : " + fcmApiV1Response.getMessage()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이게 어떤 기준으로 2차로 가는건가요?
PushNotification만 보면 1차로 해도 되는 것 같은데, PushNotification에서 왜 다시 fcmEvent으로 들어가야하는지 잘 이해가 안갑니다..!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
알림 피드백 적용한 최종본(희망) 입니다.
- 알림 이벤트에 유의미한 필드 추가
- 알림 이벤트 consumer 측에서 검증, 수행할 수 있도록 수정
- 알림 계층 간소화(1차, 2차 이벤트 -> FcmEvent)
- FCM response 5XX 시 재시도 로직 추가
public void handleFcmNotificationEvent(FcmNotificationEvent event) { | ||
FcmToken fcmToken = fcmTokenDao.fetchByLoginId(event.getReceiverId()); | ||
Campaign campaign = campaignDao.fetchById(event.getCampaignId()); | ||
FcmNotification fcmNotification = FcmNotification.from(event, fcmToken); | ||
|
||
FcmApiV1Response fcmApiV1Response = fcmApiClient.sendMessage(fcmNotification, campaign); | ||
|
||
handelFcmResponse(fcmApiV1Response, fcmNotification); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
알림 관련 피드백 적용한 최종본 입니다.
public class FcmNotificationEvent {
private String eventId;
private String receiverId;
private Long campaignId;
}
Fcm 이벤트는 알림을 만들기 위핸 정보를 포함해 발행합니다.
public void Order(...) {
...
order.complete();
eventPublisher.publishEvent(FcmNotificationEvent.from(issueEventId(),
order.getBuyerId(),
campaign.getId()
));
}
알림 이벤트를 생성하는 곳에서는 기존 비니지스와 결합을 최소화 했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이벤트 consumer 측에서 아래를 수행합니다.
- 검증(토큰 검증, 캠페인 검증)
- 외부 API 전송
- response 200 시 fcm_notifications 테이블에 저장
3-1. 5XX 시 3번 재시도
3-2. 나머지 오류는 예외 던지기
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 제가 딱 상상한 그림이었어요! 고생하셨습니다.
@Retryable( | ||
value = {HttpServerErrorException.class}, | ||
maxAttempts = 3, | ||
backoff = @Backoff(delay = 1000, multiplier = 2) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재시도를 위해 @retryable을 적용 했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재시도 동작 매커니즘을 설명해보실 수 있나용?
src/main/java/com/flab/just_10_minutes/notification/infrastructure/fcmAPiV1/FcmApiClient.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 ㅎㅎ
@Retryable( | ||
value = {HttpServerErrorException.class}, | ||
maxAttempts = 3, | ||
backoff = @Backoff(delay = 1000, multiplier = 2) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재시도 동작 매커니즘을 설명해보실 수 있나용?
|
||
FcmApiV1Response fcmApiV1Response = fcmApiClient.sendMessage(fcmNotification, campaign); | ||
|
||
handelFcmResponse(fcmApiV1Response, fcmNotification); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handel -> handle
switch (fcmApiV1Response.getCode() / 100) { | ||
case 2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
아 이거 100 나누기로 로직 쳐야하는게 좀 킹받네요 ㅋㅋㅋ
뭔가 지원해주는 메서드 없나..
public void handleFcmNotificationEvent(FcmNotificationEvent event) { | ||
FcmToken fcmToken = fcmTokenDao.fetchByLoginId(event.getReceiverId()); | ||
Campaign campaign = campaignDao.fetchById(event.getCampaignId()); | ||
FcmNotification fcmNotification = FcmNotification.from(event, fcmToken); | ||
|
||
FcmApiV1Response fcmApiV1Response = fcmApiClient.sendMessage(fcmNotification, campaign); | ||
|
||
handelFcmResponse(fcmApiV1Response, fcmNotification); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 제가 딱 상상한 그림이었어요! 고생하셨습니다.
- 토큰 등록 API 생성 - 캠페인 등록 API 생성 - 메시지 등록 API 생성 - 단건 메시지 전송 API 생성
- publishMesssage 메서드 생성 : 메시지 저장 후 메시지 전송 이벤트 발행 - MessageEventListner 생성 - 비동기 Executor 적용 - 부모 트랜잭션과 분리(REQUIRES_NEW) - 부모 트랜잭션 커밋 후 실행 - FCM 전송 response 처리
- NotificationEvent(이벤트 시작점) 생성 - 전송 방법은 채널로 분리(ex. FcmNotificationChannel)
Issue 링크 : PUSH 메시지 전송 기능 개발 #23
구현 내용 :
메인 리뷰어 지정 및 Due Date
용어 설명
FCM token은 앱을 새로 설치, 앱 내부 데이터 초기화, 앱이 새 기기에서 복구 시 갱신 됩니다.
고민 포인트, 리뷰 시 참고 사항(코드)
TODO
References
체크리스트