-
Notifications
You must be signed in to change notification settings - Fork 0
알람 끄기 잔여 횟수를 조회하는 API를 분리한다. #48
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
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package akuma.whiplash.domains.alarm.application.dto.response; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.Builder; | ||
|
|
||
| @Builder | ||
| @Schema(description = "남은 알람 끄기 횟수 응답") | ||
| public record AlarmRemainingOffCountResponse( | ||
| @Schema( | ||
| description = "회원의 남은 알람 끄기 횟수(회원당 매주 2회 부여, 매주 월요일 초기화)", | ||
| example = "1" | ||
| ) | ||
| int remainingOffCount | ||
| ) {} | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.application.dto.request.AlarmRegisterRequest; | ||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmInfoPreviewResponse; | ||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmOffResultResponse; | ||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmRemainingOffCountResponse; | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainUseCase에서 DTO 매핑으로 역할 분리 제안 현재 구조도 무방하지만, 도메인 레이어 의존성 축소를 위해 도메인 서비스는 원시 타입을 반환하고(예: 도메인 서비스 시그니처를 - public AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId) {
- return alarmQueryService.getRemainingOffCount(memberId);
- }
+ public AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId) {
+ long count = alarmQueryService.getRemainingOffCount(memberId);
+ return new AlarmRemainingOffCountResponse(count);
+ // 또는 Lombok Builder 사용 시:
+ // return AlarmRemainingOffCountResponse.builder().remainingOffCount(count).build();
+ }참고: 팀 컨벤션에 따라 본 메서드에 Also applies to: 47-49 🏁 Script executed: #!/bin/bash
echo "== AlarmUseCase.java 스니펫 확인 =="
rg "class AlarmUseCase" -A30 -B10 src/main/java/akuma/whiplash/domains/alarm/application/usecase/AlarmUseCase.java
echo "== getRemainingOffCount 메서드 정의 및 호출 확인 =="
rg "getRemainingOffCount" -tjava -A5 -B5 srcLength of output: 6619 도메인 서비스는 원시 타입 반환, UseCase에서 DTO 매핑으로 책임 분리 현재 • AlarmQueryService 인터페이스와 구현체에서 DTO 대신 수정 대상:
// AlarmQueryService.java
-public interface AlarmQueryService {
- AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId);
-}
+public interface AlarmQueryService {
+ long getRemainingOffCount(Long memberId);
+}// AlarmQueryServiceImpl.java
-@Override
-public AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId) {
- // ... 계산 로직
- return new AlarmRemainingOffCountResponse(count);
-}
+@Override
+public long getRemainingOffCount(Long memberId) {
+ // ... 계산 로직
+ return count;
+}// AlarmUseCase.java
-public AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId) {
- return alarmQueryService.getRemainingOffCount(memberId);
-}
+@Transactional(readOnly = true)
+public AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId) {
+ long count = alarmQueryService.getRemainingOffCount(memberId);
+ return new AlarmRemainingOffCountResponse(count);
+}이렇게 하면 도메인 레이어의 의존성을 줄이고, UseCase가 DTO 매핑을 책임지도록 분리할 수 있습니다. 📝 Committable suggestion
Suggested change
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.application.dto.response.CreateAlarmOccurrenceResponse; | ||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.domain.service.AlarmCommandService; | ||||||||||||||||||||||||||||||||||
| import akuma.whiplash.domains.alarm.domain.service.AlarmQueryService; | ||||||||||||||||||||||||||||||||||
|
|
@@ -42,4 +43,8 @@ public void checkinAlarm(Long memberId, Long alarmId, AlarmCheckinRequest reques | |||||||||||||||||||||||||||||||||
| public List<AlarmInfoPreviewResponse> getAlarms(Long memberId) { | ||||||||||||||||||||||||||||||||||
| return alarmQueryService.getAlarms(memberId); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public AlarmRemainingOffCountResponse getWeeklyRemainingOffCount(Long memberId) { | ||||||||||||||||||||||||||||||||||
| return alarmQueryService.getWeeklyRemainingOffCount(memberId); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,6 +67,7 @@ public class AlarmCommandServiceImpl implements AlarmCommandService { | |
| private String sheetRange; | ||
|
|
||
| private static final double CHECKIN_RADIUS_METERS = 100.0; | ||
| private static final int WEEKLY_OFF_LIMIT = 2; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) WEEKLY_OFF_LIMIT 상수의 단일 출처화 제안 한 곳(예: domain 상수 클래스 또는 설정값)으로 상수 출처를 통일하세요. 커맨드/쿼리 양쪽에서 동일 상수를 참조하면 변경 비용과 불일치 리스크를 줄일 수 있습니다. 예시(새 상수 클래스 추가): // src/main/java/akuma/whiplash/domains/alarm/domain/constant/AlarmLimits.java
package akuma.whiplash.domains.alarm.domain.constant;
public final class AlarmLimits {
private AlarmLimits() {}
public static final int WEEKLY_OFF_LIMIT = 2;
}이 파일 적용: - private static final int WEEKLY_OFF_LIMIT = 2;
+ // TODO: 상수 단일 출처화 완료 후 로컬 상수 제거 예정
+ // private static final int WEEKLY_OFF_LIMIT = 2;그리고 사용부: import static akuma.whiplash.domains.alarm.domain.constant.AlarmLimits.WEEKLY_OFF_LIMIT;🤖 Prompt for AI Agents |
||
|
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @Override | ||
| public void createAlarm(AlarmRegisterRequest request, Long memberId) { | ||
|
|
@@ -118,7 +119,7 @@ public AlarmOffResultResponse alarmOff(Long memberId, Long alarmId, LocalDateTim | |
| ); | ||
|
|
||
| // 5. 제한 초과 시 예외 발생 | ||
| if (weeklyOffCount >= 2) { | ||
| if (weeklyOffCount >= WEEKLY_OFF_LIMIT) { | ||
| throw ApplicationException.from(ALARM_OFF_LIMIT_EXCEEDED); | ||
| } | ||
|
Comment on lines
121
to
124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain주간 OFF 제한 체크의 경쟁 조건(race condition) 가능성 동일 회원에 대한 동시 요청 두 건이 같은 주 내에 거의 동시에 들어오면, 둘 다 weeklyOffCount를 낮게 읽고 제한을 통과해 초과 허용될 수 있습니다. 단일 트랜잭션이어도 별 트랜잭션 간에는 상호 가시성이 없어 방지되지 않습니다. 권장 대응(택1 또는 병행):
예시(Repository + 사용부): // MemberRepository.java
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select m from MemberEntity m where m.id = :id")
Optional<MemberEntity> findByIdForUpdate(@Param("id") Long id);// AlarmCommandServiceImpl.alarmOff(...)
MemberEntity lockedMember = memberRepository.findByIdForUpdate(memberId)
.orElseThrow(() -> ApplicationException.from(MemberErrorCode.MEMBER_NOT_FOUND));
// 이후 weeklyOffCount 조회/검증/저장 수행또는 회원-주 키를 가지는 별도 테이블(예: member_weekly_off_counter)에 unique(member_id, week_start) + 남은 횟수 컬럼을 두고, 행 잠금으로 원자적 감소를 수행하는 방식도 견고합니다. 검증 요청: 동시성 테스트(동일 memberId로 병렬 호출 3회)에서 초과가 허용되지 않는지 확인 부탁드립니다. 아래 스크립트로 동시성 제약(락/트랜잭션) 적용 여부를 확인해 주세요: 🏁 Script executed: #!/bin/bash
# AlarmCommandServiceImpl 내 weeklyOffCount 사용 위치 확인
rg "weeklyOffCount" -n src/main/java/akuma/whiplash/domains/alarm/domain/service/AlarmCommandServiceImpl.java
# 알람 끄기 메서드 시그니처 및 트랜잭션 어노테이션 확인
rg -n -A2 "class AlarmCommandServiceImpl" src/main/java/akuma/whiplash/domains/alarm/domain/service/AlarmCommandServiceImpl.java
rg -n "@Transactional" src/main/java/akuma/whiplash/domains/alarm/domain/service/AlarmCommandServiceImpl.java
# MemberRepository 에서 PESSIMISTIC_WRITE 락 메서드 정의 여부 확인
rg -n "interface MemberRepository" -A5 src/main/java/akuma/whiplash/domains
rg -n "@Lock" src/main/java/akuma/whiplash/domainsLength of output: 1373 경쟁 조건 방지를 위해 PESSIMISTIC_WRITE 락 또는 원자적 감소 로직 적용 필요 AlarmCommandServiceImpl.java (115–124행)에서 트랜잭션 기본 격리 수준(READ_COMMITTED) 하에 수정 제안 (택1 또는 병행):
또는
동일 memberId로 병렬 3회 호출 시 한도가 절대 초과되지 않는지 확인하는 동시성 테스트도 병행해 주세요. 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,12 @@ | |
|
|
||
| import akuma.whiplash.domains.alarm.application.dto.etc.OccurrencePushInfo; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmInfoPreviewResponse; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmRemainingOffCountResponse; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 🛠️ Refactor suggestion Domain 서비스가 Application DTO에 의존하고 있습니다 — 계층 분리 권장
아래처럼 반환 타입을 단순화하면 계층 결합을 낮출 수 있습니다. -import akuma.whiplash.domains.alarm.application.dto.response.AlarmRemainingOffCountResponse;
+// 애플리케이션 DTO 의존 제거
...
- AlarmRemainingOffCountResponse getRemainingOffCount(Long memberId);
+ long getRemainingOffCount(Long memberId);추가로, 구현체(AlarmQueryServiceImpl)와 UseCase에서 해당 변경에 맞춰 매핑을 적용해 주세요. Also applies to: 12-12 🤖 Prompt for AI Agents |
||
| import java.time.LocalDateTime; | ||
| import java.util.List; | ||
|
|
||
| public interface AlarmQueryService { | ||
| List<AlarmInfoPreviewResponse> getAlarms(Long memberId); | ||
| List<OccurrencePushInfo> getPreNotificationTargets(LocalDateTime startInclusive, LocalDateTime endInclusive); | ||
| AlarmRemainingOffCountResponse getWeeklyRemainingOffCount(Long memberId); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| import akuma.whiplash.domains.alarm.application.dto.etc.OccurrencePushInfo; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmInfoPreviewResponse; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmRemainingOffCountResponse; | ||
| import akuma.whiplash.domains.alarm.domain.constant.DeactivateType; | ||
| import akuma.whiplash.domains.alarm.domain.constant.Weekday; | ||
| import akuma.whiplash.domains.alarm.persistence.entity.AlarmEntity; | ||
|
|
@@ -39,6 +40,8 @@ public class AlarmQueryServiceImpl implements AlarmQueryService { | |
| private final AlarmOffLogRepository alarmOffLogRepository; | ||
| private final MemberRepository memberRepository; | ||
|
|
||
| private static final int WEEKLY_OFF_LIMIT = 2; | ||
|
|
||
| @Transactional | ||
| @Override | ||
| public List<AlarmInfoPreviewResponse> getAlarms(Long memberId) { | ||
|
|
@@ -85,6 +88,28 @@ public List<OccurrencePushInfo> getPreNotificationTargets(LocalDateTime startInc | |
| )); | ||
| } | ||
|
|
||
| @Override | ||
| public AlarmRemainingOffCountResponse getWeeklyRemainingOffCount(Long memberId) { | ||
| memberRepository | ||
| .findById(memberId) | ||
| .orElseThrow(() -> ApplicationException.from(MemberErrorCode.MEMBER_NOT_FOUND)); | ||
|
|
||
| LocalDate today = LocalDate.now(); | ||
| LocalDate monday = today.with(DayOfWeek.MONDAY); | ||
| LocalDateTime weekStart = monday.atStartOfDay(); | ||
| LocalDateTime now = LocalDateTime.now(); | ||
|
|
||
|
Comment on lines
+97
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion now/주차 기준 개선: Clock 주입 및 Monday 계산 명시화 권장
적용 예시(diff는 현재 범위 내 변경만 포함): - LocalDate today = LocalDate.now();
- LocalDate monday = today.with(DayOfWeek.MONDAY);
- LocalDateTime weekStart = monday.atStartOfDay();
- LocalDateTime now = LocalDateTime.now();
+ LocalDate today = LocalDate.now(clock);
+ LocalDate monday = today.with(java.time.temporal.TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
+ LocalDateTime weekStart = monday.atStartOfDay();
+ LocalDateTime now = LocalDateTime.now(clock);범위 밖 변경(참고): // 필드 주입
private final java.time.Clock clock;
// 구성 클래스 등지에서 Bean 제공 (예: KST 고정이 필요하다면)
@Bean
public Clock clock() {
return Clock.system(ZoneId.of("Asia/Seoul"));
}🤖 Prompt for AI Agents |
||
| long offCount = alarmOffLogRepository.countByMemberIdAndCreatedAtBetween( | ||
| memberId, weekStart, now | ||
| ); | ||
|
|
||
| int count = (int) Math.max(0, WEEKLY_OFF_LIMIT - offCount); | ||
|
|
||
| return AlarmRemainingOffCountResponse.builder() | ||
| .remainingOffCount(count) | ||
| .build(); | ||
| } | ||
|
|
||
| private AlarmInfoPreviewResponse buildPreviewResponse(AlarmEntity alarm, LocalDate today, Long memberId) { | ||
| // 1. 가장 최근 OFF 또는 CHECKIN 이력 조회 | ||
| Optional<AlarmOccurrenceEntity> recentOccurrenceOpt = | ||
|
|
@@ -133,9 +158,6 @@ private AlarmInfoPreviewResponse buildPreviewResponse(AlarmEntity alarm, LocalDa | |
| final LocalDate resolvedFirstUpcomingDate = isCurrentDeactivated ? secondDate : firstDate; | ||
| final LocalDate resolvedSecondUpcomingDate = isCurrentDeactivated ? thirdDate : secondDate; | ||
|
|
||
| // 5. 회원의 이번 주 남은 알람 끄기 횟수 계산 | ||
| long remainingOffCount = calculateRemainingOffCount(memberId); | ||
|
|
||
| return AlarmInfoPreviewResponse.builder() | ||
| .alarmId(alarm.getId()) | ||
| .alarmPurpose(alarm.getAlarmPurpose()) | ||
|
|
@@ -153,25 +175,8 @@ private AlarmInfoPreviewResponse buildPreviewResponse(AlarmEntity alarm, LocalDa | |
| .firstUpcomingDayOfWeek(DateUtil.getKoreanDayOfWeek(resolvedFirstUpcomingDate)) | ||
| .secondUpcomingDay(resolvedSecondUpcomingDate) | ||
| .secondUpcomingDayOfWeek(DateUtil.getKoreanDayOfWeek(resolvedSecondUpcomingDate)) | ||
| .remainingOffCount(remainingOffCount) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * 이번 주 월요일부터 현재까지의 OFF 로그를 기반으로 남은 끄기 횟수 계산 | ||
| */ | ||
| private long calculateRemainingOffCount(Long memberId) { | ||
| LocalDate today = LocalDate.now(); | ||
| LocalDate monday = today.with(DayOfWeek.MONDAY); | ||
| LocalDateTime weekStart = monday.atStartOfDay(); | ||
| LocalDateTime now = LocalDateTime.now(); | ||
|
|
||
| long offCount = alarmOffLogRepository.countByMemberIdAndCreatedAtBetween( | ||
| memberId, weekStart, now | ||
| ); | ||
|
|
||
| return Math.max(0, 2 - offCount); | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| import akuma.whiplash.domains.alarm.application.dto.request.AlarmRegisterRequest; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmInfoPreviewResponse; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmOffResultResponse; | ||
| import akuma.whiplash.domains.alarm.application.dto.response.AlarmRemainingOffCountResponse; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain신규 엔드포인트 설계 적절 — 오류 코드 문서화 및 테스트 보강 제안 구현은 명확하고 일관적입니다. 두 가지 보강점을 제안합니다.
원하시면 해당 엔드포인트용 Spring MVC 테스트 템플릿을 생성해 드리겠습니다. Also applies to: 110-118 🏁 Script executed: #!/bin/bash
# Display the controller definition and imports
rg -n "class AlarmController" -A15 -B15 src/main/java/akuma/whiplash/domains/alarm/presentation/AlarmController.java
# Show the new /off-count endpoint snippet
rg -n "off-count" -A5 -B5 src/main/java/akuma/whiplash/domains/alarm/presentation/AlarmController.java
# Show the existing getAlarms endpoint for comparison
rg -n "getAlarms" -A5 -B5 src/main/java/akuma/whiplash/domains/alarm/presentation/AlarmController.java
# Find occurrences of PERMISSION_DENIED across the codebase
rg -n "PERMISSION_DENIED"
# Check for authErrorCodes usage in controllers
rg -n "authErrorCodes" -A3 -B3 src/main/java/akuma/whiplash/domainsLength of output: 8965 🏁 Script executed: #!/bin/bash
# Search for any controller tests referencing the /off-count endpoint
rg -n "/off-count" -g "src/test/java/**/*.java"
# Search for tests referencing getRemainingOffCount method
rg -n "getRemainingOffCount" -g "src/test/java/**/*.java"
# Search for tests referencing AlarmController directly
rg -n "AlarmController" -g "src/test/java/**/*.java"Length of output: 158 /api/alarms/off-count 엔드포인트: PERMISSION_DENIED 생략은 의도된 패턴, 테스트 추가 필요
다만, 현재
필요하시면 Spring MVC 테스트 템플릿을 제공해 드리겠습니다. 🤖 Prompt for AI Agents |
||
| import akuma.whiplash.domains.alarm.application.dto.response.CreateAlarmOccurrenceResponse; | ||
| import akuma.whiplash.domains.alarm.application.usecase.AlarmUseCase; | ||
| import akuma.whiplash.domains.auth.application.dto.etc.MemberContext; | ||
|
|
@@ -105,4 +106,14 @@ public ApplicationResponse<List<AlarmInfoPreviewResponse>> getAlarms(@Authentica | |
| List<AlarmInfoPreviewResponse> alarms = alarmUseCase.getAlarms(memberContext.memberId()); | ||
| return ApplicationResponse.onSuccess(alarms); | ||
| } | ||
|
|
||
| @CustomErrorCodes(memberErrorCodes = {MEMBER_NOT_FOUND}) | ||
| @Operation(summary = "남은 알람 끄기 횟수 조회", description = "회원의 이번 주 남은 알람 끄기 횟수를 조회합니다.") | ||
| @GetMapping("/off-count") | ||
| public ApplicationResponse<AlarmRemainingOffCountResponse> getWeeklyRemainingOffCount( | ||
| @AuthenticationPrincipal MemberContext memberContext | ||
| ) { | ||
| AlarmRemainingOffCountResponse response = alarmUseCase.getWeeklyRemainingOffCount(memberContext.memberId()); | ||
| return ApplicationResponse.onSuccess(response); | ||
| } | ||
|
Comment on lines
+110
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent ❓ Verification inconclusive신규 엔드포인트 설계는 명확 — 테스트 커버리지 추가 필요 계약, 에러코드, 문서화 모두 일관적입니다. 다음 시나리오의 MVC/통합 테스트를 추가해 주세요:
검증용 스크립트: 원하시면 MockMvc 기반 테스트 템플릿을 제공합니다. 🏁 Script executed: #!/bin/bash
# /off-count 엔드포인트 테스트 존재 여부 점검
rg -n "/api/alarms/off-count" -g "src/test/java/**/*.java" -A2 -B2 || trueLength of output: 73 테스트 추가 필요:
추가 대상 예시 위치:
원하시면 MockMvc 테스트 템플릿을 제공합니다. 🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,6 @@ | |||||||||||||||
| public record RegisterFcmTokenRequest( | ||||||||||||||||
|
|
||||||||||||||||
| @Schema(description = "FCM 토큰", example = "djkhsa01whjas") | ||||||||||||||||
| @NotBlank | ||||||||||||||||
| @NotBlank(message = "FCM 토큰을 입력해주세요.") | ||||||||||||||||
| String fcmToken | ||||||||||||||||
|
Comment on lines
9
to
11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) 국제화/일관성: 하드코딩 메시지 대신 메시지 코드 사용을 고려해주세요. 팀에서 MessageSource(예: messages.properties)을 사용한다면, 직접 문자열 대신 메시지 코드를 사용하는 편이 일관성과 다국어 확장성에 유리합니다. 아래처럼 변경을 제안드립니다: - @NotBlank(message = "FCM 토큰을 입력해주세요.")
+ @NotBlank(message = "{validation.fcmToken.notBlank}")(messages.properties 예시) 원하시면 messages 파일 생성/추가까지 포함한 패치도 드리겠습니다. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| ) {} | ||||||||||||||||
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.
🧹 Nitpick (assertive)
단일 필드 record에서 @builder는 과합니다 + 최소값 스키마 명시 제안
@Builder없이도 가독성과 생성 용이성이 충분합니다. 팀 컨벤션에 특별한 이유가 없다면 제거를 권장합니다.remainingOffCount는 음수가 되지 않으므로 스키마에 최소값을 명시하면 API 문서 품질이 좋아집니다.예시 diff:
Also applies to: 9-13
🤖 Prompt for AI Agents