-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 7일 TTL 만료 감지 스케줄러 및 치즈네컷 기능 구현 #55
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
Walkthrough스케줄링 활성화( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Controller
participant Service
participant Validator
participant Repo
participant Redis
participant Scheduler
rect rgb(230,245,255)
note right of Controller: Cheese4cut 확정(수동) 흐름
User->>Controller: POST /v1/cheese4cut/{code}/fixed
Controller->>Service: finalizeCheese4cut(user, code, request)
Service->>Repo: find Album by code
Service->>Validator: validateUserIsMaker(album, user)
Service->>Validator: validateFinalizePhotos(album, photoIds)
alt validation ok
Service->>Repo: save(Cheese4cut)
Service-->>Controller: 성공 응답
else validation 실패
Validator-->>Service: 예외 발생
Service-->>Controller: 오류 응답
end
end
rect rgb(240,255,240)
note right of Scheduler: 앨범 만료 자동 처리 흐름
Scheduler->>Redis: getTrackedAlbumIds()
Redis-->>Scheduler: Set<albumId>
loop albumId in set
Scheduler->>Redis: isExpired(albumId)
alt expired
Scheduler->>Service: expireAlbum(albumId)
Service->>Repo: findTop4CompletedPhotoIdsByLikes / save Cheese4cut
Scheduler->>Redis: unregister(albumId)
else not expired
Scheduler-->>Scheduler: 다음 ID
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
🧰 Additional context used🧠 Learnings (1)📚 Learning: 2025-10-31T13:17:52.523ZApplied to files:
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 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.
Actionable comments posted: 8
🧹 Nitpick comments (6)
src/main/java/com/cheeeese/photo/application/PresignedUrlService.java (1)
46-49: Content-Type 지정 누락을 확인하세요.기존
generatePresignedPutUrl메서드(라인 26-31)와 달리contentType을 지정하지 않았습니다. PNG 이미지 업로드 시image/png를 명시하는 것이 좋습니다.다음과 같이 수정을 고려하세요:
PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(cheese4cutBucket) .key(objectKey) + .contentType("image/png") .build();src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoLikesRepository.java (1)
25-30: LGTM!고유한 좋아요 사용자 수를 계산하는 쿼리 메서드가 올바르게 구현되었습니다. JPQL 문법과 메서드 시그니처가 Spring Data JPA 규칙을 잘 따르고 있습니다.
선택사항:
photoIds리스트가 매우 클 경우 IN 절의 성능을 고려해야 할 수 있습니다. 예상되는 사진 개수가 적다면 문제없습니다.src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutPreviewResponse.java (1)
8-23: Preview response 구조가 잘 설계되었습니다.미리보기 상태를 나타내는 DTO로서 필요한 정보들이 적절하게 포함되어 있습니다. 다만
isFinalized가 schema에서 "항상 false"로 명시되어 있지만, 이는 런타임에 강제되지 않습니다. 필요하다면 compact constructor에서 검증을 추가할 수 있습니다.선택적 개선 (필요시):
@Builder @Schema(description = "치즈네컷 확정 전 미리보기 응답 DTO (좋아요 TOP 4)") public record Cheese4cutPreviewResponse( @Schema(description = "확정 여부 (항상 false)", example = "false") boolean isFinalized, // ... 나머지 필드들 ) implements Cheese4cutResponse { public Cheese4cutPreviewResponse { if (isFinalized) { throw new IllegalArgumentException("PreviewResponse must have isFinalized=false"); } } // ... 나머지 코드 }src/main/java/com/cheeeese/album/application/AlbumExpirationScheduler.java (1)
19-19: 스케줄링 간격이 너무 짧습니다.
fixedDelay = 1000L(1초)은 7일 TTL 기반의 만료 처리에는 과도하게 빠른 간격입니다. 프로덕션 환경에서 불필요한 Redis 조회가 반복되어 부하가 될 수 있습니다. 1분(60000L) 또는 그 이상의 간격으로 조정하는 것을 권장합니다.다음과 같이 수정하는 것을 고려하세요:
- @Scheduled(fixedDelay = 1000L) + @Scheduled(fixedDelay = 60000L) // 1분마다 실행 public void handleAlbumExpirations() {src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (1)
38-68: 매직 넘버를 상수로 치환하는 것이 좋습니다.
Line 62와 Line 66에 하드코딩된 4는 의미 파악과 추후 수정 시 실수를 유발할 수 있습니다. 클래스 상단에 상수를 두고 재사용하면 다른 위치(예: 자동 만료 로직)와도 일관성을 유지할 수 있습니다.@Service @RequiredArgsConstructor @Transactional public class Cheese4cutService { + private static final int CHEESE4CUT_PHOTO_COUNT = 4; + private final Cheese4cutRepository cheese4cutRepository; private final AlbumRepository albumRepository; @@ List<Long> topPhotoIds = photoRepository.findTop4CompletedPhotoIdsByLikes( albumId, - PageRequest.of(0, 4) + PageRequest.of(0, CHEESE4CUT_PHOTO_COUNT) ); - if (topPhotoIds.size() < 4) { + if (topPhotoIds.size() < CHEESE4CUT_PHOTO_COUNT) { throw new Cheese4cutException(Cheese4cutErrorCode.INSUFFICIENT_COUNT_FOR_CHEESE4CUT); }src/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java (1)
36-41: 도메인 비즈니스 로직 메서드 추가를 고려하세요.현재 엔티티가 빈약한 도메인 모델(anemic domain model) 패턴을 따르고 있습니다. API 응답에서 사용되는
isFinalized판단 로직이나, frameImageUrl 업데이트를 위한 메서드를 엔티티에 추가하면 도메인 로직을 캡슐화할 수 있습니다.예시:
// frameImageUrl이 설정되면 확정된 것으로 간주 public boolean isFinalized() { return frameImageUrl != null && !frameImageUrl.isEmpty(); } // 최종 프레임 이미지 업데이트 (7일 만료 시 사용) public void updateFrameImage(String frameImageUrl) { if (this.isFinalized()) { throw new Cheese4cutException(Cheese4cutErrorCode.CHEESE4CUT_ALREADY_FINALIZED); } this.frameImageUrl = frameImageUrl; }이렇게 하면 PR 목표에서 언급된 "7일 만료 후 치즈네컷 생성 문제"를 해결하는 데 도움이 될 수 있습니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
src/main/java/com/cheeeese/CheeeeseApplication.java(1 hunks)src/main/java/com/cheeeese/album/application/AlbumExpirationScheduler.java(1 hunks)src/main/java/com/cheeeese/album/application/AlbumExpirationService.java(1 hunks)src/main/java/com/cheeeese/album/application/AlbumService.java(3 hunks)src/main/java/com/cheeeese/album/domain/Album.java(1 hunks)src/main/java/com/cheeeese/album/exception/code/AlbumErrorCode.java(1 hunks)src/main/java/com/cheeeese/album/infrastructure/persistence/AlbumExpirationRedisRepository.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/application/validator/Cheese4cutValidator.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/request/Cheese4cutFixedRequest.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutFinalResponse.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutPresignedUrlResponse.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutPreviewResponse.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutResponse.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/exception/Cheese4cutException.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/exception/code/Cheese4cutErrorCode.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/infrastructure/persistence/Cheese4cutRepository.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/presentation/swagger/Cheese4cutSwagger.java(1 hunks)src/main/java/com/cheeeese/global/common/code/SuccessCode.java(1 hunks)src/main/java/com/cheeeese/global/config/SecurityConfig.java(1 hunks)src/main/java/com/cheeeese/photo/application/PresignedUrlService.java(2 hunks)src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoLikesRepository.java(1 hunks)src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.java(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-31T13:17:52.523Z
Learnt from: dahyun24
Repo: Say-Cheeeese/BE PR: 35
File: src/main/java/com/cheeeese/photo/application/PhotoService.java:46-52
Timestamp: 2025-10-31T13:17:52.523Z
Learning: In src/main/java/com/cheeeese/photo/application/PhotoService.java, the getRecentThumbnailUrls method intentionally returns only the first thumbnail URL when photos.size() < 5, rather than returning all available thumbnails. This is according to product requirements: 0 photos → empty list, 1-4 photos → single thumbnail (most recent), 5 photos → all 5 thumbnails.
Applied to files:
src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.java
📚 Learning: 2025-11-05T03:06:41.847Z
Learnt from: zyovn
Repo: Say-Cheeeese/BE PR: 44
File: src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoHistoryRepository.java:36-36
Timestamp: 2025-11-05T03:06:41.847Z
Learning: PhotoHistoryRepository의 existsByUserIdAndPhotoIdAndCreatedAt 메서드는 정확한 타임스탬프 일치를 확인하는 시그니처로, "1시간 이내 다운로드" 체크에는 부적합합니다. Spring Data JPA에서는 existsByUserIdAndPhotoIdAndCreatedAtAfter를 사용하여 >= 비교를 수행해야 합니다.
Applied to files:
src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoLikesRepository.java
🧬 Code graph analysis (3)
src/main/java/com/cheeeese/cheese4cut/exception/code/Cheese4cutErrorCode.java (1)
src/main/java/com/cheeeese/cheese4cut/exception/Cheese4cutException.java (1)
Getter(7-12)
src/main/java/com/cheeeese/album/application/AlbumExpirationService.java (1)
src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java (1)
Cheese4cutMapper(14-77)
src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (1)
src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java (1)
Cheese4cutMapper(14-77)
🔇 Additional comments (16)
src/main/java/com/cheeeese/album/exception/code/AlbumErrorCode.java (1)
24-24: LGTM!새로운 에러 코드가 적절한 HTTP 상태 코드와 함께 일관된 패턴으로 추가되었습니다.
src/main/java/com/cheeeese/global/config/SecurityConfig.java (1)
40-40: LGTM!치즈네컷 미리보기 엔드포인트를 화이트리스트에 추가하여 인증 없이 접근 가능하도록 설정한 것이 적절합니다.
src/main/java/com/cheeeese/album/domain/Album.java (1)
69-71: LGTM!앨범 상태를 만료로 변경하는 메서드가 간결하고 명확하게 구현되었습니다. 도메인 엔티티의 상태 변경 패턴을 잘 따르고 있습니다.
src/main/java/com/cheeeese/album/application/AlbumService.java (1)
80-81: Redis 등록 실패 시 처리 방식을 검토하세요.앨범 생성 후 Redis에 만료 추적을 등록하고 있지만, Redis 등록이 실패해도 앨범은 이미 DB에 저장된 상태입니다. 이로 인해 DB와 Redis 간 불일치가 발생할 수 있습니다.
다음을 확인해주세요:
- Redis 등록 실패 시 앨범 생성을 롤백해야 하는지
- Redis를 필수 구성 요소로 간주하는지 아니면 장애 허용(fail-safe)으로 처리할지
- Redis 장애 시 대체 만료 처리 방안이 있는지
Redis 등록이 필수라면, 다음과 같이 예외 처리를 추가하는 것을 고려하세요:
albumRepository.save(album); userAlbumRepository.save(UserAlbumMapper.toEntity( user, album, Role.MAKER )); -albumExpirationRedisRepository.registerAlbum(album.getId()); +try { + albumExpirationRedisRepository.registerAlbum(album.getId()); +} catch (Exception e) { + log.error("Failed to register album {} for expiration tracking", album.getId(), e); + throw new RuntimeException("앨범 만료 추적 등록에 실패했습니다.", e); +} return AlbumMapper.toCreationResponse(album);src/main/java/com/cheeeese/cheese4cut/infrastructure/persistence/Cheese4cutRepository.java (1)
8-10: 유니크 제약조건이 이미 설정되어 있습니다.검증 결과, Cheese4cut 엔티티(src/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java)의 album 필드에
@JoinColumn(name = "album_id", nullable = false, unique = true)로 이미 유니크 제약조건이 적용되어 있습니다. 따라서findByAlbumId가Optional<Cheese4cut>을 반환하는 것은 올바른 설계이며, 앨범당 정확히 하나의 Cheese4cut만 존재함이 보장됩니다.src/main/java/com/cheeeese/CheeeeseApplication.java (1)
12-12: 스케줄러 설정 확인 완료 - 프로덕션 배포 전 검토 필요
@EnableScheduling설정은 올바르게 적용되었습니다. 다만, AlbumExpirationScheduler에서@Scheduled(fixedDelay = 1000L)로 설정되어 1초마다 실행되도록 구성되어 있습니다. 현재 프로젝트에는 스케줄러 스레드 풀 설정이 없으므로 다음을 확인해주세요:
- 1초 주기가 프로덕션 환경에 적합한지 검토 (Redis 접근 빈도, CPU 부하 고려)
- 필요시 다음과 같이 스케줄러 풀 크기를 명시적으로 설정
spring: task: scheduling: pool: size: 2스케줄러 실행 간격과 풀 크기 설정이 시스템 리소스에 미치는 영향을 검토한 후 배포하시기 바랍니다.
src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutResponse.java (1)
5-9: LGTM! Sealed interface 패턴이 적절하게 사용되었습니다.치즈네컷 응답의 다형 구조를 sealed interface로 명확하게 제한하여 타입 안전성을 확보했습니다.
src/main/java/com/cheeeese/global/common/code/SuccessCode.java (1)
40-42: LGTM! 성공 코드가 일관된 패턴으로 추가되었습니다.기존 enum 패턴을 따르며 명확한 메시지를 제공합니다.
src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutPresignedUrlResponse.java (1)
6-11: LGTM! 간결한 응답 DTO입니다.Presigned URL 발급 응답으로 적절한 구조입니다.
src/main/java/com/cheeeese/cheese4cut/exception/Cheese4cutException.java (1)
7-12: LGTM! 도메인 예외 클래스가 적절하게 구현되었습니다.BusinessException을 확장하여 일관된 예외 처리 패턴을 따릅니다.
src/main/java/com/cheeeese/cheese4cut/application/validator/Cheese4cutValidator.java (1)
36-54: LGTM! 사진 검증 로직이 철저합니다.4개의 유니크한 photo ID 검증, 존재 여부 확인, 그리고 앨범 소속 및 COMPLETED 상태 검증이 적절하게 구현되었습니다.
src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutPreviewResponse.java (1)
24-32: LGTM! 중첩 record가 깔끔하게 구현되었습니다.PreviewPhotoInfo가 미리보기 사진 정보를 명확하게 표현합니다.
src/main/java/com/cheeeese/album/application/AlbumExpirationScheduler.java (1)
20-39: LGTM! 만료 처리 로직이 안정적으로 구현되었습니다.각 앨범별로 예외를 독립적으로 처리하여 하나의 실패가 전체 프로세스를 중단시키지 않도록 했으며, 적절한 로깅도 포함되어 있습니다.
src/main/java/com/cheeeese/cheese4cut/exception/code/Cheese4cutErrorCode.java (1)
8-21: 구현이 올바릅니다.에러 코드 정의가 명확하고, HTTP 상태 코드도 각 에러 유형에 적절하게 매핑되어 있습니다.
src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutFinalResponse.java (1)
6-15: Record 구조가 적절합니다.불변 DTO로서 올바르게 구현되었으며, Swagger 문서화도 명확합니다.
src/main/java/com/cheeeese/cheese4cut/presentation/swagger/Cheese4cutSwagger.java (1)
20-142: API 문서화가 상세하고 명확합니다.Swagger 문서가 다형적(polymorphic) 응답 구조를 포함한 API 동작을 잘 설명하고 있으며, 에러 케이스에 대한 예시도 제공되어 있습니다.
src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java
Show resolved
Hide resolved
src/main/java/com/cheeeese/cheese4cut/application/validator/Cheese4cutValidator.java
Show resolved
Hide resolved
src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java
Show resolved
Hide resolved
src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java
Show resolved
Hide resolved
src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java
Show resolved
Hide resolved
src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.java
Show resolved
Hide resolved
zyovn
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.
🎸🧀 수고하셧ㅆ습니다 🐇🦖
치즈네컷 신경쓸게 매우매우매우 많네요;;
만료됐을 때, 서버 -> 프론트로 트리거를 보내서 치즈네컷을 생성하는 방법은.. 지금 생각나는 건 sse나 폴링인데 둘 다 치이이이이이잉이즈에 적합하지 않은 것 같기도 합니다... 차라리 서버에서 만들어야 할 것 같기도 하네요 ㅠ.ㅠ 넘 수고하셧습니다
글고 별 건 아닌데 presigned url 관련 api에서 어떤 건 body에서 앨범 코드 받고 어떤 건 pathVariable에서 코드 받아서 나중에 통일하면 좋을 것 가타요
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.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/main/java/com/cheeeese/album/application/AlbumExpirationService.java (1)
37-44: 이미 EXPIRED 상태인 경우 로그를 추가하세요.검증 결과,
Cheese4cut엔티티의album_id컬럼에unique = true제약이 이미 적용되어 있어 동시성 문제로 인한 데이터 중복 생성은 데이터베이스 레벨에서 방지됩니다. Line 42-44의 조기 반환 체크와 함께 race condition의 발생 확률은 매우 낮습니다.다만 다음 개선이 권장됩니다:
Line 37-40에서 앨범이 이미 EXPIRED 상태인 경우(로직이 실행되지 않는 경우)에도 로그를 추가하여 재시도 호출 여부를 추적할 수 있도록 개선하면 디버깅 및 모니터링이 용이해집니다:
if (album.getStatus() != Album.AlbumStatus.EXPIRED) { album.expire(); log.info("[AlbumExpiration] Album id={} status updated to EXPIRED", albumId); } else { log.debug("[AlbumExpiration] Album id={} already EXPIRED, skipping status update", albumId); }src/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java (1)
29-33:photoIds에@NotNull추가 권장
@Size(min=4, max=4)검증은 이전 피드백에 따라 잘 추가되었습니다. 다만@NotNull을 명시하면 null 안전성을 더 명확히 보장할 수 있습니다.+import jakarta.validation.constraints.NotNull; + @ElementCollection @CollectionTable(name = "cheese4cut_photos", joinColumns = @JoinColumn(name = "cheese4cut_id")) @Column(name = "photo_id", nullable = false) +@NotNull @Size(min = 4, max = 4) private List<Long> photoIds;src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (1)
54-56: TODO 작업 추적 필요PR 목표에서 언급된 "7일 만료 시 최종 치즈네컷이 생성되지 않는 문제"와 연관된 부분입니다. 현재는 확정 여부만 반환하고 실제 4장의 사진을 제공하지 않습니다.
이 작업을 추적할 이슈를 생성해드릴까요?
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/main/java/com/cheeeese/album/application/AlbumExpirationService.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/application/validator/Cheese4cutValidator.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/request/Cheese4cutFixedRequest.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutFinalResponse.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java(1 hunks)src/main/java/com/cheeeese/cheese4cut/presentation/swagger/Cheese4cutSwagger.java(1 hunks)src/main/java/com/cheeeese/photo/application/PresignedUrlService.java(3 hunks)src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.java(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-31T13:17:52.523Z
Learnt from: dahyun24
Repo: Say-Cheeeese/BE PR: 35
File: src/main/java/com/cheeeese/photo/application/PhotoService.java:46-52
Timestamp: 2025-10-31T13:17:52.523Z
Learning: In src/main/java/com/cheeeese/photo/application/PhotoService.java, the getRecentThumbnailUrls method intentionally returns only the first thumbnail URL when photos.size() < 5, rather than returning all available thumbnails. This is according to product requirements: 0 photos → empty list, 1-4 photos → single thumbnail (most recent), 5 photos → all 5 thumbnails.
Applied to files:
src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.javasrc/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.javasrc/main/java/com/cheeeese/cheese4cut/domain/Cheese4cut.java
🧬 Code graph analysis (2)
src/main/java/com/cheeeese/album/application/AlbumExpirationService.java (2)
src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java (1)
Cheese4cutMapper(14-75)src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (1)
Service(34-122)
src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (1)
src/main/java/com/cheeeese/cheese4cut/infrastructure/mapper/Cheese4cutMapper.java (1)
Cheese4cutMapper(14-75)
🔇 Additional comments (12)
src/main/java/com/cheeeese/photo/infrastructure/persistence/PhotoRepository.java (2)
59-60: 포맷 변경이 일관성을 개선했습니다.쿼리의 SELECT 절 포맷 변경으로 가독성이 향상되었습니다.
93-105: ORDER BY 절의 createdAt ASC 의도를 확인하세요.좋아요 수가 동일할 때
createdAt ASC(오래된 순)로 정렬하는 것은 일반적이지 않은 패턴입니다. 보통은 최신 사진을 우선하기 위해DESC를 사용합니다. 이것이 비즈니스 요구사항에 따른 의도된 동작인지 확인해주세요.참고: 이전 리뷰에서 지적된 enum 하드코딩 문제가 파라미터화로 잘 해결되었습니다.
src/main/java/com/cheeeese/album/application/AlbumExpirationService.java (2)
46-58: 사진 조회 및 검증 로직이 적절합니다.상위 4개의 완료된 사진 ID를 조회하고, 부족할 경우 경고 로그를 남기며 early return하는 로직이 명확하고 안전합니다. 다만
PhotoRepository.findTop4CompletedPhotoIdsByLikes의 ORDER BY 로직이 의도대로 동작하는지 확인이 필요합니다 (PhotoRepository 리뷰 참조).
61-64: Cheese4cut 생성 로직을 확인했습니다.매퍼를 통한 엔티티 생성 및 저장 로직이 간결합니다. 다만 위에서 언급한 동시성 이슈(Line 37-44)가 해결되어야 중복 생성을 방지할 수 있습니다.
src/main/java/com/cheeeese/photo/application/PresignedUrlService.java (1)
43-59: UUID 기반 파일명 적용으로 충돌 위험이 사라졌습니다.치즈네컷 버킷 분리와 10분 TTL 유지도 요구사항에 잘 부합합니다.
src/main/java/com/cheeeese/cheese4cut/application/validator/Cheese4cutValidator.java (1)
29-53: 예외 코드 및 검증 로직이 요구사항에 맞게 정리되었습니다.MAKER 권한 검증과 사진 상태/앨범 일치 검증이 모두 적절하게 구성되었습니다.
src/main/java/com/cheeeese/cheese4cut/dto/response/Cheese4cutFinalResponse.java (1)
6-11: 확정 응답 DTO가 명확하게 표현되었습니다.
isFinalized의 의미를 Swagger 메타로 명시해 클라이언트 측 이해가 쉬워졌습니다.src/main/java/com/cheeeese/cheese4cut/presentation/swagger/Cheese4cutSwagger.java (1)
24-141: Swagger 문서가 다형 응답과 권한 플로우를 잘 전달하고 있습니다.엔드포인트별 예시와 단계 설명이 구체적이라 클라이언트 연동 시 오해 여지가 없습니다.
src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java (1)
27-55: 엔드포인트 구현 확인 완료세 개의 엔드포인트 모두 적절한 검증 애너테이션과 비즈니스 로직 위임이 잘 구현되어 있습니다. 이전 리뷰의
@NotBlank추가 요청이 모두 반영되었습니다.src/main/java/com/cheeeese/cheese4cut/application/Cheese4cutService.java (3)
78-86: Null 체크 로직 구현 확인이전 리뷰에서 논의된 NullPointerException 방어 로직이 적절히 구현되었습니다. 두 쿼리 사이의 타이밍 이슈를 고려한 안전한 구현입니다.
93-101: Presigned URL 발급 로직 확인앨범 검증, 권한 확인, URL 생성이 적절히 구현되어 있습니다.
@Transactional(readOnly=true)사용도 적절합니다.
103-121: 치즈네컷 확정 로직 검증 완료비즈니스 규칙(만료 확인, 제작자 권한, 중복 방지, 사진 검증)이 체계적으로 구현되어 있습니다. 트랜잭션 범위도 적절합니다.
src/main/java/com/cheeeese/cheese4cut/dto/request/Cheese4cutFixedRequest.java
Outdated
Show resolved
Hide resolved
src/main/java/com/cheeeese/cheese4cut/presentation/Cheese4cutController.java
Show resolved
Hide resolved
치즈네컷 쪽에서는 presigned url이 필요없을 것 같기도 해서,, 일단 두고 필요없으면 메서드 삭제하겠습니다~ |
🔗 연관된 이슈
🚀 변경 유형
📝 작업 내용
/v1/cheese4cut/{code}/presigned-url/v1/cheese4cut/{code}/fixed/v1/cheese4cut/{code}/preview📸 스크린샷
💬 리뷰 요구사항
📜 리뷰 규칙
Reviewer는 아래 P5 Rule을 참고하여 리뷰를 진행합니다.
P5 Rule을 통해 Reviewer는 Reviewee에게 리뷰의 의도를 보다 정확히 전달할 수 있습니다.
Summary by CodeRabbit
새로운 기능
개선 사항