2025-10-22 13:50:04 +09:00
|
|
|
package com.snp.batch.global.config;
|
|
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
|
|
import org.springframework.context.annotation.Configuration;
|
|
|
|
|
import org.springframework.web.reactive.function.client.WebClient;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maritime API WebClient 설정
|
|
|
|
|
*
|
|
|
|
|
* 목적:
|
|
|
|
|
* - Maritime API 서버에 대한 WebClient Bean 등록
|
|
|
|
|
* - 동일한 API 서버를 사용하는 여러 Job에서 재사용
|
|
|
|
|
* - 설정 변경 시 한 곳에서만 수정
|
|
|
|
|
*
|
|
|
|
|
* 사용 Job:
|
|
|
|
|
* - shipDataImportJob: IMO 번호 조회
|
|
|
|
|
* - shipDetailImportJob: 선박 상세 정보 조회
|
|
|
|
|
*
|
|
|
|
|
* 다른 API 서버 추가 시:
|
|
|
|
|
* - 새로운 Config 클래스 생성 (예: OtherApiWebClientConfig)
|
|
|
|
|
* - Bean 이름을 다르게 지정 (예: @Bean(name = "otherApiWebClient"))
|
|
|
|
|
*/
|
|
|
|
|
@Slf4j
|
|
|
|
|
@Configuration
|
|
|
|
|
public class MaritimeApiWebClientConfig {
|
|
|
|
|
|
|
|
|
|
@Value("${app.batch.ship-api.url}")
|
|
|
|
|
private String maritimeApiUrl;
|
|
|
|
|
|
2025-11-27 21:55:46 +09:00
|
|
|
@Value("${app.batch.ais-api.url}")
|
2025-11-21 16:25:17 +09:00
|
|
|
private String maritimeAisApiUrl;
|
|
|
|
|
|
2025-11-27 21:55:46 +09:00
|
|
|
@Value("${app.batch.webservice-api.url}")
|
|
|
|
|
private String maritimeServiceApiUrl;
|
|
|
|
|
|
|
|
|
|
|
2025-10-22 13:50:04 +09:00
|
|
|
@Value("${app.batch.ship-api.username}")
|
|
|
|
|
private String maritimeApiUsername;
|
|
|
|
|
|
|
|
|
|
@Value("${app.batch.ship-api.password}")
|
|
|
|
|
private String maritimeApiPassword;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Maritime API용 WebClient Bean
|
|
|
|
|
*
|
|
|
|
|
* 설정:
|
|
|
|
|
* - Base URL: Maritime API 서버 주소
|
|
|
|
|
* - 인증: Basic Authentication
|
|
|
|
|
* - 버퍼: 20MB (대용량 응답 처리)
|
|
|
|
|
*
|
|
|
|
|
* @return Maritime API WebClient
|
|
|
|
|
*/
|
|
|
|
|
@Bean(name = "maritimeApiWebClient")
|
|
|
|
|
public WebClient maritimeApiWebClient() {
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
log.info("Maritime API WebClient 생성");
|
|
|
|
|
log.info("Base URL: {}", maritimeApiUrl);
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
|
|
|
|
|
return WebClient.builder()
|
|
|
|
|
.baseUrl(maritimeApiUrl)
|
2026-01-12 14:41:08 +09:00
|
|
|
.filter((request, next) -> {
|
|
|
|
|
// [핵심] 여기서 최종 완성된 URI를 로그로 출력합니다.
|
|
|
|
|
log.info(">>>> API Request: [{} {}]", request.method(), request.url());
|
|
|
|
|
return next.exchange(request);
|
|
|
|
|
})
|
2025-10-22 13:50:04 +09:00
|
|
|
.defaultHeaders(headers -> headers.setBasicAuth(maritimeApiUsername, maritimeApiPassword))
|
|
|
|
|
.codecs(configurer -> configurer
|
|
|
|
|
.defaultCodecs()
|
2025-12-05 10:17:08 +09:00
|
|
|
.maxInMemorySize(100 * 1024 * 1024)) // 30MB 버퍼
|
2025-10-22 13:50:04 +09:00
|
|
|
.build();
|
|
|
|
|
}
|
2025-11-21 16:25:17 +09:00
|
|
|
|
|
|
|
|
@Bean(name = "maritimeAisApiWebClient")
|
|
|
|
|
public WebClient maritimeAisApiWebClient(){
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
log.info("Maritime AIS API WebClient 생성");
|
|
|
|
|
log.info("Base URL: {}", maritimeAisApiUrl);
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
|
|
|
|
|
return WebClient.builder()
|
|
|
|
|
.baseUrl(maritimeAisApiUrl)
|
|
|
|
|
.defaultHeaders(headers -> headers.setBasicAuth(maritimeApiUsername, maritimeApiPassword))
|
|
|
|
|
.codecs(configurer -> configurer
|
|
|
|
|
.defaultCodecs()
|
2025-12-02 16:24:57 +09:00
|
|
|
.maxInMemorySize(50 * 1024 * 1024)) // 50MB 버퍼 (AIS GetTargets 응답 ~20MB+)
|
2025-11-21 16:25:17 +09:00
|
|
|
.build();
|
|
|
|
|
}
|
2025-11-27 21:55:46 +09:00
|
|
|
|
|
|
|
|
@Bean(name = "maritimeServiceApiWebClient")
|
|
|
|
|
public WebClient maritimeServiceApiWebClient(){
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
log.info("Maritime AIS API WebClient 생성");
|
|
|
|
|
log.info("Base URL: {}", maritimeServiceApiUrl);
|
|
|
|
|
log.info("========================================");
|
|
|
|
|
|
|
|
|
|
return WebClient.builder()
|
|
|
|
|
.baseUrl(maritimeServiceApiUrl)
|
2026-01-12 14:41:08 +09:00
|
|
|
.filter((request, next) -> {
|
|
|
|
|
// [핵심] 여기서 최종 완성된 URI를 로그로 출력합니다.
|
|
|
|
|
log.info(">>>> API Request: [{} {}]", request.method(), request.url());
|
|
|
|
|
return next.exchange(request);
|
|
|
|
|
})
|
2025-11-27 21:55:46 +09:00
|
|
|
.defaultHeaders(headers -> headers.setBasicAuth(maritimeApiUsername, maritimeApiPassword))
|
|
|
|
|
.codecs(configurer -> configurer
|
|
|
|
|
.defaultCodecs()
|
2025-12-02 12:50:28 +09:00
|
|
|
.maxInMemorySize(100 * 1024 * 1024)) // 100MB 버퍼
|
2025-11-27 21:55:46 +09:00
|
|
|
.build();
|
|
|
|
|
}
|
2025-10-22 13:50:04 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ========================================
|
|
|
|
|
* 다른 API 서버 추가 예시
|
|
|
|
|
* ========================================
|
|
|
|
|
*
|
|
|
|
|
* 1. 새로운 Config 클래스 생성:
|
|
|
|
|
*
|
|
|
|
|
* @Configuration
|
|
|
|
|
* public class ExternalApiWebClientConfig {
|
|
|
|
|
*
|
|
|
|
|
* @Bean(name = "externalApiWebClient")
|
|
|
|
|
* public WebClient externalApiWebClient(
|
|
|
|
|
* @Value("${app.batch.external-api.url}") String url,
|
|
|
|
|
* @Value("${app.batch.external-api.token}") String token) {
|
|
|
|
|
*
|
|
|
|
|
* return WebClient.builder()
|
|
|
|
|
* .baseUrl(url)
|
|
|
|
|
* .defaultHeader("Authorization", "Bearer " + token)
|
|
|
|
|
* .build();
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* 2. JobConfig에서 사용:
|
|
|
|
|
*
|
|
|
|
|
* public ExternalJobConfig(
|
|
|
|
|
* ...,
|
|
|
|
|
* @Qualifier("externalApiWebClient") WebClient externalApiWebClient) {
|
|
|
|
|
* this.webClient = externalApiWebClient;
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* 3. application.yml에 설정 추가:
|
|
|
|
|
*
|
|
|
|
|
* app:
|
|
|
|
|
* batch:
|
|
|
|
|
* external-api:
|
|
|
|
|
* url: https://external-api.example.com
|
|
|
|
|
* token: ${EXTERNAL_API_TOKEN}
|
|
|
|
|
*/
|