Conversation
Walkthrough교환 게시물 CRUD와 위치 기반 검색 기능이 추가되었고, 관련 DTO·엔티티·레포지토리·서비스·컨트롤러가 도입되었으며 Post의 카테고리가 List에서 Enum으로 변경되고 Comment 클래스의 Lombok 로그 어노테이션과 일부 클래스 레벨 트랜잭션 어노테이션이 제거되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as ExchangeController
participant Service as ExchangeService
participant S3
participant DB as Database
Client->>Controller: POST /api/exchanges (multipart: imageList + request)
Controller->>Service: createExchangePost(imageList, request)
alt images present
Service->>S3: upload images
S3-->>Service: imageUrlList
else no images
Service-->>Service: imageUrlList = []
end
Service->>Service: map request -> ExchangePost (mapper, enums)
Service->>DB: save(ExchangePost)
DB-->>Service: saved ExchangePost
Service-->>Controller: ExchangePostDetailResponse
Controller-->>Client: 201 Created (BaseResponse)
sequenceDiagram
participant Client
participant Controller as ExchangeController
participant Service as ExchangeService
participant DB as Database
Client->>Controller: GET /api/exchanges?pageNum=&pageSize=&latitude=&longitude=
Controller->>Controller: validate pageNum/pageSize
alt invalid pagination
Controller-->>Client: 400 Bad Request (PageErrorStatus)
else valid
Controller->>Service: getExchangePostsByLocation(pageable, lat, lon)
Service->>DB: findByDistanceAndStatus(lat, lon, BEFORE, pageable)
DB-->>Service: Page<ExchangePost> (ordered by ST_Distance_Sphere)
Service-->>Controller: PageResponse<ExchangePostCardResponse>
Controller-->>Client: 200 OK (BaseResponse)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java (2)
131-132: 중복된 조회수 증가 호출을 제거하세요.
post.increaseViews()가 연속으로 두 번 호출되어 게시물 조회 시마다 조회수가 2씩 증가합니다. 이는 잘못된 조회수 데이터를 초래합니다.다음 diff를 적용하여 중복 호출을 제거하세요:
post.increaseViews(); - - post.increaseViews(); log.info(
163-173: 중복된 이미지 삭제 로직을 제거하세요.기존 이미지들이 두 번 삭제되고 있습니다 (lines 163-165와 lines 171-173). 첫 번째 삭제 후 파일이 이미 제거되었기 때문에 두 번째 삭제 시도는 실패하게 됩니다.
Lines 171-173의 중복 삭제 로직을 제거하세요.
다음 diff를 적용하세요:
for (MultipartFile image : images) { newImageUrls.add(s3Service.uploadImage(PathName.POST, image).getImageUrl()); } - - for (String imageUrl : oldImageUrls) { - s3Service.deleteFile(s3Service.extractKeyNameFromUrl(imageUrl)); - } } catch (Exception e) {
🧹 Nitpick comments (8)
src/main/java/com/sku/refit/domain/post/dto/request/PostRequest.java (1)
22-24: String 기반 카테고리 검증 개선 권장
postCategory필드가 String 타입이지만 실제로는PostCategoryenum 값으로 파싱됩니다. 현재@NotEmpty는 공백만 있는 문자열을 허용하며, 유효하지 않은 enum 값이 입력되면 런타임 오류가 발생할 수 있습니다.다음 두 가지 방식 중 하나를 고려하세요:
방식 1: 타입을 PostCategory enum으로 변경 (권장)
- @NotEmpty(message = "게시글 카테고리는 필수입니다.") - @Schema(description = "게시글 카테고리", example = "FREE") - private String postCategory; + @NotNull(message = "게시글 카테고리는 필수입니다.") + @Schema(description = "게시글 카테고리", example = "FREE") + private PostCategory postCategory;방식 2: 커스텀 validation 어노테이션 추가
- @NotEmpty(message = "게시글 카테고리는 필수입니다.") + @NotBlank(message = "게시글 카테고리는 필수입니다.") + @Pattern(regexp = "FREE|REPAIR|INFO", message = "유효하지 않은 카테고리입니다.")src/main/java/com/sku/refit/domain/post/entity/Post.java (1)
61-64: 주석 처리된 코드 제거 권장주석 처리된
categoryList관련 코드는 새로운postCategoryenum으로 대체되었으므로 제거하는 것이 좋습니다. 주석으로 남겨둔 코드는 코드베이스를 혼란스럽게 만들 수 있습니다.- // @ElementCollection - // @CollectionTable(name = "post_category", joinColumns = @JoinColumn(name = "post_id")) - // @Column(nullable = false) - // private List<String> categoryList;src/main/java/com/sku/refit/domain/exchange/entity/ClothSize.java (1)
10-11: 불필요한 Lombok 어노테이션
ClothSizeEnum은 필드가 없으므로@RequiredArgsConstructor와@Getter가 불필요합니다. 이 어노테이션들은 무해하지만 코드를 간소화할 수 있습니다.다음 diff를 적용하여 불필요한 어노테이션을 제거하세요:
-@Getter -@RequiredArgsConstructor @Schema(description = "옷 사이즈 Enum") public enum ClothSize {src/main/java/com/sku/refit/domain/exchange/service/ExchangeService.java (1)
39-39: Javadoc 누락
getExchangePost메서드에 Javadoc 주석이 없어 다른 메서드들과 문서화 일관성이 떨어집니다.다음 diff를 적용하여 Javadoc을 추가하세요:
+ /** + * 교환 게시글 단건 조회 + * + * @param exchangePostId 교환 게시글 ID + * @return 교환 게시글 상세 응답 + */ ExchangePostDetailResponse getExchangePost(Long exchangePostId);src/main/java/com/sku/refit/domain/exchange/entity/ExchangePost.java (2)
44-49: @ElementCollection의 fetch 전략을 명시하세요.PR 목표에서 언급된 대로, 이미지 조회 성능 개선을 위해
@ElementCollection의 fetch 전략을 검토해야 합니다. 기본값은LAZY이지만, 명시적으로 지정하는 것이 좋습니다. 카드 리스트 조회 시 N+1 쿼리가 발생할 수 있습니다.다음 옵션을 고려하세요:
옵션 1: EAGER fetch (카드 리스트에서 항상 이미지가 필요한 경우)
@ElementCollection +@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT) @CollectionTable( name = "exchange_post_image_url", joinColumns = @JoinColumn(name = "exchange_post_id"))옵션 2: Entity Graph 사용 (선택적 fetch)
Repository에서 필요한 경우에만 fetch하도록@EntityGraph를 사용하세요.Also applies to: 69-75
49-49: 필드 초기화가 불필요합니다.
@ElementCollection필드에 대한= new ArrayList<>()초기화는 JPA가 프록시로 관리하므로 불필요합니다. 제거하는 것이 JPA의 관리 의도를 명확히 합니다.@ElementCollection @CollectionTable( name = "exchange_post_image_url", joinColumns = @JoinColumn(name = "exchange_post_id")) @Column(name = "image_url", nullable = false) -private List<String> imageUrlList = new ArrayList<>(); +private List<String> imageUrlList;동일하게 Line 75의
preferCategories에도 적용하세요.Also applies to: 75-75
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java (1)
57-62: 하드코딩된 기본 좌표값을 재고하세요.서울역 좌표를 기본값으로 사용하는 것은 사용자가 위치를 제공하지 않았을 때 예상치 못한 결과를 초래할 수 있습니다. 위치 기반 검색에서 좌표는 필수 파라미터로 만드는 것이 더 명확합니다.
@GetMapping @Operation(summary = "교환 게시글 목록(페이지) 조회 (위치 기반)") ResponseEntity<BaseResponse<PageResponse<ExchangePostCardResponse>>> getExchangePostsByLocation( @Parameter(description = "페이지 번호", example = "1") @RequestParam Integer pageNum, @Parameter(description = "페이지 크기", example = "4") @RequestParam Integer pageSize, @Parameter(description = "위도", example = "37.544018") - @RequestParam(defaultValue = "37.544018") + @RequestParam(required = true) Double latitude, @Parameter(description = "경도", example = "126.951592") - @RequestParam(defaultValue = "126.951592") + @RequestParam(required = true) Double longitude);src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java (1)
123-127: @ElementCollection은 항상 초기화되므로 null 체크가 불필요합니다.JPA의
@ElementCollection은 항상 빈 컬렉션으로 초기화되므로 null이 될 수 없습니다. null 체크는 불필요하며 혼란을 줄 수 있습니다.-if (exchangePost.getImageUrlList() != null) { - for (String imageUrl : exchangePost.getImageUrlList()) { +if (!exchangePost.getImageUrlList().isEmpty()) { + for (String imageUrl : exchangePost.getImageUrlList()) { s3Service.deleteFile(s3Service.extractKeyNameFromUrl(imageUrl)); } }Also applies to: 182-186
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
src/main/java/com/sku/refit/domain/comment/controller/CommentControllerImpl.java(0 hunks)src/main/java/com/sku/refit/domain/comment/service/CommentServiceImpl.java(0 hunks)src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/controller/ExchangeControllerImpl.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/entity/ClothSize.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/entity/ClothStatus.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/entity/ExchangeCategory.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/entity/ExchangePost.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/entity/ExchangeStatus.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/exception/ExchangeErrorCode.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/mapper/ExchangeMapper.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/repository/ExchangeRepository.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/service/ExchangeService.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java(1 hunks)src/main/java/com/sku/refit/domain/post/dto/request/PostRequest.java(1 hunks)src/main/java/com/sku/refit/domain/post/dto/response/PostDetailResponse.java(3 hunks)src/main/java/com/sku/refit/domain/post/entity/Post.java(2 hunks)src/main/java/com/sku/refit/domain/post/entity/PostCategory.java(1 hunks)src/main/java/com/sku/refit/domain/post/mapper/PostMapper.java(2 hunks)src/main/java/com/sku/refit/domain/post/repository/PostRepository.java(1 hunks)src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java(3 hunks)
💤 Files with no reviewable changes (2)
- src/main/java/com/sku/refit/domain/comment/controller/CommentControllerImpl.java
- src/main/java/com/sku/refit/domain/comment/service/CommentServiceImpl.java
🧰 Additional context used
🧬 Code graph analysis (7)
src/main/java/com/sku/refit/domain/exchange/entity/ExchangePost.java (3)
src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java (1)
Getter(17-63)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
Getter(12-28)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
Getter(17-60)
src/main/java/com/sku/refit/domain/exchange/entity/ClothStatus.java (1)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
Getter(17-60)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
Getter(17-60)
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeControllerImpl.java (1)
src/main/java/com/sku/refit/domain/comment/controller/CommentControllerImpl.java (1)
RestController(22-65)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (3)
src/main/java/com/sku/refit/domain/event/dto/response/EventResponse.java (1)
Schema(12-151)src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java (1)
Getter(17-63)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
Getter(12-28)
src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java (2)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
Getter(12-28)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
Getter(17-60)
src/main/java/com/sku/refit/domain/exchange/entity/ExchangeCategory.java (2)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
Getter(12-28)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
Getter(17-60)
🔇 Additional comments (15)
src/main/java/com/sku/refit/domain/post/dto/response/PostDetailResponse.java (2)
20-21: 카테고리 타입 변경 승인
PostCategoryenum 타입으로의 변경이 적절합니다. API 응답에서 타입 안전성이 확보되고 Swagger 문서에도 명확하게 표시됩니다.
41-42: isAuthor 필드 추가 승인작성자 본인 여부를 나타내는
isAuthor필드 추가는 클라이언트에서 수정/삭제 권한 UI를 표시하는 데 유용합니다.src/main/java/com/sku/refit/domain/post/entity/Post.java (2)
83-87: update() 메서드에 postCategory 누락 확인 필요
update()메서드가postCategory를 업데이트하지 않습니다. 의도적으로 카테고리 변경을 제한하는 것이라면 괜찮지만, 게시글 수정 시 카테고리도 변경할 수 있어야 한다면 파라미터 추가가 필요합니다.카테고리 변경이 필요한 경우:
- public void update(String title, String content, List<String> imageUrlList) { + public void update(String title, String content, List<String> imageUrlList, PostCategory postCategory) { this.title = title; this.content = content; this.imageUrlList = imageUrlList; + this.postCategory = postCategory; }
57-59: Enum 영속화 방식 승인
@Enumerated(EnumType.STRING)사용은 좋은 선택입니다.EnumType.ORDINAL보다 enum 값 순서 변경에 안전하며, DB에서 직접 조회 시 가독성도 높습니다.src/main/java/com/sku/refit/domain/post/entity/PostCategory.java (1)
10-22: 잘 구성된 PostCategory enumenum 설계가 깔끔합니다. Lombok을 활용한 보일러플레이트 제거, 한글 라벨 제공, Swagger 문서화가 적절하게 적용되어 있습니다.
src/main/java/com/sku/refit/domain/exchange/exception/ExchangeErrorCode.java (1)
15-24: LGTM!에러 코드 Enum이 프로젝트의 표준 패턴을 잘 따르고 있으며, 명확한 에러 메시지와 적절한 HTTP 상태 코드를 제공합니다.
src/main/java/com/sku/refit/domain/exchange/entity/ClothStatus.java (1)
13-22: LGTM!Enum 구조가 명확하며, 한국어 레이블을 위한
ko필드와 생성자 설정이 적절합니다.src/main/java/com/sku/refit/domain/exchange/entity/ExchangeCategory.java (1)
13-26: LGTM!교환 카테고리 Enum이 명확하게 정의되어 있으며, 한국어 레이블 지원이 적절합니다.
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostCardResponse.java (1)
12-28: 잘 구현되었습니다!간결하고 명확한 DTO 구조입니다. Swagger 문서화도 적절합니다.
src/main/java/com/sku/refit/domain/exchange/mapper/ExchangeMapper.java (1)
22-45: 매핑 로직이 잘 구현되었습니다.요청 데이터를 엔티티로 변환하는 로직이 명확하고 완전합니다.
src/main/java/com/sku/refit/domain/exchange/entity/ExchangeStatus.java (1)
10-22: 깔끔한 Enum 정의입니다.교환 상태를 명확하게 정의했으며, 한글 설명이 포함되어 있어 좋습니다.
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeControllerImpl.java (2)
47-52: 페이지네이션 검증이 잘 구현되었습니다.입력값 검증을 통해 잘못된 페이지 요청을 명확하게 처리하고 있습니다.
88-88: 응답 방식이 일관적입니다.
BaseResponse.success(null)은 CommentControllerImpl의 delete 메서드와 일관된 패턴입니다.src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java (2)
48-80: 게시글 생성 로직이 잘 구조화되었습니다.이미지 업로드, Enum 변환, 엔티티 매핑, 저장, 로깅까지 명확하게 구현되어 있습니다.
82-92: 위치 기반 페이지네이션 조회가 적절합니다.ExchangeStatus.BEFORE 필터링과 거리 기반 정렬을 통해 의도한 비즈니스 로직을 정확히 구현했습니다.
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java
Outdated
Show resolved
Hide resolved
src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
src/main/java/com/sku/refit/domain/exchange/repository/ExchangeRepository.java (1)
23-38: 이전 리뷰 코멘트 참조좌표 필드 인덱스 부재 및 거리 필터링 누락 문제는 이미 이전 리뷰에서 상세히 지적되었습니다. 해당 코멘트를 참고하여 개선을 진행해 주세요.
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java (1)
37-50: 이전 리뷰 코멘트 참조이미지 리스트 검증 누락 문제는 이전 리뷰에서 상세히 지적되었습니다.
imageList가 빈 리스트일 경우getFirst()호출 시 예외가 발생하는 문제를 해결해 주세요.
🧹 Nitpick comments (2)
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java (2)
52-62: 페이징 및 좌표 파라미터 검증 추가 권장현재
pageNum,pageSize,latitude,longitude파라미터에 대한 범위 검증이 없습니다. 다음 검증을 추가하면 더 견고한 API가 됩니다:
pageNum: 0 이상의 값pageSize: 1 이상, 적절한 최대값 이하 (예: 100)latitude: -90.0 ~ 90.0 범위longitude: -180.0 ~ 180.0 범위구현 예시:
@GetMapping @Operation(summary = "교환 게시글 목록(페이지) 조회 (위치 기반)") ResponseEntity<BaseResponse<PageResponse<ExchangePostCardResponse>>> getExchangePostsByLocation( @Parameter(description = "페이지 번호", example = "1") @RequestParam @Min(0) Integer pageNum, @Parameter(description = "페이지 크기", example = "4") @RequestParam @Min(1) @Max(100) Integer pageSize, @Parameter(description = "위도", example = "37.544018") @RequestParam(defaultValue = "37.544018") @Min(-90) @Max(90) Double latitude, @Parameter(description = "경도", example = "126.951592") @RequestParam(defaultValue = "126.951592") @Min(-180) @Max(180) Double longitude);
69-83: 이미지 리스트 선택적 제공 시 검증 추가 권장수정 시
imageList가 선택적(required=false)이지만, 제공되는 경우 빈 리스트는 허용하지 않아야 합니다. 서비스 레이어에서 다음 로직을 추가하는 것을 권장합니다:
imageList가 null이면: 기존 이미지 유지imageList가 제공되었으나 비어있으면: 400 에러 반환imageList가 제공되고 비어있지 않으면: 이미지 업데이트
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/controller/ExchangeControllerImpl.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/repository/ExchangeRepository.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java
- src/main/java/com/sku/refit/domain/exchange/controller/ExchangeControllerImpl.java
🔇 Additional comments (4)
src/main/java/com/sku/refit/domain/exchange/repository/ExchangeRepository.java (1)
21-21: LGTM!상태 기반 조회 메서드가 올바르게 구현되었습니다. Spring Data JPA의 네이밍 규칙을 잘 따르고 있으며, Optional을 반환하여 안전한 처리를 지원합니다.
src/main/java/com/sku/refit/domain/exchange/controller/ExchangeController.java (3)
33-35: LGTM!인터페이스 기반 컨트롤러 설계와 Swagger 문서화가 잘 적용되어 있습니다. RESTful 경로 규칙도 적절합니다.
64-67: LGTM!단일 게시글 조회 엔드포인트가 명확하고 간결하게 정의되어 있습니다.
85-88: LGTM!게시글 삭제 엔드포인트가 명확하게 정의되어 있습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/main/java/com/sku/refit/domain/post/mapper/PostMapper.java (1)
34-44:imageUrlList매핑이 여전히 누락되었습니다.이전 리뷰에서 지적된
PostDetailResponse의imageUrlList필드 매핑이 아직 처리되지 않았습니다. Line 43에commentIdList는 매핑되었지만imageUrlList는 누락되어 있습니다.다음 diff를 적용하여 imageUrlList를 매핑하세요:
.category(post.getPostCategory()) + .imageUrlList(post.getImageUrlList()) .commentIdList(post.getCommentList().stream().map(Comment::getId).toList())src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java (2)
135-137: 조회수가 중복으로 증가하는 버그Line 135와 137에서
post.increaseViews()가 두 번 호출되어 조회수가 의도한 것보다 2배로 증가합니다. 하나를 제거해야 합니다.다음 diff를 적용하여 중복 호출을 제거하세요:
post.increaseViews(); - - post.increaseViews();
168-178: 이미지 삭제 로직 중복Lines 168-170에서 기존 이미지를 삭제한 후, 172-174에서 새 이미지를 업로드하고, 176-178에서 동일한 기존 이미지를 다시 삭제하려고 시도합니다. 이미 삭제된 파일을 재삭제하려는 것은 불필요하며 오류를 발생시킬 수 있습니다.
다음 diff를 적용하여 중복 삭제 로직을 제거하세요:
try { for (String imageUrl : oldImageUrls) { s3Service.deleteFile(s3Service.extractKeyNameFromUrl(imageUrl)); } for (MultipartFile image : images) { newImageUrls.add(s3Service.uploadImage(PathName.POST, image).getImageUrl()); } - - for (String imageUrl : oldImageUrls) { - s3Service.deleteFile(s3Service.extractKeyNameFromUrl(imageUrl)); - }
🧹 Nitpick comments (1)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
22-59: 필드 검증 및 null 안전성 고려 권장DTO 필드들에 다음 사항들을 고려해보세요:
Boolean 래퍼 타입 (line 56):
isAuthor필드가Boolean래퍼 타입을 사용하고 있어 null 값이 반환될 수 있습니다. 소비 코드에서 NPE 위험이 있으므로, 기본값이 보장되어야 한다면 primitiveboolean타입 사용을 고려하세요.필수 필드 검증:
exchangePostId,nickname,title등 필수 필드에@NotNull또는@NotBlank같은 검증 어노테이션 추가를 고려하세요.좌표 범위 검증 (lines 50, 52):
spotLatitude와spotLongitude필드에@Min/@Max어노테이션으로 유효 범위(위도: -9090, 경도: -180180) 검증을 추가하면 데이터 무결성을 보장할 수 있습니다.예시 diff:
+ @NotNull @Schema(description = "교환 게시글 식별자", example = "1") private Long exchangePostId; + @NotBlank @Schema(description = "게시글 작성자", example = "김다입") private String nickname; + @Min(-90) @Max(90) @Schema(description = "교환 희망 스팟 위도", example = "37.544018") private Double spotLatitude; + @Min(-180) @Max(180) @Schema(description = "교환 희망 스팟 경도", example = "126.951592") private Double spotLongitude; - @Schema(description = "작성자 본인 여부", example = "true") - private Boolean isAuthor; + @Schema(description = "작성자 본인 여부", example = "true") + private boolean isAuthor;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java(1 hunks)src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java(1 hunks)src/main/java/com/sku/refit/domain/post/mapper/PostMapper.java(2 hunks)src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/main/java/com/sku/refit/domain/exchange/dto/request/ExchangePostRequest.java
- src/main/java/com/sku/refit/domain/exchange/service/ExchangeServiceImpl.java
🔇 Additional comments (5)
src/main/java/com/sku/refit/domain/exchange/dto/response/ExchangePostDetailResponse.java (1)
17-20: Schema 제목 수정 확인 완료이전 리뷰에서 지적된 Schema 제목 불일치 문제가 해결되었습니다.
@Schema의 title이 클래스명과 일치하게 "ExchangePostDetailResponse DTO"로 올바르게 설정되어 있습니다.src/main/java/com/sku/refit/domain/post/mapper/PostMapper.java (2)
20-29: LGTM! 카테고리 리팩토링이 올바르게 적용되었습니다.메서드 시그니처와 빌더 로직이 새로운
PostCategoryenum 구조에 맞게 정확하게 업데이트되었습니다.
41-41: 개선 확인: null 안전성이 추가되었습니다.이전 리뷰 코멘트에서 지적된 user 파라미터의 null 체크가 올바르게 적용되었습니다.
src/main/java/com/sku/refit/domain/post/service/PostServiceImpl.java (2)
59-66: 개선 확인: 카테고리 검증 로직이 추가되었습니다.이전 리뷰에서 요청된
PostCategory.valueOf()예외 처리가 올바르게 구현되었습니다. 잘못된 카테고리 입력 시 적절한 에러 응답을 반환합니다.
101-106: LGTM! 레포지토리 메서드 호출이 올바르게 업데이트되었습니다.카테고리 리팩토링에 맞춰
findByPostCategoryContaining계열 메서드로 정확하게 변경되었습니다.
| .createdAt(post.getCreatedAt()) | ||
| .nickname(user.getNickname()) | ||
| .categoryList(post.getCategoryList()) | ||
| .nickname(post.getUser().getNickname()) |
There was a problem hiding this comment.
post.getUser()가 null일 경우 NPE 가능성
post.getUser().getNickname() 호출 시 post.getUser()가 null이면 NullPointerException이 발생합니다. Post 엔티티의 user 관계가 항상 존재한다고 보장되지 않는다면 방어적 처리가 필요합니다.
다음 diff를 적용하여 안전하게 처리하세요:
- .nickname(post.getUser().getNickname())
+ .nickname(post.getUser() != null ? post.getUser().getNickname() : "알 수 없음")🤖 Prompt for AI Agents
In src/main/java/com/sku/refit/domain/post/mapper/PostMapper.java around line
40, calling post.getUser().getNickname() can throw NPE if post.getUser() is
null; update the mapping to handle a null user (e.g., use a null-check or
Optional.ofNullable(post.getUser()).map(User::getNickname).orElse(null)) and
pass null or a safe default into .nickname(...) so the mapper never dereferences
a null user.
✨ 새로운 기능
🛠 개발 상세
🧪 테스트 방법
🧩 추가 고려사항
🔗 관련 문서 / 이슈
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선사항
✏️ Tip: You can customize this high-level summary in your review settings.