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 @@ -53,49 +53,56 @@ public class DateSchedFacade {

@Transactional
public void createDateSchedule(User user, DateSchedCreateRequest request) {
// 1. Place 목록 준비 (조회 또는 생성)
List<Place> places = request.getPlaces().stream()
.map(placeDto -> getOrCreatePlace(user, placeDto))
.toList();

// 2. DateSchedule 생성
DateSchedule dateSchedule = DateSchedule.builder()
.name(request.getName())
.scheduleAt(LocalDate.parse(request.getScheduleAt()))
.user(user)
.build();
dateSchedService.save(dateSchedule);

// 3. Route 생성 (DateSchedule 참조)
Route route = Route.builder()
.name(request.getName())
.status(RouteStatus.WRITE)
.createdBy(user)
.routeType(request.getPlaces().size() == 1 ? RouteType.PLACE : RouteType.COURSE)
.routeType(places.size() == 1 ? RouteType.PLACE : RouteType.COURSE)
.dateSchedule(dateSchedule)
.build();
routeService.saveRoute(route);
Comment on lines +62 to 77
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

현재 로직은 데이터베이스 영속성 관점에서는 올바르게 동작하지만, 양방향 연관관계가 설정된 객체의 상태 일관성을 해칠 수 있는 잠재적인 위험이 있습니다.

dateScheduleroute 객체를 각각 생성하고 저장한 후에, route 객체에는 dateSchedule을 설정했지만, dateSchedule 객체의 route 필드는 설정되지 않은 상태로 남아있게 됩니다. 이로 인해 해당 트랜잭션 내에서 dateSchedule.getRoute()를 호출하면 null이 반환되어 예상치 못한 동작을 유발할 수 있습니다.

양방향 연관관계에서는 연관관계의 양쪽 모두를 설정해주는 것이 좋습니다. DateSchedule 엔티티에 Route를 설정하는 편의 메서드(예: setRoute(Route route))를 추가하고, route 생성 후 이를 호출하여 객체 그래프의 일관성을 유지하는 것을 고려해보세요.

Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The bidirectional relationship between DateSchedule and Route is not being properly maintained. While Route.dateSchedule is set (line 75), the corresponding DateSchedule.route field is never set. This breaks the bidirectional OneToOne mapping.

Since DateSchedule has @OneToOne(mappedBy = "dateSchedule"), the Route entity owns the relationship. However, for proper bidirectional consistency, you should either:

  1. Call route.updateDateSchedule(dateSchedule) after saving the route, OR
  2. Add a method to DateSchedule to set the route field and call it after creating the route

Note that DateSchedule has factory methods like createWithNewRoute() that properly manage this bidirectional relationship.

Suggested change
routeService.saveRoute(route);
routeService.saveRoute(route);
route.updateDateSchedule(dateSchedule);

Copilot uses AI. Check for mistakes.

for (DateSchedPlaceDto placeDto : request.getPlaces()) {
Long placeId = placeDto.getPlaceId();
Place place;
if (placeId != null) {
place = placeService.getPlaceById(placeId);
} else {
validatePlace(placeDto);

Place newPlace = Place.builder()
.name(request.getName())
.region1depth(placeDto.getRegion1depth())
.region2depth(placeDto.getRegion2depth())
.region3depth(placeDto.getRegion3depth())
.address(placeDto.getAddress())
.latitude(Double.valueOf(placeDto.getLatitude()))
.longitude(Double.valueOf(placeDto.getLongitude()))
.score(0L)
.user(user)
.status(PlaceStatus.WRITE)
.build();
place = placeService.save(newPlace);
}

// 4. RoutePlace 생성
for (Place place : places) {
RoutePlace routePlace = new RoutePlace(route, place);
routeService.saveRoutePlace(routePlace);
}
}
Comment on lines 55 to +84
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The refactored createDateSchedule method lacks test coverage. While there is a commented-out test in DateScheduleControllerTest, the method's business logic - particularly the new entity creation order, bidirectional relationship handling, and the extracted getOrCreatePlace helper method - should be covered by unit or integration tests to prevent regressions.

Copilot uses AI. Check for mistakes.

DateSchedule dateSchedule = DateSchedule.builder()
.name(request.getName())
.scheduleAt(LocalDate.parse(request.getScheduleAt()))
.route(route)
private Place getOrCreatePlace(User user, DateSchedPlaceDto placeDto) {
if (placeDto.getPlaceId() != null) {
return placeService.getPlaceById(placeDto.getPlaceId());
}

validatePlace(placeDto);

Place newPlace = Place.builder()
.name(placeDto.getName())
.region1depth(placeDto.getRegion1depth())
.region2depth(placeDto.getRegion2depth())
.region3depth(placeDto.getRegion3depth())
.address(placeDto.getAddress())
.latitude(Double.valueOf(placeDto.getLatitude()))
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Potential uncaught 'java.lang.NumberFormatException'.

Copilot uses AI. Check for mistakes.
.longitude(Double.valueOf(placeDto.getLongitude()))
Comment on lines +99 to +100
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

위도와 경도 값을 String에서 Double로 변환할 때 Double.valueOf()를 직접 사용하면, 유효하지 않은 숫자 형식의 문자열이 들어올 경우 NumberFormatException이 발생하여 500 에러로 이어질 수 있습니다. validatePlace 메서드에서 이 값들의 형식을 검증하는 로직이 없다면, DTO 레벨에서 @Pattern과 같은 유효성 검사 어노테이션을 추가하거나, 이 메서드 내에서 try-catch 블록으로 예외를 처리하여 적절한 비즈니스 예외(예: 400 Bad Request)를 던지는 것을 고려해보세요.

Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Potential uncaught 'java.lang.NumberFormatException'.

Copilot uses AI. Check for mistakes.
.score(0L)
.user(user)
.status(PlaceStatus.WRITE)
.build();

dateSchedService.save(dateSchedule);
return placeService.save(newPlace);
}

public List<DateSchedResponse> getDateSchedule(User user, String format, String date) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/server/domain/route/entity/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public void updateStatus(RouteStatus routeStatus) {
this.status = routeStatus;
}

public void updateDateSchedule(DateSchedule dateSchedule) {
this.dateSchedule = dateSchedule;
}
Comment on lines +79 to +81
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

양방향 연관관계를 설정할 때는 연관관계의 양쪽 모두를 업데이트하여 객체 상태의 일관성을 유지하는 것이 중요합니다. 현재 updateDateSchedule 메서드는 RoutedateSchedule 필드만 설정하고, 반대쪽인 DateScheduleroute 필드는 업데이트하지 않습니다.

일관성을 유지하기 위해 이 메서드 내에서 dateSchedule.setRoute(this)와 같이 반대쪽 연관관계도 함께 설정하는 로직을 추가하는 것을 권장합니다. 이를 위해서는 DateSchedule 엔티티에 route를 설정할 수 있는 setter나 편의 메서드가 필요할 수 있습니다.


/**
* 새로운 Route 생성
*/
Expand Down
Loading