@RestController
@RequiredArgsConstructor
public class WebClientController {
private final WebClientService service;
/**
* 서울시 문화행사 공공서비스예약 정보 Open API JSON 연동 성공.
*/
@GetMapping("/test")
public WebClientDTO returnString(@RequestParam Integer startIndex, @RequestParam Integer endIndex) {
WebClientDTO res = service.get(startIndex, endIndex);
return res;
}
@GetMapping("/test2")
public String returnString2(@RequestParam Integer startIndex, @RequestParam Integer endIndex) {
String res = service.get2(startIndex, endIndex);
return res;
}
/**
* 서울시 공공서비스 예약 (종합) 정보
*/
@GetMapping("/test3")
public WebClientDTO returnPublicServiceReservationAPI(@RequestParam Integer startIndex, @RequestParam Integer endIndex) {
WebClientDTO res = service.getPublicServiceReservation(startIndex, endIndex);
return res;
}
@GetMapping("/test4")
public String get1365Data(@RequestParam("noticeEndde") Integer num) { //프로그램 등록 정보
System.out.println("num : " + num);
String res = service.get1365Data(num);
return res;
}
@GetMapping(value = "/test5", produces = MediaType.APPLICATION_XML_VALUE)
public Volunteer getData(@RequestParam("noticeEndde") Integer num) {
Volunteer res = service.returnData(num);
return res;
}
}
⚽ 전체 코드
@Service
@Transactional
@Slf4j
public class WebClientService {
private final String BASE_URL = "http://openAPI.seoul.go.kr:8088";
private final String BASE_URL_1365 = "http://openapi.1365.go.kr/openapi/service";
private final String BASE_URL_JOB = "http://apis.data.go.kr/B552474/SenuriService";
private final WebClient webClient;
/**
* 객체지향적으로 Bean으로 등록해서 받아와서 사용
* 등록은 xml전용으로 해놓고 가져와서 json으로 사용해야 할 경우 변경
*/
public WebClientService(WebClient webClient) {
this.webClient = webClient;
}
@Value("${api.hyunsoo.key}")
private String api_key;
@Value("${api.key.1365}")
private String api_key_1365;
@Value("${api.key.data.portal}")
private String apiKeyDataPortal;
public WebClientDTO get(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//이렇게 URI의 빌드 설정을 완료하면 WebClient의 인스턴스를 생성할 때 해당 UriBuilder로 uri를 만든다고 설정하면 된다.
// 기본 세팅 진행
WebClient webClient = WebClient.builder()
.uriBuilderFactory(factory)
.baseUrl(BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
//필요한 값들 요청.
WebClientDTO response = webClient.get()
.uri(uriBuilder -> uriBuilder.path("{api_key}/json/ListPublicReservationCulture/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.retrieve()
.bodyToMono(WebClientDTO.class)
.block();
/**
* 결과 확인 log
log.info("반환되는 값 : {}", response.toString());
*/
return response;
}
public String get2(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//이렇게 URI의 빌드 설정을 완료하면 WebClient의 인스턴스를 생성할 때 해당 UriBuilder로 uri를 만든다고 설정하면 된다.
// 기본 세팅 진행
WebClient webClient = WebClient.builder()
.uriBuilderFactory(factory)
.baseUrl(BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
//필요한 값들 요청.
String response = webClient.get()
.uri(uriBuilder -> uriBuilder.path("{api_key}/json/ListPublicReservationCulture/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.retrieve()
.bodyToMono(String.class)
.block();
/**
* 결과 확인 log
log.info("반환되는 값 : {}", response.toString());
*/
return response;
}
public WebClientDTO getPublicServiceReservation(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//모든 값 요청.
WebClientDTO res = webClient.mutate() // mutate() => Return a builder to create a new WebClient whose settings are replicated from the current WebClient.
.uriBuilderFactory(factory)
.build()
.get().uri(uriBuilder ->
uriBuilder.path("{api_key}/json/tvYeyakCOllect/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(WebClientDTO.class)
.block();
/**
* 결과 확인 log
log.info("반환되는 res : {}", res.toString());
*/
return res;
}
public String get1365Data(Integer num) {
//xml open api를 사용하기 위한 기본 세팅.
/**
* open api를 xml 형식으로 받아오기 위해 기본 세팅.
*/
WebClient webClient = WebClient.builder()
.baseUrl(BASE_URL_1365)
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Encoder(new Jaxb2XmlEncoder())
).codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Decoder(new Jaxb2XmlDecoder())
)
.build()
)
.build();
/**
* 내가 원하는 형식으로 반환 성공 !
*/
String res = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/rest")
.path("/VolunteerPartcptnService")
.path("getVltrSearchWordList")
.queryParam("serviceKey", api_key_1365)
.queryParam("noticeEndde", num)
.build())
.accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
.retrieve()
.bodyToMono(String.class)
.block();
/**
* String format을 활용한 xml 활용
String url = String.format("http://openapi.1365.go.kr/openapi/service/rest/VolunteerPartcptnService/getVltrSearchWordList?serviceKey=%s¬iceEndde=%d", api_key_1365, num);
String res = webClient.get()
.uri(url)
.accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
.retrieve()
.bodyToMono(String.class)
.block();
*/
/**
* 로그 확인
log.info("num : {}", num);
log.info("response : {}", res);
*/
return res;
}
public Volunteer returnData(Integer num) {
/**
* open api를 xml 형식으로 받아오기 위해 기본 세팅.
*/
WebClient webClient = WebClient.builder()
.baseUrl(BASE_URL_1365)
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Encoder(new Jaxb2XmlEncoder())
).codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Decoder(new Jaxb2XmlDecoder())
)
.build()
)
.build();
/**
* 내가 원하는 형식으로 반환 성공 !
*/
Volunteer res = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/rest")
.path("/VolunteerPartcptnService")
.path("getVltrSearchWordList")
.queryParam("serviceKey", api_key_1365)
.queryParam("noticeEndde", num)
.build())
.accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
.retrieve()
.bodyToMono(Volunteer.class)
.block();
/**
* 로그 확인
log.info("res : {}", res.toString());
*/
return res;
}
public String employmentSupport(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//이렇게 URI의 빌드 설정을 완료하면 WebClient의 인스턴스를 생성할 때 해당 UriBuilder로 uri를 만든다고 설정하면 된다.
// 기본 세팅 진행
WebClient webClient = WebClient.builder()
.uriBuilderFactory(factory)
.baseUrl(BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
//필요한 값들 요청.
String response = webClient.get()
.uri(uriBuilder -> uriBuilder.path("{api_key}/json/tbViewProgram/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.retrieve()
.bodyToMono(String.class)
.block();
/**
* 결과 확인 log
log.info("반환되는 값 : {}", response.toString());
*/
return response;
}
public EmploymentJsonDto returnEmploymentDto(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
EmploymentJsonDto res = webClient.mutate() // mutate() => Return a builder to create a new WebClient whose settings are replicated from the current WebClient.
.uriBuilderFactory(factory)
.build()
.get().uri(uriBuilder ->
uriBuilder.path("{api_key}/json/tbViewProgram/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(EmploymentJsonDto.class)
.block();
return res;
}
/**
* 100세누리 구인정보 목록 검색 --> 나중에 시간날 때 코드 고쳐보자.
*/
public SenuriServiceRawResponse CallSenuriListService(int numOfRows, int pageNo) {
final String uri = String.format("http://apis.data.go.kr/B552474/SenuriService/getJobList?ServiceKey=%s&numOfRows=%d&pageNo=%d",
apiKeyDataPortal,numOfRows, pageNo);
System.out.println("uri = " + uri);
try {
return webClient.get()
.uri(new URI(uri))
.accept(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(SenuriServiceRawResponse.class)
.blockOptional().orElseThrow(() -> new RuntimeException("[CallSenuriService] Error."));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
/**
* String으로 제대로 되는지 테스트
*/
public String CallSenuriListServiceString(int numOfRows, int pageNo) {
final String uri = String.format("http://apis.data.go.kr/B552474/SenuriService/getJobList?ServiceKey=%s&numOfRows=%d&pageNo=%d",
apiKeyDataPortal,numOfRows, pageNo);
System.out.println("uri = " + uri);
try {
return webClient.get()
.uri(new URI(uri))
.accept(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(String.class)
.blockOptional().orElseThrow(() -> new RuntimeException("[CallSenuriService] Error."));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
/**
* 100세누리 구인정보 상세 정보 조회
*/
public SenuriServiceDetailRawResponse CallSenuriDetailService(String id) {
final String uri = String.format("https://apis.data.go.kr/B552474/SenuriService/getJobInfo?&id=%s&serviceKey=%s",
id, apiKeyDataPortal);
try {
return webClient.get()
.uri(new URI(uri))
.accept(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(SenuriServiceDetailRawResponse.class)
.blockOptional().orElseThrow(() -> new RuntimeException("[CallSenuriServiceDetail] Error."));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WebClientDTO {
@JsonProperty("tvYeyakCOllect")
private PublicServiceReservation publicServiceReservation;
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class PublicServiceReservation {
@JsonProperty("list_total_count")
private String listTotalCount;
@JsonProperty("RESULT")
private Result result;
@JsonProperty("row")
private List<Row> rows;
}
//키 값 제대로 보고 파싱해야함.!!!! -> 코드 정리
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Result{
@JsonProperty("CODE")
private String code;
@JsonProperty("MESSAGE")
private String message;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class Row{
@JsonProperty("DIV")
private String div;
@JsonProperty("SERVICE")
private String service;
@JsonProperty("GUBUN")
private String gubun;
@JsonProperty("SVCID")
private String serviceId;
@JsonProperty("MAXCLASSNM")
private String maxClassNM;
@JsonProperty("MINCLASSNM")
private String minClassNM;
@JsonProperty("SVCSTATNM")
private String svcStatNM;
@JsonProperty("SVCNM")
private String svcNM;
@JsonProperty("PAYATNM")
private String payAtNM;
@JsonProperty("PLACENM")
private String placeNM;
@JsonProperty("USETGTINFO")
private String useTgtInfo;
@JsonProperty("SVCURL")
private String svcUrl;
@JsonProperty("X")
private String x;
@JsonProperty("Y")
private String y;
@JsonProperty("SVCOPNBGNDT")
private String svcOpnBgnDt;
@JsonProperty("SVCOPNENDDT")
private String svcOpnEndDt;
@JsonProperty("RCPTBGNDT")
private String rcptbgndt;
@JsonProperty("RCPTENDDT")
private String rcptenddt;
@JsonProperty("AREANM")
private String areaNM;
@JsonProperty("IMGURL")
private String imgUrl;
@JsonProperty("DTLCONT")
private String dtlCont;
@JsonProperty("TELNO")
private String telNo;
@JsonProperty("V_MIN")
private String vMin;
@JsonProperty("V_MAX")
private String vMax;
@JsonProperty("REVSTDDAYNM")
private String revStdDayNM;
@JsonProperty("REVSTDDAY")
private String RevStdDay;
}
public int fetchListTotalCount() {
return Integer.parseInt(this.publicServiceReservation.listTotalCount);
}
public List<PublicServiceReservationDto> toDto() {
return this.publicServiceReservation.rows.stream()
.map(r -> PublicServiceReservationDto.of(
r.div, r.service, r.gubun, r.serviceId, r.maxClassNM, r.minClassNM, r.svcStatNM, r.svcNM, r.payAtNM, r.placeNM, r.useTgtInfo, r.svcUrl,
r.x, r.y, r.svcOpnBgnDt, r.svcOpnEndDt, r.rcptbgndt, r.rcptenddt, r.areaNM, r.imgUrl, r.dtlCont, r.telNo, r.vMin, r.vMax, r.revStdDayNM, r.RevStdDay
)).collect(Collectors.toList());
}
}
@JsonProperty
를 통해 데이터를 이름 그대로 맵핑하면 된다 !public WebClientDTO get(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
//이렇게 URI의 빌드 설정을 완료하면 WebClient의 인스턴스를 생성할 때 해당 UriBuilder로 uri를 만든다고 설정하면 된다.
// 기본 세팅 진행
WebClient webClient = WebClient.builder()
.uriBuilderFactory(factory)
.baseUrl(BASE_URL)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
//필요한 값들 요청.
WebClientDTO response = webClient.get()
.uri(uriBuilder -> uriBuilder.path("{api_key}/json/ListPublicReservationCulture/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.retrieve()
.bodyToMono(WebClientDTO.class)
.block();
/**
* 결과 확인 log
log.info("반환되는 값 : {}", response.toString());
*/
return response;
}
🎈 .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
:
.uri(uriBuilder -> uriBuilder.path("{api_key}/json/ListPublicReservationCulture/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
public String get1365Data(Integer num) {
//xml open api를 사용하기 위한 기본 세팅.
/**
* open api를 xml 형식으로 받아오기 위해 기본 세팅.
*/
WebClient webClient = WebClient.builder()
.baseUrl(BASE_URL_1365)
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Encoder(new Jaxb2XmlEncoder())
).codecs(clientCodecConfigurer ->
clientCodecConfigurer.defaultCodecs()
.jaxb2Decoder(new Jaxb2XmlDecoder())
)
.build()
)
.build();
/**
* 내가 원하는 형식으로 반환 성공 !
*/
String res = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/rest")
.path("/VolunteerPartcptnService")
.path("getVltrSearchWordList")
.queryParam("serviceKey", api_key_1365)
.queryParam("noticeEndde", num)
.build())
.accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
.retrieve()
.bodyToMono(String.class)
.block();
/**
* String format을 활용한 xml 활용
String url = String.format("http://openapi.1365.go.kr/openapi/service/rest/VolunteerPartcptnService/getVltrSearchWordList?serviceKey=%s¬iceEndde=%d", api_key_1365, num);
String res = webClient.get()
.uri(url)
.accept(MediaType.valueOf(MediaType.APPLICATION_XML_VALUE))
.retrieve()
.bodyToMono(String.class)
.block();
*/
/**
* 로그 확인
log.info("num : {}", num);
log.info("response : {}", res);
*/
return res;
}
🎈 위 부분처럼 builder()를 통해 xml 형식으로 받아오기 위해 기본 세팅이 필요하다. jaxb2Encoder, jaxb2Decoder를 통해 xml 설정을 해줘야함.
🎈 그리고 WebClient 인스턴스를 사용하여 Open API에 요청 보낼 때 .accept()
메서드에 xml_value를 넣어주면 된다.
@Configuration
public class WebClientConfig {
@Bean
public WebClient webClient() {
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
clientCodecConfigurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder());
clientCodecConfigurer.defaultCodecs().maxInMemorySize(-1);
})
.build();
return WebClient.builder()
.exchangeStrategies(exchangeStrategies)
.build();
}
}
따로 private final로 설정해둔 인스턴스를 통해 json인 경우에 mutate()를 통해 설정만 변경해주면 된다 !
public EmploymentJsonDto returnEmploymentDto(Integer startIndex, Integer endIndex) {
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(BASE_URL);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
EmploymentJsonDto res = webClient.mutate() // mutate() => Return a builder to create a new WebClient whose settings are replicated from the current WebClient.
.uriBuilderFactory(factory)
.build()
.get().uri(uriBuilder ->
uriBuilder.path("{api_key}/json/tbViewProgram/{startIndex}/{endIndex}")
.build(api_key, startIndex, endIndex))
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.retrieve()
.bodyToMono(EmploymentJsonDto.class)
.block();
return res;
}
mutate()
를 통해 다시 설정하면 된다.🎈 mutate()
: WebClient의 현재 인스턴스를 복제하여 수정 가능한 빌더를 반환하는 메서드이다. 이 메서드를 사용하면 기존의 WebClient 인스턴스에서 일부 설정을 변경하거나 확장할 수 있다.
mutate()
메서드는 WebClient의 기존 설정을 그대로 가져가면서 특정 부분을 수정하고자 할 때 유용!!! 예를 들어, 기존의 WebClient 설정을 유지하면서 새로운 필터를 추가하거나, 또는 특정한 URI 템플릿을 사용하기 위해 mutate() 메서드를 사용할 수 있다.public SenuriServiceRawResponse CallSenuriService(int numOfRows, int pageNo) {
final String uri = String.format("http://apis.data.go.kr/B552474/SenuriService/getJobList?numOfRows=%d&pageNo=%d&ServiceKey=%s",
numOfRows, pageNo, apiKeyDataPortal);
System.out.println("uri = " + uri);
try {
return webClient.get()
.uri(new URI(uri))
.accept(MediaType.APPLICATION_XML)
.retrieve()
.bodyToMono(SenuriServiceRawResponse.class)
.blockOptional().orElseThrow(() -> new RuntimeException("[CallSenuriService] Error."));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}