1. 목표
당일치기 API를 만들기
Request: 여행할 관광지 노드들 (첫 번째 노드가 출발 위치)
Response: 최적화 된 여행 경로를 계산하여 스케줄 정보를 DB에 저장하고, 해당 스케줄의 uuid를 반환
2. 개발
Service 로직의 정리와 구현하면서 생긴 이슈를 다루는데 포커스를 맞추고 있어,
DTO 의 각 필드와 Repository 의 설명은 제외하였다.
1) Controller 정의
우선 유저가 앱 서비스 내에서 가고자 하는 관광지를 선택해, 당일치기 일정 생성을 할 것이다.
그럼 클라이언트에서 유저가 선택한 관광지 정보들을 Request 요청으로 보내고,
서버는 이를 당일치기 여행 스케줄을 작성해 반환한다.
[DayScheduleController]
@Controller
@RequiredArgsConstructor
@RequestMapping("/api/day")
public class DayScheduleController {
private final DayTripService dayTripService;
@PostMapping("")
public ApiResponse<ScheduleResponseDto> addSchedule(@RequestBody @Valid NaverRequestDto naverRequestDto) {
return ApiResponse.ok(dayTripService.setSchedule(naverRequestDto));
}
}
2) Service 정의
Service 로직을 개발하기 전에, 미리 정리를 개발 플로우를 다음과 같이 정의했다.
NaverRequestDto는 당일치기 관광지 정보이다.
1. 출발지 노드에서 visited[False] 인 것 중에서 가장 가까운 노드를 찾는다. → NearNodeService()
2. 모든 노드를 방문할 때까지 계속한다.
3. ScheduleTour(관광지)로 Schedule(당일치기)을 생성한다.
4. Schedule - User 연관관계에 맞추어 파라미터에 set() 데이터를 채운다.
5. 벌크 쿼리로 Schedule, ScheduleTour DB에 반영한다.
1~2. ScheduleTours 정의
- 현재 위치에서 가장 가까운 위치로 이동하는 순서로 정의한다. (그리디 알고리즘)
Map<Long, Boolean> visited = new HashMap<>(); // 관광지 방문 여부 파악
List<Node> nodes = naverRequestDto.getKotrip().get(0).getNodes(); // 관광지 리스트
// 관광지 리스트 미방문 초기화
for (Node node : nodes) {
visited.put(node.getId(), false);
}
ArrayList<ScheduleTour> scheduleTours = new ArrayList<>();
Node start = nodes.get(0);
for (int i = 0; i < nodes.size(); ++i) {
visited.put(start.getId(), true); // 방문 처리
scheduleTours.add(new ScheduleTour(start.getId(), start.getName(), 0L, start.getImageUrl(), start.getLongitude(), start.getLatitude(), null)); // 노드 정보로 ScheduleTour를 만든다.
// 현재 노드에서 가까운 노드 찾기
Node nearNode = nearNodeService.getNearNode(start, nodes, visited);
start = nearNode;
}
3~4. Schedule 객체 정의
- User의 히스토리에 저장할 수 있게끔, 위의 scheduleTours(여행경로)를 포함하여 Schedule 객체를 정의한다.
- Schedule∙User 연관관계에 맞추어 User에 Schedule을 넣는다.
Q. 이 때, 당일치기이므로 List<Schedule>로 만들어야 할 필요가 있을까?
A. n박 m일 DTO 구조와 동일하게 사용하기 위해 List<>로 정의하였다.
// 당일치기이므로 Schedule이 1개밖에 없으나, 기존 양식과 맞추기 위해 ArrayList로 정의했다.
ArrayList<Schedule> schedules = new ArrayList<>();
// 유저 조회
Authentication authentication = getAuthentication();
User user = userRepository.findUserByNickname(authentication.getName()).orElseThrow(() -> new UsernameNotFoundException("유저를 찾을 수 없습니다."));
// 랜덤값
String uuid = ClassificationId.getID();
String uuid2 = ClassificationId.getID();
Schedule schedule = Schedule.toEntity(naverRequestDto.getTitle(), uuid, naverRequestDto.getAreaId(),
naverRequestDto.getKotrip().get(0).getDate(), user, scheduleTours, uuid2);
schedules.add(schedule);
5. Bulk 쿼리로 Schedule, ScheduleTour를 DB에 반영한다.
- '전체 여행 일정', '최적화된 여행 경로'을 DB에 넣어준다.
scheduleJdbcRepository.saveAll(schedules);
scheduleTourJdbcRepository.saveAll(scheduleTours);
[DayTripService]
@Service
@RequiredArgsConstructor
public class DayTripService {
private final NearNodeService nearNodeService;
private final UserRepository userRepository;
private final ScheduleJdbcRepository scheduleJdbcRepository;
private final ScheduleTourJdbcRepository scheduleTourJdbcRepository;
public ScheduleResponseDto setSchedule(NaverRequestDto naverRequestDto) {
/**
* NaverRequestDto는 당일치기 관광지 정보이다.
*
* 1. 출발지 노드에서 visited[False] 인 것 중에서 가장 가까운 노드를 찾는다. NearNodeService()
* 2. 모든 노드를 방문할 때까지 계속한다.
* 3. ScheduleTour(관광지)로 Schedule(당일치기)을 생성한다.
* 4. Schedule - User 연관관계에 맞추어 파라미터에 set() 데이터를 채운다.
* 5. 벌크 쿼리로 Schedule, ScheduleTour DB에 반영한다.
*/
/** 1 ~ 2.
* ScheduleTours 정의
* - 현재 위치에서 가장 가까운 위치로 이동하는 순서로 정의한다. (그리디 알고리즘)
*/
Map<Long, Boolean> visited = new HashMap<>();
List<Node> nodes = naverRequestDto.getKotrip().get(0).getNodes(); // 각 관광지들
// 관광지 리스트 미방문 초기화
for (Node node : nodes) {
visited.put(node.getId(), false);
}
ArrayList<ScheduleTour> scheduleTours = new ArrayList<>();
Node start = nodes.get(0);
for (int i = 0; i < nodes.size(); ++i) {
visited.put(start.getId(), true); // 방문 처리
scheduleTours.add(new ScheduleTour(start.getId(), start.getName(), 0L, start.getImageUrl(), start.getLongitude(), start.getLatitude(), null)); // 노드 정보로 ScheduleTour를 만든다.
/**
* 현재 노드에서 가까운 노드 찾기
*/
Node nearNode = nearNodeService.getNearNode(start, nodes, visited);
start = nearNode;
}
/** 3 ~ 4.
* Schedule 객체 정의
* - User의 히스토리에 저장할 수 있게끔, 위의 scheduleTours(여행경로)를 포함하여 Schedule 객체를 정의한다.
* - Schedule - User 연관관계에 맞추어 User에 Schedule을 넣는다.
*
* Q. 이 때, 당일치기이므로 List<Schedule>로 만들어야 할 필요가 있을까?
* A. n박 m일 DTO 구조와 동일하게 사용하기 위해 List<>로 정의하였다.
*/
// 당일치기이므로 Schedule이 1개밖에 없으나, 기존 양식과 맞추기 위해 ArrayList로 정의했다.
ArrayList<Schedule> schedules = new ArrayList<>();
// 유저 조회
Authentication authentication = getAuthentication();
User user = userRepository.findUserByNickname(authentication.getName()).orElseThrow(() -> new UsernameNotFoundException("유저를 찾을 수 없습니다."));
// 랜덤값
String uuid = ClassificationId.getID();
String uuid2 = ClassificationId.getID();
Schedule schedule = Schedule.toEntity(naverRequestDto.getTitle(), uuid, naverRequestDto.getAreaId(),
naverRequestDto.getKotrip().get(0).getDate(), user, scheduleTours, uuid2);
schedules.add(schedule);
/** 5.
* 벌크 쿼리로 Schedule, ScheduleTour를 DB에 반영한다.
*/
scheduleJdbcRepository.saveAll(schedules);
scheduleTourJdbcRepository.saveAll(scheduleTours);
return new ScheduleResponseDto(uuid);
}
private Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
'Spring > 끄적끄적' 카테고리의 다른 글
Spring Boot에서의 엑셀 다운로드 API (0) | 2024.07.20 |
---|---|
@JoinColumn(name =" ", referecedColumnName= " ") (0) | 2024.07.15 |
HttpURLConnection, RestTemplate, WebClient 비교 (0) | 2024.07.11 |
WebClient 네이버 지도 비동기 API 호출하기 (2) (1) | 2024.05.20 |