Conversation
|
""" Walkthrough북마크와 코스의 수정 기능이 추가되었습니다. 각 컨트롤러에 업데이트용 API 엔드포인트가 신설되었고, 서비스 계층에 실제 업데이트 로직이 구현되었습니다. 또한, 코스와 장소의 관계를 관리하는 리포지토리에 일괄 삭제 메서드가 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant BookmarkController
participant BookmarkService
participant BookmarkRepository
Client->>BookmarkController: POST /update/{bookmarkId} (multipart/form-data)
BookmarkController->>BookmarkService: updateBookmark(user, bookmarkId, req, image)
BookmarkService->>BookmarkRepository: findById(bookmarkId)
BookmarkService-->>BookmarkRepository: save(updatedBookmark)
BookmarkController-->>Client: HTTP 200 OK
sequenceDiagram
participant Client
participant CourseController
participant CourseService
participant CourseRepository
participant CoursePlaceRepository
participant PlaceRepository
Client->>CourseController: POST /update/{courseId} (multipart/form-data)
CourseController->>CourseService: updateCourse(user, courseId, req, image)
CourseService->>CourseRepository: findById(courseId)
CourseService->>CoursePlaceRepository: deleteAllByCourse(course)
CourseService->>PlaceRepository: findById(placeId) (for each place)
CourseService->>CoursePlaceRepository: saveAll(newCoursePlaces)
CourseController-->>Client: HTTP 200 OK
Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
PR 빌드 검증 결과: ✅ 빌드 성공빌드 검증이 완료되었습니다. |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
src/main/java/jombi/freemates/service/CourseService.java (2)
199-204: 이미지 제거 기능이 누락되었습니다.현재 로직은 새 이미지가 제공될 때만 imageUrl을 업데이트하지만, 사용자가 기존 이미지를 제거하고 싶은 경우를 처리하지 않습니다.
이미지 제거 기능을 추가하기 위해 다음과 같이 수정을 고려해보세요:
// 이미지 저장 - String imageUrl = null; if (image != null && !image.isEmpty()) { - imageUrl = storage.storeImage(image); - course.setImageUrl(imageUrl); + String imageUrl = storage.storeImage(image); + course.setImageUrl(imageUrl); + } else if (req.getRemoveImage() != null && req.getRemoveImage()) { + // 이미지 제거 요청이 있는 경우 + course.setImageUrl(null); }이를 위해서는
CourseRequest에removeImage필드를 추가해야 합니다.
233-234: EntityManager 처리 일관성 개선이 필요합니다.
createCourse메서드에서는entityManager.flush()와entityManager.clear()를 모두 호출하지만,updateCourse에서는flush()만 호출합니다. 일관성을 위해 동일한 패턴을 사용하는 것이 좋습니다.일관성을 위해 다음과 같이 수정하세요:
entityManager.flush(); + entityManager.clear();
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/main/java/jombi/freemates/controller/BookmarkController.java(1 hunks)src/main/java/jombi/freemates/controller/CourseController.java(1 hunks)src/main/java/jombi/freemates/repository/CoursePlaceRepository.java(1 hunks)src/main/java/jombi/freemates/service/BookmarkService.java(1 hunks)src/main/java/jombi/freemates/service/CourseService.java(1 hunks)
🔇 Additional comments (4)
src/main/java/jombi/freemates/repository/CoursePlaceRepository.java (1)
3-3: LGTM!Course 임포트 추가와
deleteAllByCourse메서드 선언이 Spring Data JPA 컨벤션을 올바르게 따르고 있습니다. 이 메서드는 CourseService의 업데이트 로직에서 기존 연관 관계를 삭제할 때 사용될 것으로 보입니다.Also applies to: 9-9
src/main/java/jombi/freemates/service/BookmarkService.java (1)
191-227: 소유자 검증 로직이 올바르게 구현되었습니다북마크 업데이트 메서드의 소유자 검증 로직과 필드 업데이트 처리가 적절하게 구현되어 있습니다. 트랜잭션 어노테이션도 올바르게 적용되었습니다.
src/main/java/jombi/freemates/service/CourseService.java (2)
194-197: 권한 검증 로직이 적절합니다.코스 소유자만 수정할 수 있도록 하는 보안 검증이 올바르게 구현되어 있습니다. UUID 비교를 통한 소유권 확인이 적절합니다.
230-231: CoursePlace 업데이트 전략이 적절합니다.기존 CoursePlace를 모두 삭제한 후 새로 생성하는 방식은 간단하고 데이터 일관성을 보장합니다. 대용량 데이터가 아닌 경우 성능상 문제없는 접근 방식입니다.
| // 파일이 있으면 저장 후 imageUrl 세팅, 없으면 imageUrl = null | ||
| String imageUrl = null; | ||
| if (image != null && !image.isEmpty()) { | ||
| imageUrl = storage.storeImage(image); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
이미지 처리 로직을 개선하세요
현재 구현에서는 새 이미지가 제공되지 않으면 imageUrl을 null로 설정합니다. 이는 기존 이미지를 덮어쓰게 되어 사용자가 다른 필드만 업데이트하고 싶을 때 의도하지 않게 이미지를 삭제할 수 있습니다.
다음과 같이 수정하여 기존 이미지를 보존하세요:
- // 파일이 있으면 저장 후 imageUrl 세팅, 없으면 imageUrl = null
- String imageUrl = null;
- if (image != null && !image.isEmpty()) {
- imageUrl = storage.storeImage(image);
- }
+ // 파일이 있으면 저장 후 imageUrl 세팅, 없으면 기존 imageUrl 유지
+ String imageUrl = bookmark.getImageUrl(); // 기존 이미지 URL 유지
+ if (image != null && !image.isEmpty()) {
+ imageUrl = storage.storeImage(image);
+ }그리고 imageUrl 설정 부분도 수정:
- bookmark.setImageUrl(imageUrl); // 파일이 없으면 null, 있으면 저장된 경로
+ bookmark.setImageUrl(imageUrl); // 파일이 없으면 기존 유지, 있으면 저장된 경로Also applies to: 223-223
🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/service/BookmarkService.java around lines 198
to 202 and line 223, the current image handling logic sets imageUrl to null if
no new image is provided, which unintentionally deletes existing images during
updates. Modify the code to preserve the existing imageUrl when no new image is
uploaded by only updating imageUrl if a new image is present; otherwise, retain
the current imageUrl value. Adjust the assignment logic accordingly to prevent
overwriting the existing image with null.
|
|
||
| @PostMapping(value ="/update/{bookmarkId}", | ||
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| @ResponseStatus(HttpStatus.CREATED) |
There was a problem hiding this comment.
HTTP 상태 코드 불일치를 수정하세요
@ResponseStatus(HttpStatus.CREATED)와 ResponseEntity.ok()가 충돌합니다. 업데이트 작업이므로 200 OK가 더 적절합니다.
다음과 같이 수정하세요:
- @ResponseStatus(HttpStatus.CREATED)
+ @ResponseStatus(HttpStatus.OK)또는 어노테이션을 제거하고 ResponseEntity만 사용하세요:
- @ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Void> updateBookmark(Also applies to: 272-272
🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/controller/BookmarkController.java at lines 255
and 272, there is a conflict between the @ResponseStatus(HttpStatus.CREATED)
annotation and the ResponseEntity.ok() return statement. Since these methods
perform update operations, the HTTP status should be 200 OK. Fix this by either
changing the annotation to @ResponseStatus(HttpStatus.OK) or removing the
@ResponseStatus annotation entirely and relying solely on ResponseEntity.ok() to
set the status.
| @PostMapping(value ="/update/{bookmarkId}", | ||
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| @ResponseStatus(HttpStatus.CREATED) | ||
| public ResponseEntity<Void> updateBookmark( | ||
| @AuthenticationPrincipal CustomUserDetails customUserDetails, | ||
| @PathVariable("bookmarkId") UUID bookmarkId, | ||
| @RequestParam String title, | ||
| @RequestParam String description, | ||
| @RequestParam PinColor pinColor, | ||
| @RequestParam Visibility visibility, | ||
| @RequestParam(value = "image", required = false) MultipartFile image | ||
| ) { | ||
| BookmarkRequest req = BookmarkRequest.builder() | ||
| .title(title) | ||
| .description(description) | ||
| .pinColor(pinColor) | ||
| .visibility(visibility) | ||
| .build(); | ||
| bookmarkService.updateBookmark(customUserDetails, bookmarkId, req, image); | ||
| return ResponseEntity.ok().build(); | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
API 문서화를 추가하세요
다른 엔드포인트들과 일관성을 위해 @Operation과 @ApiChangeLogs 어노테이션을 추가해야 합니다.
다음과 같은 문서화를 추가하세요:
+ @ApiChangeLogs({
+ @ApiChangeLog(
+ date = "2025-01-XX",
+ author = Author.LEEDAYE,
+ issueNumber = 124,
+ description = "북마크 업데이트 기능"
+ )
+ })
+ @Operation(
+ summary = "북마크 업데이트",
+ description = """
+ ## 인증(JWT): **필요**
+
+ ## 요청 파라미터 (multipart/form-data)
+ - **`title`**: 즐겨찾기 제목
+ - **`description`**: 즐겨찾기 설명
+ - **`pinColor`**: 핀 색깔 (ENUM, 6가지 중 하나)
+ - **`visibility`**: 공개 여부 (ENUM: `PUBLIC` 또는 `PRIVATE`)
+ - **`image`**: 이미지 파일 (MultipartFile, 선택사항)
+
+ ## 에러코드
+ - `UNAUTHORIZED (401)`: 북마크 소유자가 아닙니다.
+ - `BOOKMARK_NOT_FOUND (404)`: 존재하지 않는 북마크입니다.
+ """
+ )
@PostMapping(value ="/update/{bookmarkId}",🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/controller/BookmarkController.java around lines
253 to 274, the updateBookmark method lacks API documentation annotations. To
fix this, add the @Operation annotation describing the endpoint's purpose and
behavior, and include the @ApiChangeLogs annotation to maintain consistency with
other endpoints. Ensure the annotations provide clear summary, description, and
change log details as per the project's documentation standards.
| */ | ||
| @PostMapping(value ="/update/{courseId}", | ||
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| @ResponseStatus(HttpStatus.CREATED) |
There was a problem hiding this comment.
HTTP 상태 코드 불일치를 수정하세요
@ResponseStatus(HttpStatus.CREATED)와 ResponseEntity.ok()가 충돌합니다. 업데이트 작업이므로 200 OK가 더 적절합니다.
다음과 같이 수정하세요:
- @ResponseStatus(HttpStatus.CREATED)
+ @ResponseStatus(HttpStatus.OK)또는 어노테이션을 제거하고 ResponseEntity만 사용하세요:
- @ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Void> updateCourse(Also applies to: 248-248
🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/controller/CourseController.java at lines 227
and 248, there is a conflict between the @ResponseStatus(HttpStatus.CREATED)
annotation and the use of ResponseEntity.ok() in update methods. Since these are
update operations, change the HTTP status to 200 OK by either removing the
@ResponseStatus annotation or replacing it with @ResponseStatus(HttpStatus.OK),
ensuring consistency with ResponseEntity.ok() responses.
| /** | ||
| * 코스 수정 | ||
| */ | ||
| @PostMapping(value ="/update/{courseId}", | ||
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | ||
| @ResponseStatus(HttpStatus.CREATED) | ||
| public ResponseEntity<Void> updateCourse( | ||
| @AuthenticationPrincipal CustomUserDetails user, | ||
| @PathVariable("courseId") UUID courseId, | ||
| @RequestParam("title") String title, | ||
| @RequestParam("description") String description, | ||
| @RequestParam("freeTime") Integer freeTime, | ||
| @RequestParam("visibility") Visibility visibility, | ||
| // placeIds를 여러 개 RquestParam으로 받을 수도 있고, 한 문자열(콤마 구분)로 받을 수도 있음 | ||
| @RequestParam("placeIds") List<UUID> placeIds, | ||
| @RequestParam(value = "image", required = false) MultipartFile image | ||
| ) { | ||
| CourseRequest req = CourseRequest.builder() | ||
| .title(title) | ||
| .description(description) | ||
| .freeTime(freeTime) | ||
| .visibility(visibility) | ||
| .placeIds(placeIds) | ||
| .build(); | ||
|
|
||
| courseService.updateCourse(user, courseId, req, image); | ||
| return ResponseEntity.ok().build(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
API 문서화를 추가하세요
다른 엔드포인트들과 일관성을 위해 @Operation과 @ApiChangeLogs 어노테이션을 추가해야 합니다.
다음과 같은 문서화를 추가하세요:
+ @ApiChangeLogs({
+ @ApiChangeLog(
+ date = "2025-01-XX",
+ author = Author.LEEDAYE,
+ issueNumber = 124,
+ description = "코스 업데이트 기능"
+ )
+ })
+ @Operation(
+ summary = "코스 업데이트",
+ description = """
+ ## 인증(JWT): **필요**
+
+ ## 요청 파라미터 (multipart/form-data)
+ - `title`: 코스 제목
+ - `description`: 코스 설명
+ - `freeTime`: 코스 예상 소요 시간 (분 단위)
+ - `visibility`: 공개 여부 (ENUM: `PUBLIC` 또는 `PRIVATE`)
+ - `placeIds`: 포함할 장소 ID 목록 (UUID)
+ - `image`: 코스 대표 이미지 파일 (MultipartFile, 선택사항)
+
+ ## 에러코드
+ - `UNAUTHORIZED (401)`: 코스 소유자가 아닙니다.
+ - `COURSE_NOT_FOUND (404)`: 존재하지 않는 코스입니다.
+ - `PLACE_NOT_FOUND (404)`: 존재하지 않는 장소가 포함되었습니다.
+ """
+ )
/**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * 코스 수정 | |
| */ | |
| @PostMapping(value ="/update/{courseId}", | |
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | |
| @ResponseStatus(HttpStatus.CREATED) | |
| public ResponseEntity<Void> updateCourse( | |
| @AuthenticationPrincipal CustomUserDetails user, | |
| @PathVariable("courseId") UUID courseId, | |
| @RequestParam("title") String title, | |
| @RequestParam("description") String description, | |
| @RequestParam("freeTime") Integer freeTime, | |
| @RequestParam("visibility") Visibility visibility, | |
| // placeIds를 여러 개 RquestParam으로 받을 수도 있고, 한 문자열(콤마 구분)로 받을 수도 있음 | |
| @RequestParam("placeIds") List<UUID> placeIds, | |
| @RequestParam(value = "image", required = false) MultipartFile image | |
| ) { | |
| CourseRequest req = CourseRequest.builder() | |
| .title(title) | |
| .description(description) | |
| .freeTime(freeTime) | |
| .visibility(visibility) | |
| .placeIds(placeIds) | |
| .build(); | |
| courseService.updateCourse(user, courseId, req, image); | |
| return ResponseEntity.ok().build(); | |
| } | |
| @ApiChangeLogs({ | |
| @ApiChangeLog( | |
| date = "2025-01-XX", | |
| author = Author.LEEDAYE, | |
| issueNumber = 124, | |
| description = "코스 업데이트 기능" | |
| ) | |
| }) | |
| @Operation( | |
| summary = "코스 업데이트", | |
| description = """ | |
| ## 인증(JWT): **필요** | |
| ## 요청 파라미터 (multipart/form-data) | |
| - `title`: 코스 제목 | |
| - `description`: 코스 설명 | |
| - `freeTime`: 코스 예상 소요 시간 (분 단위) | |
| - `visibility`: 공개 여부 (ENUM: `PUBLIC` 또는 `PRIVATE`) | |
| - `placeIds`: 포함할 장소 ID 목록 (UUID) | |
| - `image`: 코스 대표 이미지 파일 (MultipartFile, 선택사항) | |
| ## 에러코드 | |
| - `UNAUTHORIZED (401)`: 코스 소유자가 아닙니다. | |
| - `COURSE_NOT_FOUND (404)`: 존재하지 않는 코스입니다. | |
| - `PLACE_NOT_FOUND (404)`: 존재하지 않는 장소가 포함되었습니다. | |
| """ | |
| ) | |
| /** | |
| * 코스 수정 | |
| */ | |
| @PostMapping(value ="/update/{courseId}", | |
| consumes = MediaType.MULTIPART_FORM_DATA_VALUE) | |
| @ResponseStatus(HttpStatus.CREATED) | |
| public ResponseEntity<Void> updateCourse( | |
| @AuthenticationPrincipal CustomUserDetails user, | |
| @PathVariable("courseId") UUID courseId, | |
| @RequestParam("title") String title, | |
| @RequestParam("description") String description, | |
| @RequestParam("freeTime") Integer freeTime, | |
| @RequestParam("visibility") Visibility visibility, | |
| // placeIds를 여러 개 RquestParam으로 받을 수도 있고, 한 문자열(콤마 구분)로 받을 수도 있음 | |
| @RequestParam("placeIds") List<UUID> placeIds, | |
| @RequestParam(value = "image", required = false) MultipartFile image | |
| ) { | |
| CourseRequest req = CourseRequest.builder() | |
| .title(title) | |
| .description(description) | |
| .freeTime(freeTime) | |
| .visibility(visibility) | |
| .placeIds(placeIds) | |
| .build(); | |
| courseService.updateCourse(user, courseId, req, image); | |
| return ResponseEntity.ok().build(); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/jombi/freemates/controller/CourseController.java around lines
222 to 249, the updateCourse method lacks API documentation annotations. To
maintain consistency with other endpoints, add the @Operation annotation
describing the endpoint's purpose and behavior, and include the @ApiChangeLogs
annotation to document change history. Place these annotations above the method
signature to enhance API documentation and traceability.
PR 빌드 검증 결과: ✅ 빌드 성공빌드 검증이 완료되었습니다. |
#124
Summary by CodeRabbit