Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -12,6 +12,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -22,32 +23,27 @@
public class UserPlaceController {

private final UserPlaceService userPlaceService;
private final JwtTokenProvider jwtProvider;


@PostMapping
@Operation(summary = "유저 장소 저장", description = "유저가 웨이블존을 장소로 저장합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "장소 저장 성공"),
@ApiResponse(responseCode = "400", description = "이미 저장한 장소입니다."),
@ApiResponse(responseCode = "404", description = "해당 유저 또는 웨이블존이 존재하지 않음")
@ApiResponse(responseCode = "404", description = "해당 유저 또는 웨이블존이 존재하지 않음"),
@ApiResponse(responseCode = "403", description = "권한이 없습니다.")
})
public CommonResponse<String> saveUserPlace(
@PathVariable Long userId,
@RequestBody @Valid UserPlaceRequestDto request,
@RequestHeader(value = "Authorization") String authorizationHeader
Copy link

@coderabbitai coderabbitai bot Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

사용하지 않는 Authorization 헤더 파라미터를 제거하세요.

SecurityContextHolder를 사용하게 되면서 Authorization 헤더를 직접 파싱하지 않으므로, 해당 파라미터는 더 이상 필요하지 않습니다. JWT 필터에서 이미 처리되었기 때문입니다.

    public CommonResponse<String> saveUserPlace(
            @PathVariable Long userId,
-            @RequestBody @Valid UserPlaceRequestDto request,
-            @RequestHeader(value = "Authorization") String authorizationHeader
+            @RequestBody @Valid UserPlaceRequestDto request
    ) {
    public CommonResponse<List<UserPlaceListResponseDto>> getUserPlaces(
-            @PathVariable Long userId,
-            @RequestHeader("Authorization") String authorizationHeader
+            @PathVariable Long userId
    ) {

Also applies to: 62-62

🤖 Prompt for AI Agents
In src/main/java/com/wayble/server/user/controller/UserPlaceController.java at
lines 39 and 62, remove the unused Authorization header parameter from the
method signatures. Since SecurityContextHolder and the JWT filter handle
authorization, you no longer need to parse the Authorization header manually, so
delete the @RequestHeader(value = "Authorization") String authorizationHeader
parameters from these lines.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code rabbit 말대로 Authorization 헤더는 빼는게 좋을 것 같네요!!!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KiSeungMin 알겠습니다! Authorization 헤더 파라미터와 관련된 잔여 코드(메서드 파라미터 및 불필요한 import) 제거 계획을 아래와 같이 잡았습니다.

• UserPlaceController에서 Authorization 헤더 파라미터 삭제 및 불필요한 import 정리

</plan_code_changes>


⏳ Preparing changes...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A summary of the changes CodeRabbit can apply:

  • Remove the Authorization header parameters from the saveUserPlace and getUserPlaces methods and delete the unused JwtTokenProvider import in the UserPlaceController.java file.
  • ✅ Create PR with these edits
  • 📌 Create commit in current branch
  • 📋 Get copyable edits

) {
String token = authorizationHeader.replace("Bearer ", "");
if (!jwtProvider.validateToken(token)) {
Long tokenUserId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!userId.equals(tokenUserId)) {
throw new ApplicationException(UserErrorCase.FORBIDDEN);
}
Long tokenUserId = jwtProvider.getUserId(token);

// Path variable과 request body의 userId 일치 여부 확인
if (!userId.equals(request.userId()) || !userId.equals(tokenUserId)) {
throw new ApplicationException(UserErrorCase.FORBIDDEN);
}
userPlaceService.saveUserPlace(request);
userPlaceService.saveUserPlace(userId, request); // userId 파라미터로 넘김
return CommonResponse.success("장소가 저장되었습니다.");
}

Expand All @@ -65,12 +61,7 @@ public CommonResponse<List<UserPlaceListResponseDto>> getUserPlaces(
@PathVariable Long userId,
@RequestHeader("Authorization") String authorizationHeader
) {
String token = authorizationHeader.replace("Bearer ", "");
if (!jwtProvider.validateToken(token)) {
throw new ApplicationException(UserErrorCase.FORBIDDEN);
}
Long tokenUserId = jwtProvider.getUserId(token);

Long tokenUserId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!userId.equals(tokenUserId)) {
throw new ApplicationException(UserErrorCase.FORBIDDEN);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import jakarta.validation.constraints.NotNull;

public record UserPlaceRequestDto(
@NotNull Long userId,
@NotNull Long waybleZoneId,
@NotNull String title
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ public class UserPlaceService {
private final UserPlaceWaybleZoneMappingRepository mappingRepository;

@Transactional
public void saveUserPlace(UserPlaceRequestDto request) {
public void saveUserPlace(Long userId, UserPlaceRequestDto request) {
// 유저 존재 확인
User user = userRepository.findById(request.userId())
User user = userRepository.findById(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());
boolean alreadySaved = mappingRepository.existsByUserPlace_User_IdAndWaybleZone_Id(userId, request.waybleZoneId());
if (alreadySaved) {
throw new ApplicationException(UserErrorCase.PLACE_ALREADY_SAVED);
}
Expand Down Expand Up @@ -72,8 +72,7 @@ public List<UserPlaceListResponseDto> getUserPlaces(Long userId) {
WaybleZone waybleZone = mapping.getWaybleZone();

// 웨이블존 대표 이미지 가져오기
String imageUrl = waybleZone.getWaybleZoneImageList().stream()
.findFirst().map(img -> img.getImageUrl()).orElse(null);
String imageUrl = waybleZone.getMainImageUrl();

return UserPlaceListResponseDto.builder()
.place_id(userPlace.getId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,13 @@ public class WaybleZone extends BaseEntity {

@OneToMany(mappedBy = "waybleZone", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserPlaceWaybleZoneMapping> userPlaceMappings = new ArrayList<>();

// 대표 이미지 필드 추가
@Column(name = "main_image_url")
private String mainImageUrl;
Comment on lines +61 to +63
Copy link

@coderabbitai coderabbitai bot Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

N+1 쿼리 문제 해결을 위한 좋은 접근입니다.

대표 이미지 URL을 별도 필드로 저장하여 스트림 연산을 통한 반복적인 이미지 조회를 방지하는 것은 성능 최적화에 효과적입니다. 데이터 정합성 유지를 위해 이미지 업데이트 시 mainImageUrl도 함께 업데이트되는 로직이 구현되어 있는지 확인해보세요.

이미지 업데이트 로직에서 mainImageUrl 동기화가 제대로 구현되어 있는지 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# Description: mainImageUrl 필드를 업데이트하는 로직이 있는지 확인

# WaybleZoneImage 관련 서비스에서 mainImageUrl 업데이트 로직 찾기
rg -A 10 "setMainImageUrl|mainImageUrl" --type java

# 이미지 업로드/수정 관련 코드에서 mainImageUrl 처리 확인
ast-grep --pattern 'setMainImageUrl($$$)'

Length of output: 724


mainImageUrl 동기화 로직 구현 검토 필요

현재 WaybleZone 엔티티에 setMainImageUrl 메서드만 정의되어 있고, 서비스 레이어나 이미지 업로드·수정 처리 과정에서 해당 메서드를 호출해 필드를 갱신하는 로직은 확인되지 않습니다.
데이터 정합성을 위해 이미지 등록/수정 시 mainImageUrl이 반드시 함께 업데이트되도록 아래 위치를 검토·보완해주세요.

  • 이미지 업로드/수정을 담당하는 서비스 (예: WaybleZoneImageService, WaybleZoneService 등)
  • 컨트롤러나 핸들러 레이어에서 이미지 변경 후 zone.setMainImageUrl(...) 호출 여부

예시:

// 이미지 저장 후 대표 이미지 URL 동기화
WaybleZone zone = waybleZoneRepository.findById(zoneId)
    .orElseThrow(...);
zone.setMainImageUrl(savedImage.getUrl());
waybleZoneRepository.save(zone);
🤖 Prompt for AI Agents
In src/main/java/com/wayble/server/wayblezone/entity/WaybleZone.java around
lines 61 to 63, the mainImageUrl field has a setter but no synchronization logic
to update it during image upload or modification. Review the image upload and
update processes in services like WaybleZoneImageService or WaybleZoneService
and ensure that after saving or updating an image, the corresponding WaybleZone
entity's mainImageUrl is updated by calling setMainImageUrl with the new image
URL, followed by saving the entity to maintain data consistency.

Copy link
Member Author

@seung-in-Yoo seung-in-Yoo Jul 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 사용할수도 있어서 일단 정의 해놓은거라 뺄수도 있고 로직을 더 추가할 수도 있어서 우선 로직을 더 추가하게 된다면 그때 직접 구현할게

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seung-in-Yoo 네, 충분히 이해됩니다! 현재 단계에서는 구조만 정의해두고 나중에 필요에 따라 구현하거나 제거하는 것이 합리적인 접근 방식입니다.

향후 mainImageUrl 동기화 로직을 구현하실 때는 이미지 변경 시점에 해당 필드도 함께 업데이트되도록 하시면 됩니다. 지금은 필드 정의만으로도 충분합니다.

  /|   /|  
 ( :v:  )
o_)   (   
 \_) (_/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 code rabbit이랑 대화도 가능하군요...! ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


// 혹시 필요할수도 있어서 추가해놓음
public void setMainImageUrl(String mainImageUrl) {
this.mainImageUrl = mainImageUrl;
}
}
Loading