Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ public class BookmarkDto {
@Schema(description = "장소 정보")
private PlaceDto place;

@Schema(description = "폴더명", example = "가고 싶은 곳")
private String folder;

@Schema(description = "메모", example = "친구랑 같이 가기")
private String memo;

Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
@Schema(description = "북마크 수정 요청")
public class UpdateBookmarkRequest {

@Schema(description = "폴더명 (null이면 변경하지 않음)", example = "가고 싶은 곳")
private String folder;

@Schema(description = "메모 (null이면 변경하지 않음)", example = "친구랑 같이 가기")
private String memo;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public interface FolderPlaceRepository extends JpaRepository<FolderPlace, UUID>

Optional<FolderPlace> findByFolderAndPlaceAndDeletedAtIsNull(Folder folder, Place place);

Optional<FolderPlace> findByFolderAndPlace(Folder folder, Place place);

boolean existsByFolderAndPlaceAndDeletedAtIsNull(Folder folder, Place place);

List<FolderPlace> findByFolderAndDeletedAtIsNull(Folder folder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<FolderPlace> 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);
Expand Down Expand Up @@ -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<FolderPlace> 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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public ResponseEntity<GetFolderPlacesResponse> getFolderPlaces(
public ResponseEntity<AddFolderPlaceResponse> 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());
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<!-- 수정하지마세요 자동으로 동기화 됩니다 -->
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
## 최신 버전 : v0.1.28 (2026-02-23)
## 최신 버전 : v0.1.29 (2026-02-23)

[전체 버전 기록 보기](CHANGELOG.md)

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ bootJar {

allprojects {
group = 'kr.suhsaechan.mapsy'
version = '0.1.29'
version = '0.1.30'

repositories {
mavenCentral()
Expand Down
6 changes: 3 additions & 3 deletions version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down