Skip to content

Commit 2a7f4b7

Browse files
authored
�feat: add notification (#166)
* feat(Notification): 알림 도메인 추가 * feat(Notification): 알림 도메인 읽음 기능 추가 * feat(Notification): BaseEntity 상속 취소, 빌더 추가, NotificationType 추가 * feat(EventCacheRepository): 유실 매시지를 위한 이벤트캐시 저장소 추가 * feat(EmitterRepository): sse 연결을 관리하는 저장소 추가 - 저장, 조회, 삭제 * feat(NotificationRepository): 알림 저장소 추가 * feat(NotificationResponseDto): 알림 DTO 추가 * feat(NotificationService): 알림 서비스 추가 - 구독 - 발신 * feat(EventCacheRepository): 유실 데이터 관리 삭제 - 읽음 처리를 명시하기 위해서 데이터는 DB에 영속화되므로 유실 데이터를 관리할 필요가 없음. * feat(EmitterRepository): userId를 receiverId로 변경 - 가독성 개선 * feat(NotificationService): 알림, sse 구독, sse 발송을 구분하기 위해서 삭제 * feat(SSE subscriber): SSE 구독 추가 * feat(SSE sender): SSE 발송 추가 * feat(SSE Event): SSE 이벤트 추가 * refactor(SSE): 패키지 이동 - 알림 외 서비스에서 이용 가능 * refactor(SSE event): 명확한 이름으로 수정 * refactor(BaseEntity): 패키지 이동 * refactor(volunteerDetail): 패키지 이동 * refactor(SseSubscriptionManager): 인터페이스 명명 변경, subscribe 반환 타입 변경 - 반환 값 필요 없다고 판단 * refactor(SseSubscriptionManager): 구현체 명명 변경, subscribe 반환 타입 변경, 초기화 메시지는 Ssesender에서 관리 - SRP * feat(SseEventType): 초기화 이벤트 타입 추가 - 구독할 때, 초기화 과정에 전송이 포함되어야 함. * feat(SseEvent): 빌더로 변경 * refactor(BaseEntity): 패키지 변경 - 글로벌 패키지 루트에 있는 것이 불편했음. * feat(ServerEvent): 서버 이벤트 정의 - 타입 - 알림이 되어야 하는 이벤트 - XX가 되어야 하는 이벤트 * feat(ServerEventPublisher): 서버 이벤트 퍼블리셔 * feat(VolunteerApplyStatusChangeEvent): 봉사 신청 상태 변경 이벤트 - 서버 이벤트의 하위 클래스. * feat(ApproveVolunteerApplyService): apply가 approve 될 떄, 이벤트 퍼블리싱 - publishVolunteerApplyStatusChangeEvent * refactor(EmitterRepository): 패키지 이동 * refactor(NotificationRepository): 패키지 이동 * refactor(RedisConfig): 패키지 이동 * feat(RedisConfig): 확장 가능한 채널 토픽 구성 추가 - 컨테이너를 하나만 사용하도록 구성 * feat(RedisListenerRegistrar): Redis 알림 리스너 등록 로직 추가 - notificationTopic 구독을 위한 리스너 등록 - PostConstruct로 리스너 자동 등록 처리 * feat(NotificationType): 알림 타입 추가 - 설명 필드 추가. - 정적 팩토리 메서드 from 추가. * feat(RedisServerEventPublisher): ServerEventPublisher Redis 구현체 추가 - ServerEventType 기반으로 Redis ChannelTopic 매핑 - 이벤트를 직렬화하여 Redis Pub/Sub에 발행 - 미지원 이벤트 타입에 대한 예외 처리 추가 * feat(RedisNotificationSubscriber): Redis 메시지 구독 로직 추가 - Redis MessageListener 구현체 작성 - 수신한 메시지를 NotificationHandler로 전달하여 처리 * feat(NotificationSubscriber): 메시지 구독 로직 추가 - Redis MessageListener 구현체 작성 - 수신한 메시지를 NotificationHandler로 전달하여 처리 * feat(MessageConverter): 메시지 변환 로직 구현 - Redis 메시지를 Notification 엔티티로 변환하는 로직 추가. - VolunteerApplyStatusChange 이벤트 처리 로직 구현. - Notification 타이틀 생성 로직. * feat(NotificationResponseDto): 알림 단방향 이벤트 전송 DTO 추가 - from (entity to dto) 정적 팩토리 메서드 * feat(NotificationHandler): 알림 핸들링 추가 - 모든 알림은 저장되고 sseSender로 처리. * feat(SseInitMessage): SSE 초기화 메시지 추가 * feat(SseSender): event -> SseEvent 로 클래스 명 변경 * feat(SseService): SseUseCase 구현체 추가 - subscriptionManager, sender 의존성 주입. * feat(Notification): 컬럼 어노테이션 수정 - 바이너리 16 명시. - ENUM 명시. - read -> mysql 예약어 문제 해결 * feat(NotificationResponseDto): 알림 응답 DTO 수정 - 읽음 필드 삭제. - 수신자 아이디 필드 삭제. * feat(NotificationRepository): 알림 조회 추가 - 읽음, 읽지 않음 조건에 따라서 알림 조회 추가. * feat(NotificationQueryService): 알림 조회 추가 - 읽음, 읽지 않음 조건에 따라서 알림 조회 추가. - DTO로 변환해서 반환. * refactor(NotificationHandlerImpl): 알림 도메인 패키지 변경 * feat(NotificationController): 알림 컨트롤러 구현 - 읽음, 읽지 않음에 따른 엔드포인트 추가. * feat(Notification): 엔티티 리스너 추가, markAsRead 수정 - createdDate를 위해서 추가함. - markAsRead 퍼블릭으로 수정. * test(NotificationRepository): 알림 조회 테스트 * test(NotificationService): 알림 조회 테스트 * test(EmitterRepository): emitter 조회, 저장, 삭제 테스트 * test(SseSender): 전송, 예외 발생 시 emitter 제거, 다수 emitter 전송 테스트 - 테스트를 위한 override가 있음. * test(SseSubscriptionManager): 구독 테스트 - 콜백로 설정한 complete, timeout은 handler가 없어서 테스트가 불가능. * feat(SseController): SSE 구독 엔드포인트 추가 * test(sse): SSE 테스트 클래스 명 수정 * test(SseService): 구독 테스트 * feat(NotificationSubType): Sub 명시 * feat(ServerEvent): subType 추가, 직렬화를 위한 JsonProperty 추가 * feat(ServerEventType): ServerEventType의 하위 타입(subType) 추가, 직렬화를 위한 JsonProperty 추가, subType Getter 추가 * feat(JacksonRedisSerializer): LocalDateTime 직렬화/역직렬화 가능 설정 - objectMapper 에 모듈 설정. - config에 key value 설정 변경. * feat(VolunteerApplyStatusChangeEvent): json 역직렬화를 위해서 JsonProperty 설정 - subType * test(Notification): sub 명시 (클래스 명 변경) - 테스트 클래스 명 변경 * feat(RedisNotificationSubscriber): 메시지 컨버팅 책임 추가 * feat(NotificationHandler): 메시지 컨버팅 책임 제거 * test(NotificationHandler): 알림 핸들링 테스트 추가 - 저장 위주 * cicd(build.gradle): jacoco coverage *event* 제외 - 이건 진짜 해도 될 것 같음... * refactor: 코드 스타일 정리 - 깃 잠재적인 이슈 예방 마지막 줄 개행 - 소나 이슈 수정. * feat(Notification): 알림 도메인 추가 * feat(Notification): 알림 도메인 읽음 기능 추가 * feat(Notification): BaseEntity 상속 취소, 빌더 추가, NotificationType 추가 * feat(EventCacheRepository): 유실 매시지를 위한 이벤트캐시 저장소 추가 * feat(EmitterRepository): sse 연결을 관리하는 저장소 추가 - 저장, 조회, 삭제 * feat(NotificationRepository): 알림 저장소 추가 * feat(NotificationService): 알림 서비스 추가 - 구독 - 발신 * feat(EventCacheRepository): 유실 데이터 관리 삭제 - 읽음 처리를 명시하기 위해서 데이터는 DB에 영속화되므로 유실 데이터를 관리할 필요가 없음. * feat(EmitterRepository): userId를 receiverId로 변경 - 가독성 개선 * feat(NotificationService): 알림, sse 구독, sse 발송을 구분하기 위해서 삭제 * feat(SSE subscriber): SSE 구독 추가 * feat(SSE sender): SSE 발송 추가 * feat(SSE Event): SSE 이벤트 추가 * refactor(SSE): 패키지 이동 - 알림 외 서비스에서 이용 가능 * refactor(SSE event): 명확한 이름으로 수정 * refactor(SseSubscriptionManager): 인터페이스 명명 변경, subscribe 반환 타입 변경 - 반환 값 필요 없다고 판단 * refactor(SseSubscriptionManager): 구현체 명명 변경, subscribe 반환 타입 변경, 초기화 메시지는 Ssesender에서 관리 - SRP * refactor(EmitterRepository): 패키지 이동 * refactor(NotificationRepository): 패키지 이동 * feat(RedisConfig): 확장 가능한 채널 토픽 구성 추가 - 컨테이너를 하나만 사용하도록 구성 * feat(NotificationType): 알림 타입 추가 - 설명 필드 추가. - 정적 팩토리 메서드 from 추가. * feat(NotificationResponseDto): 알림 단방향 이벤트 전송 DTO 추가 - from (entity to dto) 정적 팩토리 메서드 * feat(NotificationResponseDto): 알림 응답 DTO 수정 - 읽음 필드 삭제. - 수신자 아이디 필드 삭제. * test(SseSubscriptionManager): 구독 테스트 - 콜백로 설정한 complete, timeout은 handler가 없어서 테스트가 불가능. * test(sse): SSE 테스트 클래스 명 수정 * feat(NotificationSubType): Sub 명시 * fix: 임포트 수정 * fix: 리베이스 오류 수정
1 parent 97e547c commit 2a7f4b7

File tree

57 files changed

+1578
-91
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1578
-91
lines changed

build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ def jacocoExcludePatterns = [
121121
'**/fixture/*',
122122
'**/controller/**',
123123
'**/fixture/*',
124-
'**/*Factory*'
124+
'**/*Factory*',
125+
'**/event/**'
125126
]
126127

127128
def jacocoExcludePatternsForVerify = [
@@ -139,7 +140,8 @@ def jacocoExcludePatternsForVerify = [
139140
'*.fixture.*',
140141
'*.controller.*',
141142
'*.fixture.*',
142-
'*.*Factory*'
143+
'*.*Factory*',
144+
'*.*event*.*'
143145
]
144146

145147
jacocoTestReport {

src/main/java/com/somemore/center/domain/Center.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.somemore.center.domain;
22

33
import com.somemore.center.dto.request.CenterProfileUpdateRequestDto;
4-
import com.somemore.global.common.BaseEntity;
4+
import com.somemore.global.common.entity.BaseEntity;
55
import jakarta.persistence.*;
66
import lombok.*;
77

src/main/java/com/somemore/community/domain/CommunityBoard.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.somemore.community.domain;
22

33
import com.somemore.community.dto.request.CommunityBoardUpdateRequestDto;
4-
import com.somemore.global.common.BaseEntity;
4+
import com.somemore.global.common.entity.BaseEntity;
55
import static lombok.AccessLevel.PROTECTED;
66

77
import jakarta.persistence.Column;

src/main/java/com/somemore/community/domain/CommunityComment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.somemore.community.domain;
22

33
import com.somemore.community.dto.request.CommunityCommentUpdateRequestDto;
4-
import com.somemore.global.common.BaseEntity;
4+
import com.somemore.global.common.entity.BaseEntity;
55
import jakarta.persistence.*;
66
import lombok.Builder;
77
import lombok.Getter;

src/main/java/com/somemore/domains/Notification.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/main/java/com/somemore/global/common/BaseEntity.java renamed to src/main/java/com/somemore/global/common/entity/BaseEntity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.somemore.global.common;
1+
package com.somemore.global.common.entity;
22

33
import jakarta.persistence.Column;
44
import jakarta.persistence.EntityListeners;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.somemore.global.common.event;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.Getter;
5+
import lombok.experimental.SuperBuilder;
6+
7+
import java.time.LocalDateTime;
8+
9+
@Getter
10+
@SuperBuilder
11+
public abstract class ServerEvent<T extends Enum<T>> {
12+
13+
@JsonProperty("type")
14+
private final ServerEventType type;
15+
16+
@JsonProperty("subType")
17+
private final T subType;
18+
19+
@JsonProperty("createdAt")
20+
private final LocalDateTime createdAt;
21+
22+
protected ServerEvent(
23+
@JsonProperty("type") ServerEventType type,
24+
@JsonProperty("subType") T subType,
25+
@JsonProperty("createdAt") LocalDateTime createdAt
26+
) {
27+
this.type = type;
28+
this.subType = subType;
29+
this.createdAt = (createdAt == null) ? LocalDateTime.now() : createdAt;
30+
}
31+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.somemore.global.common.event;
2+
3+
public interface ServerEventPublisher {
4+
5+
<T extends Enum<T>> void publish(ServerEvent<T> event);
6+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.somemore.global.common.event;
2+
3+
import com.somemore.notification.domain.NotificationSubType;
4+
import lombok.Getter;
5+
import lombok.RequiredArgsConstructor;
6+
7+
import java.util.Arrays;
8+
9+
@RequiredArgsConstructor
10+
@Getter
11+
public enum ServerEventType {
12+
NOTIFICATION(NotificationSubType.class);
13+
14+
private final Class<? extends Enum<?>> subtype;
15+
16+
public static ServerEventType from(String value) {
17+
return Arrays.stream(ServerEventType.values())
18+
.filter(type -> type.name().equalsIgnoreCase(value))
19+
.findFirst()
20+
.orElseThrow(() -> new IllegalArgumentException("잘못된 이벤트 타입입니다: " + value));
21+
}
22+
}

src/main/java/com/somemore/global/configure/RedisConfig.java

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)