Skip to content

Commit 8b146ea

Browse files
길찾기 로직 리팩토링 & 가게 정보를 가지고 오는 로직 문제 수정 (#14)
# 변경점 👍 * 비동기로 경로와 가게 정보를 가지고 오는 API에서 가게 정보를 가지고 오지 못하는 문제가 발생하여 이를 리팩토링하였습니다. # 버그 해결 💊 * 가게 정보를 가지고 오지 못하는 문제의 원인을 분석하다가 Controller 단에서 비동기 자료형인 Mono로 반환하지 않아서 발생하는 문제임을 인지하였습니다. * ImageURL을 가져오는 부분에서 DB에서 해당 장소에 대한 Image를 가져와서 이 Image들이 존재하지 않으면 API를 가져오는 방식으로 했는데, 비동기방식에서는 이 방식을 같이 섞어쓸 수 없다는 에러가 발생하였습니다. # 리팩토링 💧 * RouteController에서 응답형인 ResponseEntity를 Mono로 감싸 비동기로 응답값을 반환하는 로직이 제대로 동작하게끔 만들었습니다. * RouteController에서 공통 작성된 부분을 내부 함수로 넣고, slow, findOut, right 세 가지 길에 대하여 이를 참조하도록 리팩토링하였습니다. * 가게에 대한 ImageURL이 DB에 있는지 확인하기 위한 로직을 DB에서 가져와 세는 방식이 아니라, 비동기에 걸맞게 존재 여부만 빠르게 확인하고 없다면 네이버 API를 가져오는 방식으로 진행하도록 수정하였습니다. # 테스트 💻 Postman으로 작동을 Black box 테스트(입력과 그에 따른 출력을 살펴보는 것 위주) 방식으로 검증하였습니다. # 스크린샷 🖼 <img width="2108" height="1512" alt="화면 캡처 2026-06-03 154956" src="https://github.com/user-attachments/assets/84bd01a8-2e34-4ab7-9465-243a00a3dfb8" /> # 비고 ✏ 전달되는 JSON에 대한 내용은 Notion API 명세에 작성해 놓겠습니다! --------- Co-authored-by: Seogyeong Yun <gsm09157@naver.com>
1 parent d7155c3 commit 8b146ea

12 files changed

Lines changed: 300974 additions & 300937 deletions

File tree

backend/.idea/compiler.xml

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/.idea/gradle.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/src/main/java/_team/onmyway/controller/RouteController.java

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import reactor.core.publisher.Mono;
1919

2020
import java.util.List;
21+
import java.util.function.Function;
2122

2223
@RestController
2324
@RequestMapping("/route")
@@ -28,47 +29,34 @@ public class RouteController {
2829
private final ObjectMapper objectMapper;
2930

3031
@PostMapping("/findOut")
31-
public ResponseEntity<?> getFindOutRoute(@RequestBody List<PositionDTO> positions) {
32-
if (positions.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
33-
PositionDTO start = positions.get(0);
34-
35-
RouteResponseDTO routing = routeService.findOutRoute(positions).block();
36-
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());
37-
38-
ObjectNode response = objectMapper.createObjectNode();
39-
response.set("route", objectMapper.valueToTree(routing));
40-
response.set("recommendations", objectMapper.valueToTree(recommendations));
41-
42-
return new ResponseEntity<>(response, HttpStatus.OK);
32+
public Mono<ResponseEntity<?>> getFindOutRoute(@RequestBody List<PositionDTO> positions) {
33+
return processRouteAndRecommend(positions, routeService::findOutRoute);
4334
}
4435

4536
@PostMapping("/right")
46-
public ResponseEntity<?> getRightRoute(@RequestBody List<PositionDTO> positions) {
47-
if (positions.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
48-
PositionDTO start = positions.get(0);
49-
50-
RouteResponseDTO routing = routeService.rightRoute(positions).block();
51-
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());
52-
53-
ObjectNode response = objectMapper.createObjectNode();
54-
response.set("route", objectMapper.valueToTree(routing));
55-
response.set("recommendations", objectMapper.valueToTree(recommendations));
56-
57-
return new ResponseEntity<>(response, HttpStatus.OK);
37+
public Mono<ResponseEntity<?>> getRightRoute(@RequestBody List<PositionDTO> positions) {
38+
return processRouteAndRecommend(positions, routeService::rightRoute);
5839
}
5940

6041
@PostMapping("/slow")
61-
public ResponseEntity<?> getRoute(@RequestBody List<PositionDTO> positions) {
62-
if (positions.isEmpty()) return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
63-
PositionDTO start = positions.get(0);
64-
65-
RouteResponseDTO routing = routeService.slowRoute(positions).block();
66-
Mono<AllCategoryRecommendationsDTO> recommendations = recommendationService.recommendByRoute(routing, start.getLat(), start.getLon());
67-
68-
ObjectNode response = objectMapper.createObjectNode();
69-
response.set("route", objectMapper.valueToTree(routing));
70-
response.set("recommendations", objectMapper.valueToTree(recommendations));
42+
public Mono<ResponseEntity<?>> getRoute(@RequestBody List<PositionDTO> positions) {
43+
return processRouteAndRecommend(positions, routeService::slowRoute);
44+
}
7145

72-
return new ResponseEntity<>(response, HttpStatus.OK);
46+
// 비동기 로직 한 번에 묶기
47+
private Mono<ResponseEntity<?>> processRouteAndRecommend(
48+
List<PositionDTO> positions,
49+
Function<List<PositionDTO>, Mono<RouteResponseDTO>> processer) {
50+
if (positions.isEmpty()) return Mono.just(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
51+
PositionDTO start = positions.get(0);
52+
return processer.apply(positions)
53+
.flatMap(routing -> recommendationService.recommendByRoute(routing, start.getLat(), start.getLon())
54+
.map(recommendations -> {
55+
ObjectNode response = objectMapper.createObjectNode();
56+
response.set("route", objectMapper.valueToTree(routing));
57+
response.set("recommendations", objectMapper.valueToTree(recommendations));
58+
59+
return new ResponseEntity<>(response, HttpStatus.OK);
60+
}));
7361
}
7462
}

backend/src/main/java/_team/onmyway/repository/PhotosRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import org.springframework.data.jpa.repository.Query;
77

88
import java.util.List;
9+
import java.util.Optional;
910

1011
public interface PhotosRepository extends JpaRepository<Photos, Long> {
12+
boolean existsByPlaceId(Long placeId);
13+
14+
Optional<Photos> findFirstByPlaceId(Long placeId);
1115
public List<Photos> findByPlace(Place place);
1216
}

backend/src/main/java/_team/onmyway/repository/PlaceRepository.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import _team.onmyway.entity.Place;
44
import _team.onmyway.entity.ServiceCategory;
5+
import org.springframework.data.jpa.repository.EntityGraph;
56
import org.springframework.data.jpa.repository.JpaRepository;
67
import org.springframework.data.jpa.repository.Query;
78

@@ -40,12 +41,14 @@ List<Place> findRandomByCategoryInRadius(
4041
int limit
4142
);
4243

44+
@EntityGraph(attributePaths = {"workingTimes", "serviceCategory"})
45+
// JPQL로 해결
4346
@Query(value = """
44-
SELECT * FROM place p
45-
WHERE p.service_category_id IN :categoryIds
47+
SELECT p FROM Place p
48+
WHERE p.serviceCategory.id IN :categoryIds
4649
AND p.lat BETWEEN :minLat AND :maxLat
4750
AND p.lng BETWEEN :minLng AND :maxLng
48-
""", nativeQuery = true)
51+
""")
4952
List<Place> findByBoundingBox(
5053
List<Long> categoryIds,
5154
double minLat,

backend/src/main/java/_team/onmyway/service/ImageService.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ public ImageService(PhotosRepository photosRepository) {
3333
}
3434

3535
public Mono<String> getImageURL(Place p) {
36-
List<Photos> dbPhotos = p.getPhotos();
37-
if (dbPhotos.size() > 0) {
38-
return Mono.just(dbPhotos.get(0).getPhotoURL());
36+
boolean hasPhoto = photosRepository.existsById(p.getId());
37+
if (hasPhoto) {
38+
return Mono.just(photosRepository.findFirstByPlaceId(p.getId()).get().getPhotoURL());
3939
} else {
4040
return webClient.get()
4141
.uri(uri -> uri

backend/src/main/java/_team/onmyway/service/RecommendationService.java

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.fasterxml.jackson.databind.ObjectMapper;
1212
import lombok.RequiredArgsConstructor;
1313
import lombok.extern.slf4j.Slf4j;
14+
import org.hibernate.jdbc.Work;
1415
import org.springframework.stereotype.Service;
1516
import reactor.core.publisher.Flux;
1617
import reactor.core.publisher.Mono;
@@ -105,10 +106,18 @@ public Mono<AllCategoryRecommendationsDTO> recommendByRoute(RouteResponseDTO rou
105106
.flatMap(p ->
106107
imageService.getImageURL(p)
107108
.map(imageURL -> {
108-
WorkingTime workingTime = p.getWorkingTimes().get(day%7);
109-
return toPlaceRecommendationDTO(p, userLat, userLng,
110-
workingTime.isClosed(), workingTime.getOpenTime(),
111-
workingTime.getCloseTime(), imageURL);
109+
List<WorkingTime> workingTimes = p.getWorkingTimes();
110+
int index = day&7;
111+
112+
if (workingTimes != null & !workingTimes.isEmpty() && index < workingTimes.size()) {
113+
WorkingTime workingTime = workingTimes.get(index);
114+
return toPlaceRecommendationDTO(p, userLat, userLng,
115+
workingTime.isClosed(), workingTime.getOpenTime(),
116+
workingTime.getCloseTime(), imageURL);
117+
} else {
118+
return toPlaceRecommendationDTO(p, userLat, userLng, true,
119+
null, null, imageURL);
120+
}
112121
})
113122
)
114123
.collectList()
@@ -249,17 +258,29 @@ private Mono<CategoryRecommendationDTO> recommendSingleCategory(double lat, doub
249258
return Flux.fromIterable(places)
250259
.flatMap(place -> {
251260
List<WorkingTime> placeWorkingTime = place.getWorkingTimes();
252-
WorkingTime workingTime = placeWorkingTime.get(day%7);
253-
254-
// 2. imageService.getImageURL(place)가 Mono<String>을 반환한다고 가정
255-
return imageService.getImageURL(place)
256-
.map(imageURL -> toPlaceRecommendationDTO(
257-
place, lat, lng,
258-
workingTime.isClosed(),
259-
workingTime.getOpenTime(),
260-
workingTime.getCloseTime(),
261-
imageURL
262-
));
261+
if (placeWorkingTime.isEmpty()) {
262+
return imageService.getImageURL(place)
263+
.map(imageURL -> toPlaceRecommendationDTO(
264+
place, lat, lng,
265+
true,
266+
null,
267+
null,
268+
imageURL
269+
));
270+
}
271+
else {
272+
WorkingTime workingTime = placeWorkingTime.get(day%7);
273+
274+
// 2. imageService.getImageURL(place)가 Mono<String>을 반환한다고 가정
275+
return imageService.getImageURL(place)
276+
.map(imageURL -> toPlaceRecommendationDTO(
277+
place, lat, lng,
278+
workingTime.isClosed(),
279+
workingTime.getOpenTime(),
280+
workingTime.getCloseTime(),
281+
imageURL
282+
));
283+
}
263284
})
264285
.collectList() // 3. 비동기로 생성된 DTO들을 다시 List로 모음
265286
.map(placeInfos -> {

0 commit comments

Comments
 (0)