-
Notifications
You must be signed in to change notification settings - Fork 0
[✨ feat] 알림 메시지 템플릿 타입 구현 #42
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
Conversation
BaseException과 Exception 처리만으로 충분하므로, UnsupportedOperationException, HttpMediaTypeNotSupportedException, SocketTimeoutException에 대한 별도 핸들러를 제거함.
- INVALID_USER_NAME 메시지에 공백 허용 여부를 명확히 표현
- 클래스명 PushToken → FcmToken 으로 명확히 표현 - 생성자, 팩토리 메서드 등 관련 메서드명 일괄 수정 - 예외 코드도 FCM_TOKEN_NOT_NULL 로 변경
- @Embedded 필드 타입을 PushToken → FcmToken으로 수정 - 클래스명 변경에 따른 필드 타입 일관성 유지
- FcmToken 클래스명 변경에 따라 테스트 클래스 및 내부 테스트 대상 수정 - 예외 메시지 검증 시 FCM_TOKEN_NOT_NULL 에 맞춰 assert 문 수정
- PUSH_TOKEN_NOT_NULL → FCM_TOKEN_NOT_NULL 로 수정하여 도메인 명확성 확보
- @DisplayName 어노테이션을 "푸시 토큰 테스트" → "FCM 토큰 테스트"로 변경
스크랩 상태에 대한 유효성 검사 및 중복 상태 예외를 처리하기 위한 ScrapErrorCode 열거형을 구현하였습니다. - scrapped/unscrapped 외 상태에 대한 예외 정의 - 이미 스크랩된 상태 또는 스크랩되지 않은 상태에 대한 예외 정의 - [SCRAP ERROR] prefix 포함한 에러 메시지 포맷 구성
ScrapErrorCode를 인자로 받아 스크랩 관련 예외를 처리할 수 있도록 ScrapException을 정의하였습니다. - 공통 예외 상위 클래스(BaseException) 상속 - 스크랩 도메인 전용 예외 처리 지원
- 사용자(User)와 연관된 스크랩 정보를 저장하는 Scrap 엔티티 정의 - 스크랩 상태 관리(SCRAPPED/UNSCRAPPED)를 위한 ScrapStatus Enum과 연동 - 스크랩 생성 정적 팩토리 메서드 `of(User user)` 제공 - 상태 전이 메서드(scrap, unscrap) 및 상태 확인 메서드(isScrapped, isUnscrapped) 추가 - 불필요한 Scraps 클래스 제거
- SCRAPPED, UNSCRAPPED 두 가지 상태를 정의하는 ScrapStatus Enum 구현 - 문자열 입력으로부터 Enum 매핑 기능(from) 및 유효성 검증 메서드(isValid) 제공 - 동일 상태로의 중복 전이를 방지하는 scrap(), unscrap() 메서드 구현 - 상태 확인 메서드(isScrapped, isUnscrapped) 및 Jackson 직렬화/역직렬화 설정 추가 - 잘못된 상태 입력 또는 중복 전이 시 ScrapException 발생 처리
- from() 메서드의 유효/무효 입력값 처리 검증 - scrap(), unscrap() 전이 메서드 정상 동작 및 예외 케이스 테스트 - isScrapped(), isValid() 등의 유틸성 메서드에 대한 테스트 포함 - 대소문자 구분 없이 상태 변환 가능함을 검증
- ScrapStatus 입력값이 null인 경우를 명확히 처리하기 위해 SCRAP_STATUS_CANNOT_BE_NULL 코드 추가 - from() 메서드 내 null 체크와 연계하여 예외 메시지 명확화
- 입력값이 null일 경우 ScrapStatus.from()에서 NPE 방지를 위해 명시적 검증 로직 추가 - ScrapException을 통해 SCRAP_STATUS_CANNOT_BE_NULL 예외 코드 반환 - null 검증 책임을 분리한 validateNotNull() 메서드로 구현
- ScrapStatus.from(null) 호출 시 ScrapException 발생 여부 테스트 - isValid(null) 호출 시 false 반환 테스트 추가 - null 입력값 처리 로직에 대한 테스트 커버리지 보완
- 메인 메시지 null 여부 및 유효성 검증 에러 추가 - 메시지 포맷 시 파라미터 누락 및 값 누락에 대한 에러 정의 - 공통 인터페이스 ErrorCode 구현
- 메시지 관련 예외 처리를 위한 MessageException 클래스 정의 - BaseException 상속 및 MessageErrorCode 기반 생성자 구현
- 템플릿 내 플레이스홀더를 파라미터로 치환하는 포맷 메서드 정의 - 포맷팅 필요 여부에 따라 파라미터 검증 및 치환 수행 - 누락된 파라미터나 값이 있을 경우 MessageException 발생 - MessageTemplate 인터페이스 구현을 위한 추상 클래스 설계
- 사용자에게 보여줄 메인 메시지를 Enum 형태로 정의 - 각 항목은 포맷팅이 필요 없는 고정 메시지로 구성 - AbstractMessageTemplate 상속을 통해 포맷팅 기능 확장
- 메시지 전송 대상 구분을 위한 타입 정의 - 스크랩한 사용자(SCRAPPED_USER)와 전체 사용자(ALL_USERS) 구분
- 메인/서브 메시지 및 타겟 유형을 조합한 템플릿 타입 정의 - 메시지 종류별로 메인 메시지, 서브 메시지, 대상 사용자 연결 - 각 템플릿 타입에서 메시지 포맷 메서드(main, sub) 제공
- 사용자 이름을 포함하는 메시지 본문(SubMessage) 템플릿 정의 - 포맷팅이 필요한 메시지로 구성되어 파라미터 기반 치환 수행 - AbstractMessageTemplate 상속으로 포맷팅 로직 재사용
- Messages → Message로 클래스명 단수형으로 변경하여 의미 명확화 - 동일한 필드(mainMessage, subMessage)는 유지 - 기존 Messages 클래스 제거 및 새로운 Message 클래스 적용
- 메시지 문자열을 반환하는 value 메서드 정의 - 포맷팅 필요 여부를 판단하는 needsFormatting 메서드 정의 - 파라미터 기반 포맷을 수행하는 format 메서드 포함 - AbstractMessageTemplate, MainMessage, SubMessage 등에서 구현 예정
- Messages 클래스명을 Message로 변경함에 따라 필드 타입 및 import 구문 수정 - 관련 JPA 매핑 어노테이션 유지 (cascade, orphanRemoval 등)
- MessageTemplateType의 main/sub 메시지 포맷 정상 동작 테스트 - 파라미터 누락, 잘못된 키, null 값 등에 대한 예외 케이스 검증 - 포맷이 불필요한 메인 메시지와 여분 파라미터 무시 동작 확인 - AbstractMessageTemplate의 중복 플레이스홀더 처리 테스트 포함
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.
Pull Request Overview
This PR implements a new messaging template system and refines the scrap domain error handling while also cleaning up legacy code. Key changes include:
- Removal of the unused Scraps.java file and refactoring of scrap domain functionality.
- Addition of enums and interfaces for message templates, including MainMessage, SubMessage, MessageTemplateType, and MessageTargetType.
- Updated notification and message domain classes to align with the new messaging template structure and error handling.
Reviewed Changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/org/terning/scrap/domain/Scraps.java | Removed unused scrap domain file. |
| src/main/java/org/terning/scrap/domain/ScrapStatus.java | Added enum for managing scrap statuses. |
| src/main/java/org/terning/scrap/domain/Scrap.java | Introduced Scrap entity with scrap/unscrap logic. |
| src/main/java/org/terning/scrap/common/failure/ScrapException.java | Created custom exception for scrap errors. |
| src/main/java/org/terning/scrap/common/failure/ScrapErrorCode.java | Defined error codes for scrap-related failures. |
| src/main/java/org/terning/notification/domain/Notifications.java | Updated notification entity to reference the singular Message entity. |
| src/main/java/org/terning/message/domain/enums/SubMessage.java | Added sub-message enum for templated message details. |
| src/main/java/org/terning/message/domain/enums/MessageTemplateType.java | Implemented combined message template type for structured message formatting. |
| src/main/java/org/terning/message/domain/enums/MessageTargetType.java | Added enum to differentiate message recipients. |
| src/main/java/org/terning/message/domain/enums/MainMessage.java | Added main-message enum for templated message texts. |
| src/main/java/org/terning/message/domain/MessageTemplate.java | Introduced interface for message template implementations. |
| src/main/java/org/terning/message/domain/Message.java | Renamed entity from plural to singular for clarity. |
| src/main/java/org/terning/message/domain/AbstractMessageTemplate.java | Provided common abstract implementation for message template formatting. |
| src/main/java/org/terning/message/common/failure/MessageException.java | Created custom exception for message template errors. |
| src/main/java/org/terning/message/common/failure/MessageErrorCode.java | Defined detailed error codes for message templating issues. |
| src/main/java/org/terning/global/failure/GlobalExceptionHandler.java | Streamlined global exception handling by removing obsolete exception handlers. |
Comments suppressed due to low confidence (1)
src/main/java/org/terning/message/domain/AbstractMessageTemplate.java:58
- [nitpick] Consider including the missing placeholder key in the exception message for better debugging. For example, you could append the key value to the error message to indicate which placeholder is missing its replacement.
if (replacement == null) {
JungYoonShin
left a comment
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.
구현양이 많았을텐데 고생하셨습니다!! 최고
| INTERESTED_ANNOUNCEMENT_REMINDER( | ||
| MainMessage.INTERESTED_ANNOUNCEMENT, | ||
| SubMessage.INTERESTED_ANNOUNCEMENT_DETAIL, | ||
| MessageTargetType.SCRAPPED_USER | ||
| ), | ||
|
|
||
| RECENTLY_POSTED_INTERNSHIP_RECOMMENDATION( | ||
| MainMessage.RECENTLY_POSTED_INTERNSHIP, | ||
| SubMessage.RECENTLY_POSTED_INTERNSHIP_DETAIL, | ||
| MessageTargetType.ALL_USERS | ||
| ), | ||
|
|
||
| TRENDING_INTERNSHIP_ALERT( | ||
| MainMessage.TRENDING_INTERNSHIP, | ||
| SubMessage.TRENDING_INTERNSHIP_DETAIL, | ||
| MessageTargetType.ALL_USERS | ||
| ); |
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.
좋게 봐주셔서 감사합니다~! 🙌
메인 메시지랑 짝을 이루는 서브 메시지가 항상 존재하고,
또 알림 대상도 각각 다르게 설정되어야 하다 보니
이 세 가지를 하나의 enum으로 묶어서 관리하면 훨씬 실수도 줄이고, 유지보수도 쉬워질 것 같아서 이렇게 구성해봤어요!
예를 들어, 관심 공고 알림은 스크랩한 유저만 대상으로 보내야 하고,
최신 인턴 공고나 트렌딩 인턴 공고는 전체 유저 대상이잖아요.
이런 조건들을 각각 분산해서 관리하면 빠뜨리기 쉽고 로직도 복잡해지는데,
enum으로 묶어두니까 전송 조건까지 포함해서 알림 한 세트를 명확하게 표현할 수 있어서 편하더라구요! 😄
추가적으로 메시지 포맷이 변경되거나 알림 조건이 바뀌더라도
enum 하나만 수정하면 되니 확장성 측면에서도 괜찮은 구조라고 생각했어요! 💡
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.
오...알림 유형또한 이런식으로 관리하면 확실히 편할 것 같네요
| @Enumerated(EnumType.STRING) | ||
| private ScrapStatus status; |
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.
요 컬럼은 왜 필요한가유???, 스크랩여부 표시하는 거 같은데 스크랩 된 공고만 scrap 테이블에 존재하는게 아닌 로직인가요 ??
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.
피드백 감사합니다~! 😊
말씀처럼 status는 스크랩 여부를 나타내는 컬럼이에요!
스크랩된 공고만 scraps 테이블에 저장되는 구조는 아니고, 스크랩을 한 번이라도 한 공고는 테이블에 남기되 상태값(SCRAPPED, UNSCRAPPED)으로 현재 상태를 관리하는 방식이에요.
이렇게 상태값으로 관리하면,
- 스크랩 취소 후에도 이력을 남길 수 있고,
- 다시 복구할 때 빠르게 처리할 수 있으며,
- 무엇보다 스크랩 여부를 기준으로 다양한 비즈니스 로직에서 검증이 가능하다는 장점이 있어요 🙌
예를 들어 알림 서버에서 "스크랩한 공고에 대해서만 알림을 전송한다"는 로직이 있다면,
해당 공고의 status가 SCRAPPED인지 아닌지를 검증해서 불필요한 알림을 막거나, 유효한 대상만 선별하는 데에도 활용할 수 있어요.
그리고 도메인 제약은 ScrapStatus enum에 정의된 scrap(), unscrap() 메서드에서 처리하고 있어서,
상태 전이 시점에도 일관성을 보장할 수 있도록 구성돼 있습니다!
좋은 질문 감사드려요~ 😄
| INTERESTED_ANNOUNCEMENT_REMINDER( | ||
| MainMessage.INTERESTED_ANNOUNCEMENT, | ||
| SubMessage.INTERESTED_ANNOUNCEMENT_DETAIL, | ||
| MessageTargetType.SCRAPPED_USER | ||
| ), | ||
|
|
||
| RECENTLY_POSTED_INTERNSHIP_RECOMMENDATION( | ||
| MainMessage.RECENTLY_POSTED_INTERNSHIP, | ||
| SubMessage.RECENTLY_POSTED_INTERNSHIP_DETAIL, | ||
| MessageTargetType.ALL_USERS | ||
| ), | ||
|
|
||
| TRENDING_INTERNSHIP_ALERT( | ||
| MainMessage.TRENDING_INTERNSHIP, | ||
| SubMessage.TRENDING_INTERNSHIP_DETAIL, | ||
| MessageTargetType.ALL_USERS | ||
| ); |
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 String format(Map<String, String> params) { | ||
| return new AbstractMessageTemplate(template, requiresFormatting){}.format(params); | ||
| } |
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.
이런식으로 parameter를 넣어줄 수 있는 것을 새롭게 알았네요!!
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.
저도 처음엔 생소했는데, Map<String, String>으로 파라미터를 넘기면 템플릿 포맷 확장할 때 유연하게 대응할 수 있어서 꽤 유용하더라고요! 😄
특히 알림 메시지처럼 다양한 변수들이 들어갈 수 있는 경우, 미리 고정된 포맷보다는 이렇게 유동적으로 처리하는 쪽이 확장성 면에서 더 낫겠다고 생각했어요! 💡
감사합니다! 😊 |
📄 Work Description
MessageErrorCode,MessageException정의MessageTemplate및 공통 추상 클래스AbstractMessageTemplate구현MainMessage,SubMessageenum을 통해 메시지 템플릿 정의 및 포맷 처리 구조화MessageTemplateTypeenum 구현MessageTargetTypeenum 정의MessageTemplateTypeTest작성💬 To Reviewers
📷 Screenshot
⚙️ ISSUE
✅ PR check list