Skip to content
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

푸시 알림 기능 리팩토링 및 알림 선택 구독 기능 추가 #621

Merged
merged 47 commits into from
Oct 15, 2024

Conversation

pricelees
Copy link
Contributor

PR의 목적이 무엇인가요?

  • 알림 기능 리팩토링 -> 알림 기능은 이벤트를 이용한 비동기로 구현하여 기존 도메인과 별도로 처리되도록 수정
  • 알림 구독 기능 추가(현재는 모임 생성, 특정 채팅방)
  • FCM 토큰 관리 방법 수정(활성화, 비활성화, 제거 상태로 구분)
  • FCM 메시지 전송 오류 처리 기능 구현(문제되는 토큰은 삭제, 일부 예외는 재전송 시도)

이슈 ID는 무엇인가요?

설명

모든 기능은 제 개인 EC2에 배포한 뒤 정상 동작 확인했고, 새로 추가된 ChatRoom에 대한 전송도 일단 기존 코드 참고해서 구현해놨어요.
(이쪽은 자세히 알고있는게 아니라 에러가 발생할 수도 있습니다.)

구독 기능은 일단 테스트를 위해 API만 뚫어놨는데, 이 부분은 추후에 프론트와 합의 후 수정하겠습니다.

질문 혹은 공유 사항 (Optional)

pricelees and others added 30 commits October 2, 2024 18:00
# Conflicts:
#	backend/src/main/java/mouda/backend/moim/business/ChamyoService.java
#	backend/src/main/java/mouda/backend/moim/business/CommentService.java
#	backend/src/main/java/mouda/backend/moim/business/MoimService.java
#	backend/src/main/java/mouda/backend/notification/business/NotificationService.java
#	backend/src/main/java/mouda/backend/notification/domain/NotificationType.java
#	backend/src/main/java/mouda/backend/notification/infrastructure/entity/ChatRoomSubscription.java
#	backend/src/main/java/mouda/backend/notification/infrastructure/entity/SubscriptionEntity.java
#	backend/src/main/java/mouda/backend/notification/infrastructure/repository/SubscriptionRepository.java
#	backend/src/main/java/mouda/backend/notification/presentation/request/FcmTokenRequest.java
#	backend/src/test/java/mouda/backend/notification/infrastructure/SubscriptionEntityTest.java
@pricelees pricelees requested a review from hoyeonyy October 10, 2024 10:55
@pricelees pricelees added BE 백엔드 관련 이슈입니다. 🌱 기능추가 feature (새로운 기능 구현) ⚒️ 리팩터링 refactor (기능이 변경되지는 않지만 코드를 수정) labels Oct 10, 2024
Copy link
Contributor

@ay-eonii ay-eonii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상돌 ,, 코드량이 엄청나네요 ... 정말 감사합니다 👍

코멘트 몇개 남겼는데 다른 건 읽어보시고 스웨거부분만 확인부탁드려용

