-
Notifications
You must be signed in to change notification settings - Fork 0
[✨ feat] FCM 토큰 유효성 검증 API 구현 #46
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
- 메인 메시지 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의 중복 플레이스홀더 처리 테스트 포함
- messageTemplateType 필드 추가 및 Enum 매핑 처리 - formattedMainMessage, formattedSubMessage 필드 추가 - MessageTemplateType 기반 메시지 생성을 위한 of 정적 메서드 구현 - 동일 템플릿 타입 비교를 위한 isSameType 메서드 추가
- MessageTemplateType을 기반으로 Message 객체 생성 기능 테스트 - main/sub 메시지 포맷 정상 동작 여부 검증 - 포맷 파라미터 누락, null 값, 잘못된 키 등의 예외 케이스 검증 - isSameType 메서드를 통한 템플릿 타입 비교 기능 테스트
FCM 메시지 전송 실패에 대한 에러 코드를 FcmErrorCode enum으로 정의하였습니다. - HttpStatus.INTERNAL_SERVER_ERROR 상태와 함께 실패 메시지 제공 - 공통 인터페이스 ErrorCode 구현
FcmErrorCode를 활용한 FCM 전송 실패 예외 FcmException을 정의하였습니다. - BaseException을 상속받아 공통 예외 처리 방식 유지 - FcmErrorCode 기반으로 에러 메시지 및 상태 전달
FCM 토큰이 만료되었는지 여부를 판단하는 FcmTokenValidator 인터페이스를 정의하였습니다. - FcmToken 도메인 객체 기반의 만료 여부 검증 메서드 선언 - 다양한 구현체 확장을 위한 계약 역할 수행
FCM 서버에 검증 메시지를 전송하여 FCM 토큰이 만료되었는지를 확인하는 FirebaseFcmTokenValidator 구현체를 추가하였습니다. - MessagingErrorCode.UNREGISTERED 에러 시 만료로 간주 - 예외 발생 시 FcmException으로 변환하여 공통 에러 처리 유지
사용자의 FCM 토큰이 만료되었는지 여부를 확인하는 API를 UserController에 구현하였습니다. - POST /api/v1/users/fcm-tokens/reissue-required 엔드포인트 추가 - 요청 바디로 FcmTokenReissueRequiredRequest 수신 - 결과는 FcmTokenReissueRequiredResponse로 반환하며, 성공 응답 래핑 처리
사용자의 FCM 토큰 재발급 필요 여부를 확인하는 로직을 담당할 FcmTokenValidateService 인터페이스를 정의하였습니다. - 요청 DTO를 받아 응답 DTO로 결과 반환 - 서비스 로직의 확장성과 테스트 용이성을 고려한 구조
사용자의 FCM 토큰이 만료되었는지 확인하는 FcmTokenValidateServiceImpl 클래스를 구현하였습니다. - UserRepository를 통해 사용자 조회 - FcmTokenValidator를 통해 토큰 만료 여부 검증 - 결과를 FcmTokenReissueRequiredResponse로 반환 - 사용자 없을 시 USER_NOT_FOUND 예외 처리
UserService에서 FcmTokenValidateService를 사용해 FCM 토큰 만료 여부 확인 기능을 위임 처리하였습니다. - FcmTokenReissueRequiredRequest를 받아 서비스 호출 - FcmTokenReissueRequiredResponse 반환
사용자 조회 실패 시 예외 처리를 위한 USER_NOT_FOUND 에러 코드를 추가하였습니다. - HttpStatus.NOT_FOUND 상태와 메시지 구성 - 공통 에러 코드 인터페이스 구현을 위한 포맷 유지
FCM 토큰 재발급 필요 여부를 성공적으로 응답했을 때 사용할 UserSuccessCode를 추가하였습니다. - HTTP 상태는 200 OK - 메시지: "FCM 토큰 재발급 여부를 성공적으로 확인했습니다."
User 도메인에 FCM 토큰 만료 여부를 FcmTokenValidator를 통해 확인하는 isFcmTokenExpired 메서드를 추가하였습니다. - token 필드의 위임 메서드 형태로 구현 - User 내부에서 FCM 토큰 유효성 검증 가능하도록 개선
User 엔티티에 대한 기본 CRUD 작업을 수행할 수 있도록 JpaRepository를 상속한 UserRepository 인터페이스를 정의하였습니다. - Long 타입의 사용자 ID 기반 조회 지원 - @repository 어노테이션으로 스프링 빈으로 등록
FcmTokenValidator를 이용해 토큰 만료 여부를 확인할 수 있는 isExpiredWith 메서드를 FcmToken VO에 추가하였습니다. - 외부 유효성 검증 전략을 위임받아 처리 가능 - 테스트 및 검증을 위한 TODO 주석 추가 - value 필드 중복 반환 제거로 정리
사용자의 FCM 토큰 재발급 필요 여부를 확인하기 위한 요청 DTO를 정의하였습니다. - userId를 필드로 포함 - of 정적 팩토리 메서드를 통해 객체 생성 가능하도록 구성
FCM 토큰 재발급이 필요한지를 클라이언트에 전달하기 위한 응답 DTO를 정의하였습니다. - reissueRequired 필드를 포함 - 정적 팩토리 메서드 of 제공
불필요한 contextLoads 메서드만 포함된 기본 생성 테스트 클래스 TerningApplicationTests를 삭제하였습니다. - 불필요한 통합 테스트 실행을 방지하고 명확한 단위 테스트 환경 구성 목적
단위 테스트에서 FCM 토큰 만료 여부를 제어하기 위한 FakeFcmTokenValidator 클래스를 추가하였습니다. - FcmTokenValidator 인터페이스 구현 - setExpired 메서드를 통해 만료 여부를 임의로 설정 가능 - 테스트 독립성과 예측 가능성 확보
FcmTokenValidateServiceImpl에 대한 단위 테스트를 작성하였습니다. - FakeFcmTokenValidator를 사용해 만료 여부를 제어 - 토큰이 만료된 경우 true, 그렇지 않으면 false를 반환하는지 확인 - 존재하지 않는 사용자 ID에 대해 예외가 발생하는지 검증 - @DisplayName, @nested를 활용해 테스트 가독성 향상
단위 테스트에서 UserRepository를 대체할 수 있는 인메모리 기반의 UserRepositoryTest 클래스를 구현하였습니다. - id 자동 생성 및 필드 직접 주입을 통해 테스트 가능하도록 구성 - save, findById, delete 등 기본 CRUD 기능 구현 - Spring Data JPA의 JpaRepository 인터페이스 구현 메서드 중 필요한 부분만 처리 - 실제 DB에 의존하지 않고 사용자 도메인 테스트 가능
- `setUp()`에서 유저 객체를 더 명확히 구분 - `userId`를 잘못된 변수로 재사용하던 부분 수정 - `FcmTokenReissueRequiredRequest` 중복 코드를 하나로 통합 - 잘못된 변수명 수정 및 불필요한 코드 제거 - 테스트의 예외 처리 및 반환 값 검증 부분 수정
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 an FCM token validation API that checks if a token is expired based on the user's account status and push notification settings.
- Introduces FcmTokenValidateService to determine token validity via Firebase messaging.
- Refactors the messaging domain by replacing the plural Messages entity with a singular Message entity and adding enums for message templates.
- Configures Firebase initialization using properties injected via FcmProperties.
Reviewed Changes
Copilot reviewed 42 out of 42 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/org/terning/notification/domain/Notifications.java | Updated to use a singular Message entity instead of Messages. |
| src/main/java/org/terning/message/domain/enums/SubMessage.java | Added enum for detailed sub message templates. |
| src/main/java/org/terning/message/domain/enums/MessageTemplateType.java | Introduced enum tying main and sub messages with target types. |
| src/main/java/org/terning/message/domain/enums/MessageTargetType.java | Defined enum for target user types for messaging. |
| src/main/java/org/terning/message/domain/enums/MainMessage.java | Added enum for main messages with formatting support. |
| src/main/java/org/terning/message/domain/Messages.java | Removed the Messages entity in favor of using the Message entity. |
| src/main/java/org/terning/message/domain/Message.java | New entity encapsulating message details and type matching. |
| src/main/java/org/terning/message/domain/AbstractMessageTemplate.java | Provides common formatting logic with placeholder replacement. |
| src/main/java/org/terning/message/domain/MessageTemplate.java | Interface for message templating. |
| src/main/java/org/terning/message/common/failure/MessageException.java | Exception for message templating errors. |
| src/main/java/org/terning/message/common/failure/MessageErrorCode.java | Enum defining error codes and messages for template failures. |
| src/main/java/org/terning/fcm/validate/FirebaseFcmTokenValidator.java | Implements token validation logic using Firebase messaging. |
| src/main/java/org/terning/fcm/validate/FcmTokenValidator.java | Interface for defining FCM token validation. |
| src/main/java/org/terning/fcm/config/FcmProperties.java | Configured properties for Firebase service key injection. |
| src/main/java/org/terning/fcm/config/FcmConfig.java | Initializes Firebase with JSON credentials from properties. |
| src/main/java/org/terning/fcm/common/failure/FcmException.java | Exception for handling FCM-related failures. |
| src/main/java/org/terning/fcm/common/failure/FcmErrorCode.java | Enum defining error codes and messages for FCM failures. |
| src/main/java/org/terning/TerningApplication.java | Enabled configuration properties for FcmProperties. |
…`@GetMapping`이 중복 사용되었던 부분 수정 - `@RequestBody`와 `@PathVariable`의 변수명 중복 해결 - `FcmTokenReissueRequiredRequest` 객체의 생성 로직 개선 - `@PostMapping`과 `@GetMapping`이 중복 사용되었던 부분 수정 - `@RequestBody`와 `@PathVariable`의 변수명 중복 해결 - `FcmTokenReissueRequiredRequest` 객체의 생성 로직 개선
junggyo1020
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.
우선 Message.of 메서드를 통해 내부적으로 포맷된 메세지를 넘겨받고 있는데요.
포맷에 대한 오류 검증이 외부에 의존하고 있다보니 도메인 레벨에서는 그냥 값이 넘어와버릴 수 있어 불완전하다는 생각이 들었어요. 포맷로직 자체를 내부에서 처리할 수 있도록 고려하면 좋지 않을까하는 생각이 드는데 생각이 궁금합니다.
그리고 햔제 validator에서는 해당 토큰이 만료된 토큰인지만 판단하고 있는데 이후에 토큰의 재발급 조건이 변경될 가능성을 고려하고 계시는 건지 궁금합니다.
마지막으로 재발급이 필요한지 여부를 서버가 판단하고 리턴하는 구조인데, 실제 재발급 API 호출 시점은 어디서 이루어지게 되나요?
고생하셨습니다!!:)
이전 PR에서 말씀해주신 것처럼, 도메인 내부에서 메시지 포맷까지 처리하는 방향에 저도 공감합니다. 말씀해주신 내용을 바탕으로 이슈로 등록해두었고, 도메인의 책임을 명확히 하기 위해 내부에서 포맷 로직까지 처리하는 방향으로 개선해보려고 합니다 💪 현재는 Message.of()를 호출할 때 외부에서 이미 포맷된 메시지를 주입받고 있기 때문에, 도메인 입장에서는 메시지가 올바르게 포맷되었는지 신뢰할 수 없는 구조입니다. 이로 인해 말씀처럼 불완전한 값이 도메인으로 넘어올 수 있다는 리스크가 있고, 도메인 자체의 무결성을 보장하기 어렵다는 점에 저도 공감했습니다. 말씀해주신 것처럼
다만 좋은 피드백 감사합니다! 🙌 |
피드백 감사합니다! 😊 현재 소셜 로그인 특성상 이미 회원가입이 완료된 유저가 다시 로그인하는 흐름이라, 다만, 말씀처럼 재발급 조건이 향후 다양해질 가능성은 충분히 있다고 생각해서, 향후 토큰 만료 외에도, 예를 들어 일정 기간 미사용, 유저 상태, 앱 버전 등의 조건이 추가될 경우 좋은 포인트 짚어주셔서 감사합니다! 🙌 |
좋은 질문 감사합니다! 🙌 제가 생각한 전체 플로우는 다음과 같습니다:
이 구조의 장점은 비즈니스 판단(만료 여부)은 서버가 수행하고, 정리하자면, 재발급 API 호출 시점은 클라이언트가 로그인 응답을 받은 직후이며, 이 구조가 사용자 경험을 방해하지 않으면서도 안정적으로 토큰 상태를 유지할 수 있어 적절하다고 생각했습니다! 💡 |
📄 Work Description
FCM 토큰 재발급 필요 여부를 클라이언트가 판단할 수 있도록, 사용자의 계정 상태 및 푸시 알림 설정을 고려하여 만료 여부를 확인하는 기능을 구현하였습니다.
주요 변경사항:
💬 To Reviewers
📷 Screenshot
⚙️ ISSUE
✅ PR check list