diff --git a/MS-Common/src/main/java/kr/suhsaechan/mapsy/common/exception/constant/ErrorCode.java b/MS-Common/src/main/java/kr/suhsaechan/mapsy/common/exception/constant/ErrorCode.java index bf95086..044746a 100644 --- a/MS-Common/src/main/java/kr/suhsaechan/mapsy/common/exception/constant/ErrorCode.java +++ b/MS-Common/src/main/java/kr/suhsaechan/mapsy/common/exception/constant/ErrorCode.java @@ -115,7 +115,7 @@ public enum ErrorCode { CANNOT_DELETE_SAVED_PLACE(HttpStatus.BAD_REQUEST, "임시 저장된 장소만 삭제할 수 있습니다."), CANNOT_UPDATE_UNSAVED_PLACE(HttpStatus.BAD_REQUEST, "저장된 장소만 수정할 수 있습니다."), - + INVALID_RATING(HttpStatus.BAD_REQUEST, "별점은 1-5 사이의 값이어야 합니다."), // Folder diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/BookmarkDto.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/BookmarkDto.java index b05cd0a..8d36ea9 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/BookmarkDto.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/BookmarkDto.java @@ -22,9 +22,6 @@ public class BookmarkDto { @Schema(description = "장소 정보") private PlaceDto place; - @Schema(description = "폴더명", example = "가고 싶은 곳") - private String folder; - @Schema(description = "메모", example = "친구랑 같이 가기") private String memo; @@ -48,7 +45,6 @@ public static BookmarkDto from(MemberPlace memberPlace) { return BookmarkDto.builder() .memberPlaceId(memberPlace.getId()) .place(PlaceDto.from(memberPlace.getPlace())) - .folder(memberPlace.getFolder()) .memo(memberPlace.getMemo()) .rating(memberPlace.getRating()) .visited(memberPlace.getVisited()) diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/UpdateBookmarkRequest.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/UpdateBookmarkRequest.java index 59d0ae9..abe2b10 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/UpdateBookmarkRequest.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/dto/UpdateBookmarkRequest.java @@ -14,9 +14,6 @@ @Schema(description = "북마크 수정 요청") public class UpdateBookmarkRequest { - @Schema(description = "폴더명 (null이면 변경하지 않음)", example = "가고 싶은 곳") - private String folder; - @Schema(description = "메모 (null이면 변경하지 않음)", example = "친구랑 같이 가기") private String memo; diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/entity/Folder.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/entity/Folder.java index e33bacf..87b1df8 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/entity/Folder.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/entity/Folder.java @@ -19,12 +19,10 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; @Entity @Builder @Getter -@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Folder extends SoftDeletableBaseEntity { @@ -56,6 +54,14 @@ public class Folder extends SoftDeletableBaseEntity { @Builder.Default private Boolean isDefault = false; + public void updateName(String name) { + this.name = name; + } + + public void updateVisibility(FolderVisibility visibility) { + this.visibility = visibility; + } + @PrePersist protected void onCreate() { if (name == null) { diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/repository/FolderPlaceRepository.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/repository/FolderPlaceRepository.java index 2c599e4..6a39f67 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/repository/FolderPlaceRepository.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/repository/FolderPlaceRepository.java @@ -23,6 +23,8 @@ public interface FolderPlaceRepository extends JpaRepository Optional findByFolderAndPlaceAndDeletedAtIsNull(Folder folder, Place place); + Optional findByFolderAndPlace(Folder folder, Place place); + boolean existsByFolderAndPlaceAndDeletedAtIsNull(Folder folder, Place place); List findByFolderAndDeletedAtIsNull(Folder folder); diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/FolderService.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/FolderService.java index 1cca629..afe409e 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/FolderService.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/FolderService.java @@ -22,6 +22,7 @@ import kr.suhsaechan.mapsy.place.repository.FolderRepository; import kr.suhsaechan.mapsy.place.repository.PlaceRepository; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; @@ -84,10 +85,10 @@ public UpdateFolderResponse updateFolder(Member member, UUID folderId, UpdateFol Folder folder = getFolderWithOwnerValidation(folderId, member); if (request.getName() != null) { - folder.setName(request.getName()); + folder.updateName(request.getName()); } if (request.getVisibility() != null) { - folder.setVisibility(request.getVisibility()); + folder.updateVisibility(request.getVisibility()); } Folder savedFolder = folderRepository.save(folder); @@ -151,9 +152,20 @@ public AddFolderPlaceResponse addPlaceToFolder(Member member, UUID folderId, Add Place place = placeRepository.findById(request.getPlaceId()) .orElseThrow(() -> new CustomException(ErrorCode.PLACE_NOT_FOUND)); - // 중복 체크 - if (folderPlaceRepository.existsByFolderAndPlaceAndDeletedAtIsNull(folder, place)) { - throw new CustomException(ErrorCode.FOLDER_PLACE_ALREADY_EXISTS); + // 기존 레코드 조회 (Soft Delete 포함) + Optional existingFolderPlace = folderPlaceRepository.findByFolderAndPlace(folder, place); + + if (existingFolderPlace.isPresent()) { + FolderPlace fp = existingFolderPlace.get(); + if (fp.isActive()) { + // 이미 활성 상태면 중복 에러 + throw new CustomException(ErrorCode.FOLDER_PLACE_ALREADY_EXISTS); + } + // Soft Delete된 레코드 복원 + fp.restore(); + FolderPlace restoredFolderPlace = folderPlaceRepository.save(fp); + log.info("Place restored to folder: folderPlaceId={}", restoredFolderPlace.getId()); + return AddFolderPlaceResponse.from(restoredFolderPlace); } int maxPosition = folderPlaceRepository.findMaxPositionByFolder(folder); @@ -211,9 +223,19 @@ public void addPlaceToDefaultFolder(Member member, Place place) { Folder defaultFolder = folderRepository.findByOwnerAndIsDefaultTrueAndDeletedAtIsNull(member) .orElseGet(() -> createDefaultFolder(member)); - // 이미 기본 폴더에 있으면 스킵 - if (folderPlaceRepository.existsByFolderAndPlaceAndDeletedAtIsNull(defaultFolder, place)) { - log.debug("Place already in default folder: placeId={}", place.getId()); + // 기존 레코드 조회 (Soft Delete 포함) + Optional existingFolderPlace = folderPlaceRepository.findByFolderAndPlace(defaultFolder, place); + + if (existingFolderPlace.isPresent()) { + FolderPlace fp = existingFolderPlace.get(); + if (fp.isActive()) { + log.debug("Place already in default folder: placeId={}", place.getId()); + return; + } + // Soft Delete된 레코드 복원 + fp.restore(); + folderPlaceRepository.save(fp); + log.info("Place restored to default folder: placeId={}", place.getId()); return; } diff --git a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/MemberPlaceService.java b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/MemberPlaceService.java index da63269..3d9382d 100644 --- a/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/MemberPlaceService.java +++ b/MS-Place/src/main/java/kr/suhsaechan/mapsy/place/service/MemberPlaceService.java @@ -266,9 +266,6 @@ public BookmarkDto updateBookmark(UUID memberId, UUID memberPlaceId, UpdateBookm } // 3. 필드별 수정 (null이 아닌 경우만) - if (request.getFolder() != null) { - memberPlace.updateFolder(request.getFolder()); - } if (request.getMemo() != null) { memberPlace.updateMemo(request.getMemo()); } diff --git a/MS-Web/src/main/java/kr/suhsaechan/mapsy/web/controller/FolderController.java b/MS-Web/src/main/java/kr/suhsaechan/mapsy/web/controller/FolderController.java index 0cfea9b..2a0e6c9 100644 --- a/MS-Web/src/main/java/kr/suhsaechan/mapsy/web/controller/FolderController.java +++ b/MS-Web/src/main/java/kr/suhsaechan/mapsy/web/controller/FolderController.java @@ -96,7 +96,7 @@ public ResponseEntity getFolderPlaces( public ResponseEntity addPlaceToFolder( @AuthenticationPrincipal CustomUserDetails userDetails, @PathVariable UUID folderId, - @RequestBody AddFolderPlaceRequest request + @Valid @RequestBody AddFolderPlaceRequest request ) { log.info("Add place to folder request from member: {}, folderId: {}, placeId: {}", userDetails.getMemberId(), folderId, request.getPlaceId()); diff --git a/README.md b/README.md index c331668..e46e7a1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -## 최신 버전 : v0.1.28 (2026-02-23) +## 최신 버전 : v0.1.29 (2026-02-23) [전체 버전 기록 보기](CHANGELOG.md) diff --git a/build.gradle b/build.gradle index b23d6d7..fd00da8 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ bootJar { allprojects { group = 'kr.suhsaechan.mapsy' - version = '0.1.29' + version = '0.1.30' repositories { mavenCentral() diff --git a/version.yml b/version.yml index 0e0b2b5..5fd85e4 100644 --- a/version.yml +++ b/version.yml @@ -33,11 +33,11 @@ # - project_type은 최초 설정 후 변경하지 마세요 # - 버전은 항상 높은 버전으로 자동 동기화됩니다 # =================================================================== -version: "0.1.29" -version_code: 33 # app build number +version: "0.1.30" +version_code: 34 # app build number project_type: "spring" # spring, flutter, next, react, react-native, react-native-expo, node, python, basic metadata: - last_updated: "2026-02-23 05:22:52" + last_updated: "2026-02-23 05:52:24" last_updated_by: "Cassiiopeia" default_branch: "main" integrated_from: "SUH-DEVOPS-TEMPLATE"