99import com .back .web7_9_codecrete_be .global .error .code .LocationErrorCode ;
1010import com .back .web7_9_codecrete_be .global .error .exception .BusinessException ;
1111import lombok .RequiredArgsConstructor ;
12+ import lombok .extern .slf4j .Slf4j ;
13+ import org .springframework .cache .annotation .Cacheable ;
1214import org .springframework .retry .annotation .Backoff ;
15+ import org .springframework .retry .annotation .Recover ;
1316import org .springframework .retry .annotation .Retryable ;
1417import org .springframework .stereotype .Service ;
1518import org .springframework .web .client .HttpServerErrorException ;
1922import java .util .List ;
2023
2124@ Service
25+ @ Slf4j
2226@ RequiredArgsConstructor
2327public class KakaoLocalService {
2428
2529 private final RestClient kakaoRestClient ;
2630 private final RestClient kakaoMobilityClient ;
2731
32+
33+ public double round4 (double num ) { //좌표의 소수점 숫자가 다르면 매번 다른 캐싱을 해야하니, 통일시켜줌
34+
35+ return Math .round (num * 10000 ) / 10000.0 ;
36+ }
37+
2838 // 해당 좌표의 1km 근방에 존재하는 음식점를 거리순으로 나타냄
39+ @ Cacheable (
40+ cacheNames = "nearByRestaurants" ,
41+ key = "T(String).format('lat=%s:lng=%s:r=1000', T(java.lang.Math).round(#lat*10000)/10000.0, T(java.lang.Math).round(#lng*10000)/10000.0)"
42+ )
2943 @ Retryable ( //최초 1번, 재시도 2번 시도
3044 retryFor = {HttpServerErrorException .class , ResourceAccessException .class }, //외부 서버의 문제, 네트워크, 타임아웃 문제인 경우에 재시도
45+ maxAttempts = 3 ,
3146 backoff = @ Backoff (delay = 200 , multiplier = 2.0 ) //0.2초, 0.4초, 0.8초 순으로 재시도
3247 )
33- public List <KakaoLocalResponse .Document > searchNearbyRestaurants (double lat , double lng ) {
34- //좌표의 소수점 숫자가 다르면 매번 다른 캐싱을 해야하니, 통일시켜줌
35- lat = Math .round (lat * 10000 ) / 10000.0 ;
36- lng = Math .round (lng * 10000 ) / 10000.0 ;
37- final double y = lat ;
38- final double x = lng ;
48+ public List <KakaoLocalResponse .Document > searchNearbyRestaurantsCached (double lat , double lng ) {
49+ double nLat = round4 (lat );
50+ double nLng = round4 (lng );
51+
3952 return kakaoRestClient .get ()
4053 .uri (uriBuilder -> uriBuilder
4154 .path ("/v2/local/search/keyword.json" )
4255 .queryParam ("query" , "음식점" )
4356 .queryParam ("category_group_code" , "FD6" )
44- .queryParam ("x" , x )
45- .queryParam ("y" , y )
57+ .queryParam ("x" , nLng )
58+ .queryParam ("y" , nLat )
4659 .queryParam ("radius" , 1000 ) // 반경 1km
4760 .queryParam ("sort" , "distance" )
4861 .build ()
@@ -53,24 +66,25 @@ public List<KakaoLocalResponse.Document> searchNearbyRestaurants(double lat, dou
5366 }
5467
5568 // 해당 좌표의 1km 근방에 존재하는 카페를 거리순으로 나타냄
69+ @ Cacheable (
70+ cacheNames = "nearByCafes" ,
71+ key = "T(String).format('lat=%s:lng=%s:r=1000', T(java.lang.Math).round(#lat*10000)/10000.0, T(java.lang.Math).round(#lng*10000)/10000.0)"
72+ )
5673 @ Retryable ( //최초 1번, 재시도 2번 시도
5774 retryFor = {HttpServerErrorException .class , ResourceAccessException .class }, //외부 서버의 문제, 네트워크, 타임아웃 문제인 경우에 재시도
75+ maxAttempts = 3 ,
5876 backoff = @ Backoff (delay = 200 , multiplier = 2.0 ) //0.2초, 0.4초, 0.8초 순으로 재시도
5977 )
60- public List <KakaoLocalResponse .Document > searchNearbyCafes (double lat , double lng ) {
61-
62- //좌표의 소수점 숫자가 다르면 매번 다른 캐싱을 해야하니, 통일시켜줌
63- lat = Math .round (lat * 10000 ) / 10000.0 ;
64- lng = Math .round (lng * 10000 ) / 10000.0 ;
65- final double y = lat ;
66- final double x = lng ;
78+ public List <KakaoLocalResponse .Document > searchNearbyCafesCached (double lat , double lng ) {
79+ double nLat = round4 (lat );
80+ double nLng = round4 (lng );
6781 return kakaoRestClient .get ()
6882 .uri (uriBuilder -> uriBuilder
6983 .path ("/v2/local/search/keyword.json" )
7084 .queryParam ("query" , "카페" )
7185 .queryParam ("category_group_code" , "CE7" )
72- .queryParam ("x" , x )
73- .queryParam ("y" , y )
86+ .queryParam ("x" , nLng )
87+ .queryParam ("y" ,nLat )
7488 .queryParam ("radius" , 1000 ) // 반경 1km
7589 .queryParam ("sort" , "distance" )
7690 .build ()
@@ -182,4 +196,12 @@ public KakaoRouteTransitResponse NaviSearchTransit(KakaoRouteTransitRequest tran
182196 .body (KakaoRouteTransitResponse .class ); //KakaoRouteTransitResponse로 카카오 자동차 api에서 주는 응답값
183197 return response ;
184198 }
199+
200+ @ Recover
201+ public List <KakaoLocalResponse .Document > recover (Exception e , double lat , double lng ) {
202+ // 로그 남기기
203+ log .warn ("Kakao API 실패 (재시도 소진) lat={}, lng={}, msg={}" , lat , lng , e .getMessage ());
204+ // 실패하면 서비스 정책대로 처리
205+ throw new BusinessException (LocationErrorCode .EXTERNAL_API_FAILED );
206+ }
185207}
0 commit comments