@Override
public ResponseEntity<RestResponse<SubscriptionResponse>> readSpecificChatRoomSubscription(
@LoginMember Member member,
@Valid @RequestBody ChatSubscriptionRequest chatSubscriptionRequest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

스웨거에서 이렇게 나와요 ㅜㅅㅜ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스웨거에서 이렇게 나와요 ㅜㅅㅜ

Get요청을 보낼때 RequestBody를 쓴 실수.. RequestParam으로 수정했어요!

Comment on lines +52 to +60
public void changeChatRoomSubscription(long darakbangId, long chatRoomId) {
for (UnsubscribedChatRooms unsubscribedChatRoom : unsubscribedChats) {
if (unsubscribedChatRoom.getDarakbangId() == darakbangId) {
unsubscribedChatRoom.changeChatRoomSubscription(chatRoomId);
return;
}
}
unsubscribedChats.add(UnsubscribedChatRooms.create(darakbangId, chatRoomId));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 완전탐색을 해야한다면 Map형식으로 저장하는 것도 고려해보면 좋을 것 가탕요

다락방Id를 key, List만 가진 UnsubscribedChatRooms를 value 로 하는 방식으로요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 완전탐색을 해야한다면 Map형식으로 저장하는 것도 고려해보면 좋을 것 가탕요

다락방Id를 key, List만 가진 UnsubscribedChatRooms를 value 로 하는 방식으로요

이 부분은 조금 더 고민해볼게요. 일단 [{"darakbangId":1,"chatRoomIds":[1]},{"darakbangId":2,"chatRoomIds":[1,2]}] 형태로 데이터를 저장하고자 해서 List를 사용했어요!

@ksk0605 이건 테바가 확인해주면 좋겠네요.


Optional<FcmTokenEntity> findByToken(String token);

void deleteAllByTokenIn(List<String> unregisteredTokens);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얘만 인덴트가 안맞아여ㅎㅎ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얘만 인덴트가 안맞아여ㅎㅎ

제꺼에서는 맞는데요..? ㅋㅋㅋ 그래도 한번 더 정렬해서 다시 올릴게요 ~

Comment on lines 75 to 76
FcmFailedResponse retryResponse = retry(failedResponse, notification,
failedResponse.getNonRetryableFailedTokens());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(잘 못 이해했을수도 있는데 ㅜㅜ) 재시도할수 없는 토큰을 retry 하는 이유가 뭔ㄱㅏ요 ..?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(잘 못 이해했을수도 있는데 ㅜㅜ) 재시도할수 없는 토큰을 retry 하는 이유가 뭔ㄱㅏ요 ..?

디버깅용으로 찍어보고 수정을 안한 오타입니다..ㅎㅎ getFailedWith5xxTokens()가 들어가는게 맞아요👍

List<String> retryTokens) {
log.info("Retrying for notification: {}. failed: {}, Thread: {}", notification.getTitle(), retryTokens,
Thread.currentThread().getName());
MulticastMessage message = fcmMessageFactory.createMessage(notification, retryTokens).get(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get(0)을 하는 이유에 대해 설명해주실 수 있낭요

Copy link
Contributor Author

@pricelees pricelees Oct 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get(0)을 하는 이유에 대해 설명해주실 수 있낭요

토큰이 500개 이상인 경우 500개당 1개의 MulticastMessage 객체를 만드는데, fcmMessageFactory에서는 일원화를 위해 500개 미만이어도 리스트에 담아 반환하고 있습니다. 그리고 List<MulticastMessage>를 forEach를 통해 하나씩 전송하구요.

�FcmResponseHandler에서의 재시도 처리는 한 개의 MulticastMessage만을 대상으로 하는데요, 그래서 여기서 fcmMessageFactory.createMessage()로 얻는 리스트는 1개의 MulticastMessage를 가질 수 밖에 없습니다. 그래서 get(0)으로 그걸 꺼내오는거에요!

Long memberId = commentFinder.readMemberIdByParentId(childComment.getParentId());

assertThat(memberId).isEqualTo(darakbangHogee.getId());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 한번만 해주세용

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 한번만 해주세용

싫어요

Comment on lines 13 to 19
@Test
void getNewCommentNotificationRecipients() {
}

@Test
void getNewReplyNotificationRecipients() {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 투두인가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 투두인가요?

경우의 수가 너무 많아서 고민이 필요했던 부분인데.. 테스트 추가했어요!
테스트 돌리면서 보니 처리가 하나 틀린 부분이 있더라구요. 덕분에 그거까지 수정할 수 있었네요~ 🫡

Copy link
Contributor Author

@pricelees pricelees left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테니 디버깅 미쳤다..👍🙇

Comment on lines 75 to 76
FcmFailedResponse retryResponse = retry(failedResponse, notification,
failedResponse.getNonRetryableFailedTokens());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(잘 못 이해했을수도 있는데 ㅜㅜ) 재시도할수 없는 토큰을 retry 하는 이유가 뭔ㄱㅏ요 ..?

디버깅용으로 찍어보고 수정을 안한 오타입니다..ㅎㅎ getFailedWith5xxTokens()가 들어가는게 맞아요👍

List<String> retryTokens) {
log.info("Retrying for notification: {}. failed: {}, Thread: {}", notification.getTitle(), retryTokens,
Thread.currentThread().getName());
MulticastMessage message = fcmMessageFactory.createMessage(notification, retryTokens).get(0);
Copy link
Contributor Author

@pricelees pricelees Oct 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get(0)을 하는 이유에 대해 설명해주실 수 있낭요

토큰이 500개 이상인 경우 500개당 1개의 MulticastMessage 객체를 만드는데, fcmMessageFactory에서는 일원화를 위해 500개 미만이어도 리스트에 담아 반환하고 있습니다. 그리고 List<MulticastMessage>를 forEach를 통해 하나씩 전송하구요.

�FcmResponseHandler에서의 재시도 처리는 한 개의 MulticastMessage만을 대상으로 하는데요, 그래서 여기서 fcmMessageFactory.createMessage()로 얻는 리스트는 1개의 MulticastMessage를 가질 수 밖에 없습니다. 그래서 get(0)으로 그걸 꺼내오는거에요!

Comment on lines +52 to +60
public void changeChatRoomSubscription(long darakbangId, long chatRoomId) {
for (UnsubscribedChatRooms unsubscribedChatRoom : unsubscribedChats) {
if (unsubscribedChatRoom.getDarakbangId() == darakbangId) {
unsubscribedChatRoom.changeChatRoomSubscription(chatRoomId);
return;
}
}
unsubscribedChats.add(UnsubscribedChatRooms.create(darakbangId, chatRoomId));
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 완전탐색을 해야한다면 Map형식으로 저장하는 것도 고려해보면 좋을 것 가탕요

다락방Id를 key, List만 가진 UnsubscribedChatRooms를 value 로 하는 방식으로요

이 부분은 조금 더 고민해볼게요. 일단 [{"darakbangId":1,"chatRoomIds":[1]},{"darakbangId":2,"chatRoomIds":[1,2]}] 형태로 데이터를 저장하고자 해서 List를 사용했어요!

@ksk0605 이건 테바가 확인해주면 좋겠네요.


Optional<FcmTokenEntity> findByToken(String token);

void deleteAllByTokenIn(List<String> unregisteredTokens);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

얘만 인덴트가 안맞아여ㅎㅎ

제꺼에서는 맞는데요..? ㅋㅋㅋ 그래도 한번 더 정렬해서 다시 올릴게요 ~

@Override
public ResponseEntity<RestResponse<SubscriptionResponse>> readSpecificChatRoomSubscription(
@LoginMember Member member,
@Valid @RequestBody ChatSubscriptionRequest chatSubscriptionRequest
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스웨거에서 이렇게 나와요 ㅜㅅㅜ

Get요청을 보낼때 RequestBody를 쓴 실수.. RequestParam으로 수정했어요!

Long memberId = commentFinder.readMemberIdByParentId(childComment.getParentId());

assertThat(memberId).isEqualTo(darakbangHogee.getId());
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개행 한번만 해주세용

싫어요

Comment on lines 13 to 19
@Test
void getNewCommentNotificationRecipients() {
}

@Test
void getNewReplyNotificationRecipients() {
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋ 투두인가요?

경우의 수가 너무 많아서 고민이 필요했던 부분인데.. 테스트 추가했어요!
테스트 돌리면서 보니 처리가 하나 틀린 부분이 있더라구요. 덕분에 그거까지 수정할 수 있었네요~ 🫡

@pricelees pricelees requested a review from ay-eonii October 13, 2024 05:12
Copy link
Contributor

@ay-eonii ay-eonii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

짱 멋집니다 짱돌 👍

Copy link
Contributor

@Mingyum-Kim Mingyum-Kim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생했습니다 상돌 😀 나중에 알림 작업하게 될 때 자세히 읽어볼게요 ...

@pricelees pricelees merged commit 0c432a1 into develop-backend Oct 15, 2024
1 check passed
Copy link
Contributor

@ksk0605 ksk0605 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생했어요 상돌 이제 알림은 그만 보고싶네요 ㅋㅋ큐ㅠㅠ

Comment on lines 24 to 36

return darakbangMembers.stream()
.filter(darakbangMember -> darakbangMember.getId() != authorId) // TODO: 모임 만들어진 알림을 정말 만든 사람이 몰라야 하나?
.map(darakbangMember -> new Recipient(darakbangMember.getMemberId(), darakbangMember.getId()))
.toList();
}

public List<Recipient> getMoimStatusChangedNotificationRecipients(long moimId) {
List<Chamyo> chamyos = chamyoRepository.findAllByMoimId(moimId);

return chamyos.stream()
.filter(chamyo -> chamyo.getMoimRole() != MoimRole.MOIMER) // TODO: 모임 상태의 변화도 알림을 정말 만든 사람이 몰라야 하나?
.map(chamyo -> new Recipient(chamyo.getDarakbangMember().getMemberId(), chamyo.getDarakbangMember().getId()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO에 있는 내용은 같이 이야기를 나눴으니까 지워주세요!

@ay-eonii ay-eonii deleted the refactor/#564 branch October 17, 2024 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BE 백엔드 관련 이슈입니다. ⚒️ 리팩터링 refactor (기능이 변경되지는 않지만 코드를 수정) 🌱 기능추가 feature (새로운 기능 구현)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants