[KC-108] 분석도엽 ai api호출 테스트

This commit is contained in:
2026-01-12 13:42:25 +09:00
parent e42c99cd60
commit 0b45f42528
10 changed files with 155 additions and 71 deletions

View File

@@ -1,24 +0,0 @@
package com.kamco.cd.kamcoback.config;
import io.netty.channel.ChannelOption;
import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
HttpClient httpClient =
HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.responseTimeout(Duration.ofSeconds(5));
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient)).build();
}
}

View File

@@ -0,0 +1,30 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
@RequiredArgsConstructor
public class ExternalHttpClient {
private final RestTemplate restTemplate;
public ResponseEntity<String> exchange(
String url, HttpMethod method, Object body, HttpHeaders headers) {
HttpEntity<Object> entity = new HttpEntity<>(body, headers);
return restTemplate.exchange(url, method, entity, String.class);
}
public ExternalCallResult call(String url, HttpMethod method, Object body, HttpHeaders headers) {
ResponseEntity<String> res = exchange(url, method, body, headers);
int code = res.getStatusCodeValue();
return new ExternalCallResult(code, code >= 200 && code < 300, res.getBody());
}
public record ExternalCallResult(int statusCode, boolean success, String body) {}
}

View File

@@ -0,0 +1,21 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import java.time.Duration;
import java.util.List;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.connectTimeout(Duration.ofSeconds(2))
.readTimeout(Duration.ofSeconds(3))
.additionalInterceptors(List.of(new RetryInterceptor()))
.build();
}
}

View File

@@ -0,0 +1,49 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
public class RetryInterceptor implements ClientHttpRequestInterceptor {
private static final int MAX_RETRY = 3;
private static final long WAIT_MILLIS = 3000;
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
IOException lastException = null;
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
try {
// HTTP 응답을 받으면(2xx/4xx/5xx 포함) 그대로 반환
return execution.execute(request, body);
} catch (IOException e) {
// 네트워크/타임아웃 등 I/O 예외만 재시도
lastException = e;
}
// 마지막 시도가 아니면 대기
if (attempt < MAX_RETRY) {
sleep();
}
}
// 마지막 예외를 그대로 던져서 원인이 로그에 남게 함
throw lastException;
}
private void sleep() throws IOException {
try {
TimeUnit.MILLISECONDS.sleep(WAIT_MILLIS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Retry interrupted", ie);
}
}
}