diff --git a/src/main/java/DiffLens/back_end/domain/search/service/implement/NaturalSearchService.java b/src/main/java/DiffLens/back_end/domain/search/service/implement/NaturalSearchService.java index 901e7f8..e5b5c78 100644 --- a/src/main/java/DiffLens/back_end/domain/search/service/implement/NaturalSearchService.java +++ b/src/main/java/DiffLens/back_end/domain/search/service/implement/NaturalSearchService.java @@ -17,6 +17,7 @@ import DiffLens.back_end.domain.search.service.interfaces.SearchService; import DiffLens.back_end.global.fastapi.dto.response.MainSearchResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,6 +28,7 @@ /** * 자연어 Service --> SearchService 구현 */ +@Slf4j @Service("naturalSearchService") @RequiredArgsConstructor public class NaturalSearchService implements SearchService { @@ -47,6 +49,9 @@ public class NaturalSearchService implements SearchService request body의 클래스 - * @param response body의 클래스 - * - */ - @LogExecutionTime("서브서버 호출 소요시간") - public R sendRequest(FastApiRequestType type, T requestBody) { - - // 요청 타입이 맞지 않을 경우에 대한 예외 - if (!type.getRequestBody().isInstance(requestBody)) { - throw new IllegalArgumentException("올바르지 않은 요청 타입 " + type.name()); // TODO : 에외처리... - } - - R block = null; - try { - block = fastApiWebClient.post() - .uri(type.getUri()) - .bodyValue(requestBody) - .retrieve() - .bodyToMono((Class) type.getResponseType()) - .block(); - } catch (Exception e) { // fast api 호출 중 에러 발생시 예외 발생 - throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); - } - - return block; - } - - /** - * PathVariable을 사용하는 GET 요청 - * - * @param type fast api에 보낼 요청을 관리하는 enum - FastApiRequestType - * @param requestBody 요청 본문 (null 가능) - * @param pathVariables URI에 포함될 경로 변수들 - * @return fast api 로부터 응답받은 데이터 ( R ) - * @param request body의 클래스 - * @param response body의 클래스 - */ - @LogExecutionTime("서브서버 호출 소요시간") - public R sendRequestWithPathVariable( - FastApiRequestType type, - T requestBody, - Object... pathVariables - ) { - try { - return fastApiWebClient.get() - .uri(type.getUri(), pathVariables) - .exchangeToMono(response -> { - HttpStatusCode status = response.statusCode(); - - if (status.is4xxClientError() || status.is5xxServerError()) { - return response.bodyToMono(FastApiErrorDto.class) - .flatMap(errorDto -> { - String detail = errorDto.getDetail(); - - if ("해당 검색 결과에는 패널이 없습니다.".equals(detail)) { - return Mono.error(new ErrorHandler( - SearchStatus.NO_RESULT - )); - } - - // 기타 에러는 기본 서브서버 에러로 처리 - return Mono.error(new RuntimeException( - "서브 서버 요청 중 오류 발생. 문의 바람 : " + detail - )); - }); - } - - // 정상 응답 - return response.bodyToMono((Class) type.getResponseType()); - }) - .block(); - - } catch (ErrorHandler e) { - throw e; - } catch (Exception e) { - throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); - } - } - - - - - /** - * Query Parameter를 사용하는 POST 요청 - * - * @param type fast api에 보낼 요청을 관리하는 enum - FastApiRequestType - * @param queryParams 쿼리 파라미터 맵 (key-value 쌍) - * @return fast api 로부터 응답받은 데이터 ( R ) - * @param response body의 클래스 - */ - @LogExecutionTime("서브서버 호출 소요시간") - public R sendRequestWithQueryParams(FastApiRequestType type, java.util.Map queryParams) { - R block = null; - try { - block = fastApiWebClient.post() - .uri(uriBuilder -> { - var builder = uriBuilder.path(type.getUri()); - queryParams.forEach((key, value) -> { - if (value != null) { - builder.queryParam(key, value); - } - }); - return builder.build(); - }) - .retrieve() - .bodyToMono((Class) type.getResponseType()) - .block(); - } catch (WebClientResponseException e) { - // 서브서버의 실제 응답 상태 코드와 메시지 로깅 - System.err.println("=== 서브서버 응답 오류 ==="); - System.err.println("URI: " + type.getUri()); - System.err.println("Query Params: " + queryParams); - System.err.println("Status Code: " + e.getStatusCode()); - System.err.println("Response Body: " + e.getResponseBodyAsString()); - System.err.println("Error Message: " + e.getMessage()); - e.printStackTrace(); - throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); - } catch (Exception e) { - // 기타 예외 (네트워크 오류, 타임아웃 등) - System.err.println("=== 서브서버 요청 오류 ==="); - System.err.println("URI: " + type.getUri()); - System.err.println("Query Params: " + queryParams); - System.err.println("Error Type: " + e.getClass().getName()); - System.err.println("Error Message: " + e.getMessage()); - e.printStackTrace(); - throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); - } - - return block; - } - -} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/FastApiRequestType.java b/src/main/java/DiffLens/back_end/global/fastapi/FastApiRequestType.java index 3a329f8..e92a092 100644 --- a/src/main/java/DiffLens/back_end/global/fastapi/FastApiRequestType.java +++ b/src/main/java/DiffLens/back_end/global/fastapi/FastApiRequestType.java @@ -12,28 +12,26 @@ public enum FastApiRequestType { // 검색 - NATURAL_SEARCH("/ai/search", FastNaturalLanguageRequestDTO.NaturalSearch.class, - FastNaturalLanguageResponseDTO.NaturalSearch.class), - NATURAL_SEARCH2("/api/search/", MainSearchRequest.class, - MainSearchResponse.class), - RE_SEARCH("/api/re-search", FastReSearchRequestDTO.ReSearch.class, FastReSearchResponseDTO.ReSearch.class), + NATURAL_SEARCH("/api/search/", MainSearchRequest.class, + MainSearchResponse.class, "자연어 검색"), + RE_SEARCH("/api/re-search", FastReSearchRequestDTO.ReSearch.class, FastReSearchResponseDTO.ReSearch.class, "재검색"), // 추천 RECOMMENDATIONS("/api/quick-search/recommendations", FastHomeRequestDTO.HomeRecommendRequest.class, - FastHomeResponseDTO.HomeRecommend.class), // 일반 추천 + FastHomeResponseDTO.HomeRecommend.class, "검색어 일반 추천"), // 일반 추천 RECOMMENDATIONS_BY_MEMBER("/api/quick-search/recommendations/by-member", FastHomeRequestDTO.HomeRecommendByMemberRequest.class, - FastHomeResponseDTO.HomeRecommend.class), + FastHomeResponseDTO.HomeRecommend.class, "검색어 유저기반 추천"), // 라이브러리 비교 COMPARE("/api/cohort-comparison/compare", Void.class, - FastLibraryCompareResponseDTO.CompareResult.class), + FastLibraryCompareResponseDTO.CompareResult.class, "라이브러리 비교"), // 차트 CHART_RECOMMENDATIONS("/api/chart/search-result/{searchId}/recommendations", Void.class, - FastChartResponseDTO.ChartRecommendationsResponse.class), + FastChartResponseDTO.ChartRecommendationsResponse.class, "차트 추천"), CHART_FROM_LIBRARY("/api/chart/from-library", FastLibraryChartRequestDTO.class, - FastLibraryChartResponseDTO.LibraryChartResponse.class), + FastLibraryChartResponseDTO.LibraryChartResponse.class, "라이브러리 차트 추천"), // REFINE_SEARCH("/search/refine", // FastNaturalSearchResponseDTO.SearchResult.class), ; @@ -41,5 +39,6 @@ public enum FastApiRequestType { private final String uri; private final Class requestBody; // request body private final Class responseType; // response body + private final String name; } diff --git a/src/main/java/DiffLens/back_end/global/fastapi/FastApiService.java b/src/main/java/DiffLens/back_end/global/fastapi/FastApiService.java index 1eea862..2bfef01 100644 --- a/src/main/java/DiffLens/back_end/global/fastapi/FastApiService.java +++ b/src/main/java/DiffLens/back_end/global/fastapi/FastApiService.java @@ -2,14 +2,17 @@ import DiffLens.back_end.global.fastapi.dto.request.FastHomeRequestDTO; import DiffLens.back_end.global.fastapi.dto.request.FastLibraryChartRequestDTO; -import DiffLens.back_end.global.fastapi.dto.request.FastNaturalLanguageRequestDTO; import DiffLens.back_end.global.fastapi.dto.request.MainSearchRequest; import DiffLens.back_end.global.fastapi.dto.response.FastHomeResponseDTO; import DiffLens.back_end.global.fastapi.dto.response.FastLibraryChartResponseDTO; -import DiffLens.back_end.global.fastapi.dto.response.FastNaturalLanguageResponseDTO; import DiffLens.back_end.global.fastapi.dto.response.FastLibraryCompareResponseDTO; import DiffLens.back_end.global.fastapi.dto.response.MainSearchResponse; import DiffLens.back_end.global.fastapi.dto.response.FastChartResponseDTO; +import DiffLens.back_end.global.fastapi.fastApiClients.PathVariableFastApiClient; +import DiffLens.back_end.global.fastapi.fastApiClients.PostFastApiClient; +import DiffLens.back_end.global.fastapi.fastApiClients.QueryParamFastApiClient; +import DiffLens.back_end.global.responses.code.status.error.SearchStatus; +import DiffLens.back_end.global.responses.exception.handler.ErrorHandler; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,17 +23,13 @@ @RequiredArgsConstructor public class FastApiService { - private final FastApiClient fastApiClient; + private final PostFastApiClient postClient; + private final PathVariableFastApiClient pathClient; + private final QueryParamFastApiClient queryClient; // 자연어 검색 - public FastNaturalLanguageResponseDTO.NaturalSearch getNaturalSearch( - FastNaturalLanguageRequestDTO.NaturalSearch request) { - return fastApiClient.sendRequest(FastApiRequestType.NATURAL_SEARCH, request); - } - - // 자연어 검색2 public MainSearchResponse getMainSearch(MainSearchRequest request) { - return fastApiClient.sendRequest(FastApiRequestType.NATURAL_SEARCH2, request); + return postClient.sendRequest(FastApiRequestType.NATURAL_SEARCH, request); } // 라이브러리 비교 @@ -38,26 +37,33 @@ public FastLibraryCompareResponseDTO.CompareResult compareLibraries(Long cohort1 java.util.Map queryParams = new java.util.HashMap<>(); queryParams.put("cohort_1_id", cohort1Id); queryParams.put("cohort_2_id", cohort2Id); - return fastApiClient.sendRequestWithQueryParams(FastApiRequestType.COMPARE, queryParams); + return queryClient.sendRequest(FastApiRequestType.COMPARE, queryParams); } // 추천검색 public FastHomeResponseDTO.HomeRecommend recommend(FastHomeRequestDTO.HomeRecommendRequest request) { - return fastApiClient.sendRequest(FastApiRequestType.RECOMMENDATIONS, request); + return postClient.sendRequest(FastApiRequestType.RECOMMENDATIONS, request); } // 차트 추천 public FastChartResponseDTO.ChartRecommendationsResponse getChartRecommendations(Long searchId) { - return fastApiClient.sendRequestWithPathVariable( + + FastChartResponseDTO.ChartRecommendationsResponse response = pathClient.sendRequest( FastApiRequestType.CHART_RECOMMENDATIONS, null, searchId); + + // 호출 예외 발생하면 null을 반환함 -> null일 경우 NO_RESULT 반환 + if(response == null) + throw new ErrorHandler(SearchStatus.NO_RESULT); + + return response; } // 라이브러리로부터 차트 생성 public FastLibraryChartResponseDTO.LibraryChartResponse getChartsFromLibrary( FastLibraryChartRequestDTO request) { - return fastApiClient.sendRequest(FastApiRequestType.CHART_FROM_LIBRARY, request); + return postClient.sendRequest(FastApiRequestType.CHART_FROM_LIBRARY, request); } } diff --git a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastLibraryRequestDTO.java b/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastLibraryRequestDTO.java deleted file mode 100644 index d0d996f..0000000 --- a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastLibraryRequestDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package DiffLens.back_end.global.fastapi.dto.request; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -/** - * - * 라이브러리 비교 - * Spring Boot -> Fast API 요청 형태 - * - */ -public class FastLibraryRequestDTO { - - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class LibraryCompare{ - private Long libraryId1; - private Long libraryId2; - } - -} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastNaturalLanguageRequestDTO.java b/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastNaturalLanguageRequestDTO.java deleted file mode 100644 index e55532a..0000000 --- a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastNaturalLanguageRequestDTO.java +++ /dev/null @@ -1,47 +0,0 @@ -package DiffLens.back_end.global.fastapi.dto.request; - -import lombok.*; - -import java.util.List; - -/** - * - * 자연어 검색 - * Spring Boot -> Fast API 요청 형태 - * - */ -public class FastNaturalLanguageRequestDTO { - - // 자연어 검색 요청 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class NaturalSearch { - - private String query; - - private String searchMode; - - private NaturalSearchFilters filters; - - } - - @Getter - @Setter - @NoArgsConstructor - @AllArgsConstructor - public static class NaturalSearchFilters { - - private String respondentCount; - private String gender; - private String ageGroup; - private List region; - private String martialStatus; - private String childrenCount; - private String occupation; - - } - - -} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastPanelRequestDTO.java b/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastPanelRequestDTO.java deleted file mode 100644 index e33fc92..0000000 --- a/src/main/java/DiffLens/back_end/global/fastapi/dto/request/FastPanelRequestDTO.java +++ /dev/null @@ -1,97 +0,0 @@ -package DiffLens.back_end.global.fastapi.dto.request; - -import lombok.*; - -import java.util.List; - -/** - * - * Spring Boot -> Fast API 요청 형태 - * - */ -public class FastPanelRequestDTO { - - // 자연어 검색 요청 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class FastNaturalSearch{ - - private String question; - - private String mode; - - private FastSearchFilters filters; - - } - - // 재검색 요청 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class FastRefineSearch{ - - private String question; - - private String mode; - - private FastSearchFilters filters; - - - } - - // 차트 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class FastChart{ - - private String tempColumn; - - } - - // 추천 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class FastRecommendation { - - private String tempColumn; - - } - - // 라이브러리 비교 - @Getter - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class FastLibraryCompare { - - private Long libraryId1; - private Long libraryId2; - private List panelIds1; - private List panelIds2; - - } - - // ------------------------------- - // 아 아래는 위에서 필요한 클래스들 - - @Getter - @Setter - @NoArgsConstructor - @AllArgsConstructor - public static class FastSearchFilters{ - - private Integer count; - private String gender; - private List filters; - - } - - -} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/FastApiClient.java b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/FastApiClient.java new file mode 100644 index 0000000..5afb71a --- /dev/null +++ b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/FastApiClient.java @@ -0,0 +1,40 @@ +package DiffLens.back_end.global.fastapi.fastApiClients; + +import DiffLens.back_end.global.fastapi.FastApiRequestType; + +public interface FastApiClient { + + /** + * FastAPI 서버와의 모든 종류의 요청을 하나의 메서드로 처리하기 위한 통합 인터페이스입니다. + * + * 이 메서드는 기존 FastApiClient에서 분리되어 있던 다음 세 가지 요청 방식을 모두 지원합니다: + * + * 1) POST 요청 + RequestBody 전송 + * - 일반적인 FastAPI POST/PUT 요청을 처리합니다. + * - JSON Body를 전송하고, 지정된 타입의 응답을 반환받습니다. + * + * 2) GET 요청 + PathVariable 전달 + * - 경로에 값을 포함하여 요청하는 형태를 처리합니다. + * - 예: /api/item/{id} + * + * 3) GET 요청 + Query Parameter 전달 + * - key=value 형태의 쿼리 파라미터를 전달하는 GET 요청을 처리합니다. + * - 예: /api/items?keyword=abc&page=3 + * + * 위 세 가지 기능을 하나의 sendRequest 메서드로 통합하여, + * 요청 타입(FastApiRequestType)에 따라 적절한 방식으로 FastAPI 서버와 통신합니다. + * + * @param type FastAPI 요청 메타데이터 (URI, HTTP Method, 응답 타입 등) + * @param requestBody POST/PUT 요청 시 전송할 Body (GET 계열에서는 무시) + * @param pathVariables PathVariable 또는 QueryParam 등 요청 시 필요한 추가 매개변수 + * @param 요청 Body 타입 + * @param 응답 Body 타입 + * @return FastAPI 서버로부터 받은 응답 데이터 + */ + R sendRequest( + FastApiRequestType type, + T requestBody, + Object... pathVariables + ); + +} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PathVariableFastApiClient.java b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PathVariableFastApiClient.java new file mode 100644 index 0000000..0ced510 --- /dev/null +++ b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PathVariableFastApiClient.java @@ -0,0 +1,56 @@ +package DiffLens.back_end.global.fastapi.fastApiClients; + +import DiffLens.back_end.global.fastapi.FastApiRequestType; +import DiffLens.back_end.global.fastapi.dto.FastApiErrorDto; +import DiffLens.back_end.global.logger.annotations.SubServerExecutionTime; +import DiffLens.back_end.global.responses.code.status.error.ErrorStatus; +import DiffLens.back_end.global.responses.exception.handler.ErrorHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatusCode; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +/** + * path variable에 대한 FastApiClient + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class PathVariableFastApiClient implements FastApiClient { + + private final WebClient fastApiWebClient; + + @Override + @SubServerExecutionTime("서브서버 호출 소요시간") + public R sendRequest(FastApiRequestType type, T requestBody, Object... pathVariables) { + try { + return fastApiWebClient.get() + .uri(type.getUri(), pathVariables) + .exchangeToMono(response -> { + HttpStatusCode status = response.statusCode(); + + if (status.is4xxClientError() || status.is5xxServerError()) { + return response.bodyToMono(FastApiErrorDto.class) + .flatMap(errorDto -> { + String detail = errorDto.getDetail(); + + log.warn("🐞 [서브서버 호출 예외 발생] errorCode : {}, detail : {}", status.value(), detail); + + return Mono.just(null); + }); + } + + // 정상 응답 + return response.bodyToMono((Class) type.getResponseType()); + }) + .block(); + + } catch (ErrorHandler e) { + throw e; + } catch (Exception e) { + throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); + } + } +} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PostFastApiClient.java b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PostFastApiClient.java new file mode 100644 index 0000000..6d11311 --- /dev/null +++ b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/PostFastApiClient.java @@ -0,0 +1,43 @@ +package DiffLens.back_end.global.fastapi.fastApiClients; + +import DiffLens.back_end.global.fastapi.FastApiRequestType; +import DiffLens.back_end.global.logger.annotations.SubServerExecutionTime; +import DiffLens.back_end.global.responses.code.status.error.ErrorStatus; +import DiffLens.back_end.global.responses.exception.handler.ErrorHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PostFastApiClient implements FastApiClient { + + private final WebClient fastApiWebClient; + + @Override + @SubServerExecutionTime("서브서버 호출 소요시간") + public R sendRequest(FastApiRequestType type, T requestBody, Object... ignore) { + + // 요청 타입이 맞지 않을 경우에 대한 예외 + if (!type.getRequestBody().isInstance(requestBody)) { + throw new IllegalArgumentException("올바르지 않은 요청 타입 " + type.name()); // TODO : 에외처리... + } + + R block = null; + try { + block = fastApiWebClient.post() + .uri(type.getUri()) + .bodyValue(requestBody) + .retrieve() + .bodyToMono((Class) type.getResponseType()) + .block(); + } catch (Exception e) { // fast api 호출 중 에러 발생시 예외 발생 + log.warn("[{} 호출 중 예외 발생] {} : ", type.getName(), e.getMessage()); + throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); + } + + return block; + } +} diff --git a/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/QueryParamFastApiClient.java b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/QueryParamFastApiClient.java new file mode 100644 index 0000000..5835e22 --- /dev/null +++ b/src/main/java/DiffLens/back_end/global/fastapi/fastApiClients/QueryParamFastApiClient.java @@ -0,0 +1,74 @@ +package DiffLens.back_end.global.fastapi.fastApiClients; + +import DiffLens.back_end.global.fastapi.FastApiRequestType; +import DiffLens.back_end.global.logger.annotations.SubServerExecutionTime; +import DiffLens.back_end.global.responses.code.status.error.ErrorStatus; +import DiffLens.back_end.global.responses.exception.handler.ErrorHandler; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +import java.util.Map; + +/** + * Query Parameter에 대한 FastApiService + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class QueryParamFastApiClient implements FastApiClient { + + private final WebClient fastApiWebClient; + + @Override + @SubServerExecutionTime("서브서버 호출 소요시간") + public R sendRequest(FastApiRequestType type, T requestBody, Object... params) { + + Map queryParams = (Map) params[0]; + + try { + return fastApiWebClient.post() + .uri(uriBuilder -> { + var builder = uriBuilder.path(type.getUri()); + queryParams.forEach((key, value) -> { + if (value != null) { + builder.queryParam(key, value); + } + }); + return builder.build(); + }) + .retrieve() + .bodyToMono((Class) type.getResponseType()) + .block(); + + } catch (WebClientResponseException e) { + + StringBuilder sb = new StringBuilder(); + sb.append("\n=== 서브서버 응답 오류 ===\n") + .append("URI: ").append(type.getUri()).append("\n") + .append("Query Params: ").append(queryParams).append("\n") + .append("Status Code: ").append(e.getStatusCode()).append("\n") + .append("Response Body: ").append(e.getResponseBodyAsString()).append("\n") + .append("Error Message: ").append(e.getMessage()).append("\n"); + + log.error(sb.toString(), e); + + throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); + + } catch (Exception e) { + + StringBuilder sb = new StringBuilder(); + sb.append("\n=== 서브서버 요청 오류 ===\n") + .append("URI: ").append(type.getUri()).append("\n") + .append("Query Params: ").append(queryParams).append("\n") + .append("Error Type: ").append(e.getClass().getName()).append("\n") + .append("Error Message: ").append(e.getMessage()).append("\n"); + + log.error(sb.toString(), e); + + throw new ErrorHandler(ErrorStatus.SUB_SERVER_ERROR); + } + } +} diff --git a/src/main/java/DiffLens/back_end/global/aop/annotations/LogExecutionTime.java b/src/main/java/DiffLens/back_end/global/logger/annotations/SubServerExecutionTime.java similarity index 63% rename from src/main/java/DiffLens/back_end/global/aop/annotations/LogExecutionTime.java rename to src/main/java/DiffLens/back_end/global/logger/annotations/SubServerExecutionTime.java index c11c2d1..368d3e0 100644 --- a/src/main/java/DiffLens/back_end/global/aop/annotations/LogExecutionTime.java +++ b/src/main/java/DiffLens/back_end/global/logger/annotations/SubServerExecutionTime.java @@ -1,10 +1,10 @@ -package DiffLens.back_end.global.aop.annotations; +package DiffLens.back_end.global.logger.annotations; import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented -public @interface LogExecutionTime { +public @interface SubServerExecutionTime { String value() default "메서드 실행 시간"; } diff --git a/src/main/java/DiffLens/back_end/global/aop/ApiRequestLogAspect.java b/src/main/java/DiffLens/back_end/global/logger/aop/ApiRequestLogAspect.java similarity index 69% rename from src/main/java/DiffLens/back_end/global/aop/ApiRequestLogAspect.java rename to src/main/java/DiffLens/back_end/global/logger/aop/ApiRequestLogAspect.java index a9283f7..3540f6d 100644 --- a/src/main/java/DiffLens/back_end/global/aop/ApiRequestLogAspect.java +++ b/src/main/java/DiffLens/back_end/global/logger/aop/ApiRequestLogAspect.java @@ -1,8 +1,9 @@ -package DiffLens.back_end.global.aop; +package DiffLens.back_end.global.logger.aop; import DiffLens.back_end.domain.members.entity.Member; import DiffLens.back_end.domain.members.service.auth.CurrentUserService; -import DiffLens.back_end.global.aop.annotations.LogExecutionTime; +import DiffLens.back_end.global.fastapi.FastApiRequestType; +import DiffLens.back_end.global.logger.annotations.SubServerExecutionTime; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,6 +15,8 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import java.lang.reflect.Method; + @Aspect @Component @Slf4j @@ -27,7 +30,7 @@ public class ApiRequestLogAspect { * CommonPointcut.restControllerEndpoints() Pointcut 지정하여 * API 호출 시 호출 전후로 로그 출력하도록 하는 Aspect */ - @Around("CommonPointCut.restControllerEndpoints()") + @Around("DiffLens.back_end.global.logger.aop.CommonPointCut.restControllerEndpoints()") public Object logApiRequest(ProceedingJoinPoint jp) throws Throwable { long start = System.currentTimeMillis(); @@ -60,26 +63,43 @@ public Object logApiRequest(ProceedingJoinPoint jp) throws Throwable { } } - @Around("CommonPointCut.methodRuntimeEndpoints()") - public Object logExecutionTime(ProceedingJoinPoint jp) throws Throwable { + @Around("DiffLens.back_end.global.logger.aop.CommonPointCut.methodRuntimeEndpoints()") + public Object subServerExecutionTime(ProceedingJoinPoint jp) throws Throwable { long start = System.currentTimeMillis(); MethodSignature signature = (MethodSignature) jp.getSignature(); - String methodName = signature.toShortString(); + Method method = signature.getMethod(); - // 어노테이션 가져오기 - LogExecutionTime annotation = signature.getMethod().getAnnotation(LogExecutionTime.class); + SubServerExecutionTime annotation = method.getAnnotation(SubServerExecutionTime.class); String label = annotation.value(); + // === FastApiRequestType 찾아오기 === + FastApiRequestType apiType = null; + for (Object arg : jp.getArgs()) { + if (arg instanceof FastApiRequestType) { + apiType = (FastApiRequestType) arg; + break; + } + } + + // label 확장 + if (apiType != null) + label = label + " - " + apiType.getName(); // ← 자동 확장 + try { Object result = jp.proceed(); long end = System.currentTimeMillis(); - log.info("⏱️ [{}] {}: {}ms", label, methodName, (end - start)); + log.info("⏱️ [{}] {}: {}ms", label, signature.toShortString(), (end - start)); return result; } catch (Throwable ex) { long end = System.currentTimeMillis(); - log.error("💥 [{}] {} ({}ms) - {}", label, methodName, (end - start), ex.getMessage()); + log.error("💥 [{}] {} ({}ms) - {}", + label, + signature.toShortString(), + (end - start), + ex.getMessage()); throw ex; } } + } \ No newline at end of file diff --git a/src/main/java/DiffLens/back_end/global/aop/CommonPointCut.java b/src/main/java/DiffLens/back_end/global/logger/aop/CommonPointCut.java similarity index 70% rename from src/main/java/DiffLens/back_end/global/aop/CommonPointCut.java rename to src/main/java/DiffLens/back_end/global/logger/aop/CommonPointCut.java index 1be492c..be05150 100644 --- a/src/main/java/DiffLens/back_end/global/aop/CommonPointCut.java +++ b/src/main/java/DiffLens/back_end/global/logger/aop/CommonPointCut.java @@ -1,4 +1,4 @@ -package DiffLens.back_end.global.aop; +package DiffLens.back_end.global.logger.aop; import org.aspectj.lang.annotation.Pointcut; @@ -8,7 +8,7 @@ public class CommonPointCut { @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)") public void restControllerEndpoints() {} - @Pointcut("@annotation(DiffLens.back_end.global.aop.annotations.LogExecutionTime)") + @Pointcut("@annotation(DiffLens.back_end.global.logger.annotations.SubServerExecutionTime)") public void methodRuntimeEndpoints() {} } diff --git a/src/test/java/DiffLens/back_end/ApplicationTests.java b/src/test/java/DiffLens/back_end/ApplicationTests.java index 5827e17..41552a5 100644 --- a/src/test/java/DiffLens/back_end/ApplicationTests.java +++ b/src/test/java/DiffLens/back_end/ApplicationTests.java @@ -10,4 +10,4 @@ class ApplicationTests { void contextLoads() { } -} +} \ No newline at end of file