From 2daf4d80deaecea9d23701754bf9284caecf686a Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:09:23 +0900 Subject: [PATCH 1/9] =?UTF-8?q?[feat]=20User=20=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=20=EC=97=B0=EA=B4=80=EA=B4=80=EA=B3=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/wayble/server/user/entity/User.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wayble/server/user/entity/User.java b/src/main/java/com/wayble/server/user/entity/User.java index 009f8280..d150505a 100644 --- a/src/main/java/com/wayble/server/user/entity/User.java +++ b/src/main/java/com/wayble/server/user/entity/User.java @@ -58,5 +58,6 @@ public class User extends BaseEntity { // TODO 프로필 이미지 관련 작업 필요 - // TODO 내가 저장한 장소 관련 작업 필요 + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List userPlaces = new ArrayList<>(); } From 42c2ed4a27f6a51552293ae68a86b3fae2a23f6b Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:11:30 +0900 Subject: [PATCH 2/9] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=A0=80=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20DTO=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wayble/server/user/dto/UserPlaceSaveRequestDto.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java diff --git a/src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java b/src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java new file mode 100644 index 00000000..df9f0bc9 --- /dev/null +++ b/src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java @@ -0,0 +1,9 @@ +package com.wayble.server.user.dto; + +import jakarta.validation.constraints.NotNull; + +public record UserPlaceSaveRequestDto( + @NotNull Long userId, + @NotNull Long waybleZoneId, + @NotNull String title +) {} From 002dd511aad95ad3941d31f685bb63aecd3c8811 Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:12:58 +0900 Subject: [PATCH 3/9] =?UTF-8?q?[feat]=20UserPlaceRepository=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/user/repository/UserPlaceRepository.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java diff --git a/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java b/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java new file mode 100644 index 00000000..8c14f4be --- /dev/null +++ b/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java @@ -0,0 +1,10 @@ +package com.wayble.server.user.repository; + +import com.wayble.server.user.entity.UserPlace; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserPlaceRepository extends JpaRepository { + Optional findByUser_IdAndTitle(Long userId, String title); +} From cfcfb901ef2d6541b5cdb98f4c16e7e029cfb36f Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:14:42 +0900 Subject: [PATCH 4/9] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20->=20=EC=9B=A8=EC=9D=B4=EB=B8=94=EC=A1=B4=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/UserPlaceWaybleZoneMappingRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java diff --git a/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java b/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java new file mode 100644 index 00000000..763c2c8a --- /dev/null +++ b/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java @@ -0,0 +1,8 @@ +package com.wayble.server.user.repository; + +import com.wayble.server.user.entity.UserPlaceWaybleZoneMapping; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserPlaceWaybleZoneMappingRepository extends JpaRepository { + boolean existsByUserPlace_User_IdAndWaybleZone_Id(Long userId, Long zoneId); +} From 03f4d082f7ded520f05b7c4a6f8314412775d76c Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:19:16 +0900 Subject: [PATCH 5/9] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wayble/server/user/exception/UserErrorCase.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java index 24b7bd15..c3825f42 100644 --- a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java +++ b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java @@ -8,7 +8,9 @@ @RequiredArgsConstructor public enum UserErrorCase implements ErrorCase { - USER_NOT_FOUND(400, 1001, "사용자를 찾을 수 없습니다."); + USER_NOT_FOUND(400, 1001, "사용자를 찾을 수 없습니다."), + WAYBLE_ZONE_NOT_FOUND(404, 1002, "해당 웨이블존을 찾을 수 없습니다."), + PLACE_ALREADY_SAVED(400, 1003, "이미 저장한 장소입니다."); private final Integer httpStatusCode; private final Integer errorCode; From dc37c306347513d1aecb8e7f7dce61bd478bcbe7 Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:24:25 +0900 Subject: [PATCH 6/9] =?UTF-8?q?[refactor]=20UserPlaceRequestDto=EB=A1=9C?= =?UTF-8?q?=20DTO=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{UserPlaceSaveRequestDto.java => UserPlaceRequestDto.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/com/wayble/server/user/dto/{UserPlaceSaveRequestDto.java => UserPlaceRequestDto.java} (82%) diff --git a/src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java b/src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java similarity index 82% rename from src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java rename to src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java index df9f0bc9..5285ab76 100644 --- a/src/main/java/com/wayble/server/user/dto/UserPlaceSaveRequestDto.java +++ b/src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java @@ -2,7 +2,7 @@ import jakarta.validation.constraints.NotNull; -public record UserPlaceSaveRequestDto( +public record UserPlaceRequestDto( @NotNull Long userId, @NotNull Long waybleZoneId, @NotNull String title From b21815d40d361fcf5aa3fbc85033f90f391fe5c8 Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 21:27:18 +0900 Subject: [PATCH 7/9] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=A0=80=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/user/service/UserPlaceService.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/service/UserPlaceService.java diff --git a/src/main/java/com/wayble/server/user/service/UserPlaceService.java b/src/main/java/com/wayble/server/user/service/UserPlaceService.java new file mode 100644 index 00000000..c1c0ca41 --- /dev/null +++ b/src/main/java/com/wayble/server/user/service/UserPlaceService.java @@ -0,0 +1,57 @@ +package com.wayble.server.user.service; + + +import com.wayble.server.common.exception.ApplicationException; +import com.wayble.server.user.dto.UserPlaceRequestDto; +import com.wayble.server.user.entity.User; +import com.wayble.server.user.entity.UserPlace; +import com.wayble.server.user.entity.UserPlaceWaybleZoneMapping; +import com.wayble.server.user.exception.UserErrorCase; +import com.wayble.server.user.repository.UserPlaceRepository; +import com.wayble.server.user.repository.UserPlaceWaybleZoneMappingRepository; +import com.wayble.server.user.repository.UserRepository; +import com.wayble.server.wayblezone.entity.WaybleZone; +import com.wayble.server.wayblezone.repository.WaybleZoneRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserPlaceService { + + private final UserRepository userRepository; + private final WaybleZoneRepository waybleZoneRepository; + private final UserPlaceRepository userPlaceRepository; + private final UserPlaceWaybleZoneMappingRepository mappingRepository; + + public void saveUserPlace(UserPlaceRequestDto request) { + // 유저 존재 확인 + User user = userRepository.findById(request.userId()) + .orElseThrow(() -> new ApplicationException(UserErrorCase.USER_NOT_FOUND)); + + // 웨이블존 존재 확인 + WaybleZone waybleZone = waybleZoneRepository.findById(request.waybleZoneId()) + .orElseThrow(() -> new ApplicationException(UserErrorCase.WAYBLE_ZONE_NOT_FOUND)); + + // 중복 저장 확인 + boolean alreadySaved = mappingRepository.existsByUserPlace_User_IdAndWaybleZone_Id(request.userId(), request.waybleZoneId()); + if (alreadySaved) { + throw new ApplicationException(UserErrorCase.PLACE_ALREADY_SAVED); + } + + // 저장 + UserPlace userPlace = userPlaceRepository.save( + UserPlace.builder() + .title(request.title()) + .user(user) + .build() + ); + + mappingRepository.save( + UserPlaceWaybleZoneMapping.builder() + .userPlace(userPlace) + .waybleZone(waybleZone) + .build() + ); + } +} \ No newline at end of file From 982c0fdee31c9ecac739c15e8cac765ab73acdc2 Mon Sep 17 00:00:00 2001 From: seungin Date: Sun, 13 Jul 2025 23:31:04 +0900 Subject: [PATCH 8/9] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EC=A0=80=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserPlaceController.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/com/wayble/server/user/controller/UserPlaceController.java diff --git a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java new file mode 100644 index 00000000..b4d1c3c1 --- /dev/null +++ b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java @@ -0,0 +1,36 @@ +package com.wayble.server.user.controller; + +import com.wayble.server.common.response.CommonResponse; +import com.wayble.server.user.dto.UserPlaceRequestDto; +import com.wayble.server.user.service.UserPlaceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/users/{userId}/places") +@RequiredArgsConstructor +public class UserPlaceController { + + private final UserPlaceService userPlaceService; + + @PostMapping + @Operation(summary = "유저 장소 저장", description = "유저가 웨이블존을 장소로 저장합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "장소 저장 성공"), + @ApiResponse(responseCode = "400", description = "이미 저장한 장소입니다."), + @ApiResponse(responseCode = "404", description = "해당 유저 또는 웨이블존이 존재하지 않음") + }) + public CommonResponse saveUserPlace( + @PathVariable Long userId, + @RequestBody UserPlaceRequestDto request, + + // 테스트를 위해 임시 허용 (로그인 구현되면 삭제) + @RequestHeader(value = "Authorization", required = false) String authorizationHeader + ) { + userPlaceService.saveUserPlace(request); + return CommonResponse.success("장소가 저장되었습니다."); + } +} From f3d3f2541827896b71373c8208c067e5dbcac469 Mon Sep 17 00:00:00 2001 From: seungin Date: Mon, 14 Jul 2025 00:20:41 +0900 Subject: [PATCH 9/9] =?UTF-8?q?[refactor]=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EC=A7=84=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/review/controller/ReviewController.java | 4 +++- .../server/user/controller/UserPlaceController.java | 12 ++++++++++-- .../wayble/server/user/exception/UserErrorCase.java | 5 +++-- .../wayble/server/user/service/UserPlaceService.java | 2 ++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/wayble/server/review/controller/ReviewController.java b/src/main/java/com/wayble/server/review/controller/ReviewController.java index 4c493c15..2fde91d2 100644 --- a/src/main/java/com/wayble/server/review/controller/ReviewController.java +++ b/src/main/java/com/wayble/server/review/controller/ReviewController.java @@ -38,7 +38,9 @@ public class ReviewController { public CommonResponse registerReview( @PathVariable Long waybleZoneId, @RequestBody @Valid ReviewRegisterDto dto, - @RequestHeader(value = "Authorization", required = false) String authorizationHeader // 테스트를 위해 임시로 토큰 없이도 접근하도록 설정 + + // TODO: 로그인 구현 후 Authorization 헤더 필수로 변경 필요 + @RequestHeader(value = "Authorization", required = false) String authorizationHeader ) { reviewService.registerReview(waybleZoneId, dto, authorizationHeader); return CommonResponse.success("리뷰가 등록되었습니다."); diff --git a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java index b4d1c3c1..0fb80e43 100644 --- a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java +++ b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java @@ -1,11 +1,14 @@ package com.wayble.server.user.controller; +import com.wayble.server.common.exception.ApplicationException; import com.wayble.server.common.response.CommonResponse; import com.wayble.server.user.dto.UserPlaceRequestDto; +import com.wayble.server.user.exception.UserErrorCase; import com.wayble.server.user.service.UserPlaceService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -25,11 +28,16 @@ public class UserPlaceController { }) public CommonResponse saveUserPlace( @PathVariable Long userId, - @RequestBody UserPlaceRequestDto request, + @RequestBody @Valid UserPlaceRequestDto request, - // 테스트를 위해 임시 허용 (로그인 구현되면 삭제) + // TODO: 로그인 구현 후 Authorization 헤더 필수로 변경 필요 @RequestHeader(value = "Authorization", required = false) String authorizationHeader ) { + // Path variable과 request body의 userId 일치 여부 확인 + if (!userId.equals(request.userId())) { + throw new ApplicationException(UserErrorCase.INVALID_USER_ID); + } + userPlaceService.saveUserPlace(request); return CommonResponse.success("장소가 저장되었습니다."); } diff --git a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java index c3825f42..0bb69bf3 100644 --- a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java +++ b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java @@ -8,9 +8,10 @@ @RequiredArgsConstructor public enum UserErrorCase implements ErrorCase { - USER_NOT_FOUND(400, 1001, "사용자를 찾을 수 없습니다."), + USER_NOT_FOUND(404, 1001, "사용자를 찾을 수 없습니다."), WAYBLE_ZONE_NOT_FOUND(404, 1002, "해당 웨이블존을 찾을 수 없습니다."), - PLACE_ALREADY_SAVED(400, 1003, "이미 저장한 장소입니다."); + PLACE_ALREADY_SAVED(400, 1003, "이미 저장한 장소입니다."), + INVALID_USER_ID(400, 1004, "요청 경로의 유저 ID와 바디의 유저 ID가 일치하지 않습니다."); private final Integer httpStatusCode; private final Integer errorCode; diff --git a/src/main/java/com/wayble/server/user/service/UserPlaceService.java b/src/main/java/com/wayble/server/user/service/UserPlaceService.java index c1c0ca41..55539022 100644 --- a/src/main/java/com/wayble/server/user/service/UserPlaceService.java +++ b/src/main/java/com/wayble/server/user/service/UserPlaceService.java @@ -12,6 +12,7 @@ import com.wayble.server.user.repository.UserRepository; import com.wayble.server.wayblezone.entity.WaybleZone; import com.wayble.server.wayblezone.repository.WaybleZoneRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -24,6 +25,7 @@ public class UserPlaceService { private final UserPlaceRepository userPlaceRepository; private final UserPlaceWaybleZoneMappingRepository mappingRepository; + @Transactional public void saveUserPlace(UserPlaceRequestDto request) { // 유저 존재 확인 User user = userRepository.findById(request.userId())