참고 : https://innopc.tistory.com/31
참고 2: https://www.petrikainulainen.net/programming/spring-framework/spring-batch-tutorial-reading-information-from-a-rest-api/
참고 2 깃허브 : https://github.com/pkainulainen/spring-batch-examples
스프링 배치의 ItemReader는 파일/XML/DB에서 데이터를 읽기 위한 전용 클래스를 제공하지만 RestAPI로 다른시스템의 데이터를 읽어오는 전용 클래스는 아직 없다고 한다.
Rest 클라이언트 기능은 스프링에서 제공하는 RestTemplate를 이용하면 간단하게 구현 가능하다.
참고 : https://www.culture.go.kr/data/openapi/openapiView.do?id=468&category=F&gubun=A
사용이유 : totalCount가 649358로 가장 많아서.
넘어오는 구조는 다음과 같다. title이랑 person 말고는 별로 쓸모는 없군… 음 데이터를 보니까 일본어, 중국어도 많이 보이고 정작 도서에 대한 정보는 별로 없는듯…
GET http://api.kcisa.kr/openapi/service/rest/meta16/getNlTot?serviceKey=서비스키&numOfRows=10&pageNo=1
Accept: application/json
{
"header": {
"resultCode": "string",
"resultMsg": "string"
},
"body": {
"items": {
"item": [
{
"title": "string",
"creator": "string",
"collectionDb": "string",
"extent": "string",
"description": "string",
"url": "string",
"affiliation": "string",
"subDescription": "string",
"spatialCoverage": "string",
"person": "string"
}
]
},
"numOfRows": "10",
"pageNo": "1",
"totalCount": "string"
}
}
※ Spring Framework 5부터는 WebFlux 스택과 함께 Spring은 WebClient 라는 새로운 HTTP 클라이언트를 도입하여 기존의 동기식 API를 제공할 뿐 만 아니라 효율적인 비차단 및 비동기 접근 방식을 지원하여, Spring 5.0 이후 부터는 RestTemplate는 deprecated 되었습니다. (WebClient 사용 지향)
참고 : https://blog.naver.com/hj_kim97/222295259904
하지만 RestTemplate도 아직까지는 많이 쓰이는거 같으므로 여기서는 RestTemplate를 사용해보도록 하겠다.
RestTemplate GET 요청시 headers와 query string을 함께 요청하는 방법에 대해 알아본다. HttpHeaders를 이용해 헤더 값을 지정할 수 있고 이때는 exchange 메서드를 사용해서 요청해야 한다.
query string 을 같이 요청하려면, url string에 직접 추가해줄수도 있겠지만 아무래도 그런것보다는 좀 더 쉽고 보기 좋게 하는 방법이 좋을 거 같아서… 찾아보니 UriComponentsBuilder를 많이 쓰는거 같음.
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(apiUrl)
.queryParam("numOfRows", "100")
.queryParam("pageNo", 1);
log.info("Fetching book data from an external API by using the url: {}", uriBuilder.toUriString());
ResponseEntity<String> response = restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET,
new HttpEntity<>(headers), String.class);
우리가 관심있는 부분은 item 배열과 numOfRows, pageNo, totalCount 이다. 이 부분을 파싱해보도록 하자.
json 라이브러리 중에 jackson과 gson이 있는데 jackson이 스프링에 내장되어있어서 따로 설치가 필요없다는 점 때문에 요즘에는 jackson을 더 많이 사용하는듯하다. (스프링에서 밀어주나?)
해당 포스트에서 첫번째 방법을 사용해서 하나씩 파싱해서 형변환하고 이 작업을 반복해서 내가 원하는 값을 뽑아내고 있는데 코드가 정말… 더럽다. 자바에서 json 파싱은 진짜 너무 어려운거 같다.
이게 아니라면 하나씩 클래스를 만들어서 지정해야 하는데 이건 깔끔한 대신에 클래스 하나하나 다 생성해야 되므로 이것도 단점이 있고.
private List<BookDTO> fetchBookDataFromAPI() throws JsonProcessingException {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", "application/json");
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(apiUrl)
.queryParam("numOfRows", "100")
.queryParam("pageNo", 1);
log.info("Fetching book data from an external API by using the url: {}", uriBuilder.toUriString());
ResponseEntity<String> response = restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET,
new HttpEntity<>(headers), String.class);
// Json parsing 하는 부분... 근데 이게 최선인가 싶네
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> responseObject = objectMapper.readValue(response.getBody(),
new TypeReference<Map<String, Object>>() {});
Map<String, Object> responseProperty = (Map<String, Object>) responseObject.get("response");
Map<String, Object> bodyProperty = (Map<String, Object>) responseProperty.get("body");
Map<String, Object> itemsProperty = (Map<String, Object>) bodyProperty.get("items");
BookDTO[] bookData = objectMapper.readValue(objectMapper.writeValueAsString(itemsProperty.get("item")), BookDTO[].class);
int numOfRows = Integer.parseInt(bodyProperty.get("numOfRows").toString());
int pageNo = Integer.parseInt(bodyProperty.get("pageNo").toString());
int totalCount = Integer.parseInt(bodyProperty.get("totalCount").toString());
return Arrays.asList(bookData);
}
json을 객체로 매핑시키려면 결국 하나씩 클래스를 만들어서 지정해줘야 하는데 이런걸 대신하는 사이트가 있다.
참고 : https://github.com/joelittlejohn/jsonschema2pojo
POJO라는 뜻은 ‘Plain Old Java Object'의 약자이다. json 형태를 넣어주고 원하는 스타일 형식을 넣어주면 그 형식에 맞게 자바 클래스를 생성해준다. 그러면 우리는 이걸 복붙해서 사용하면 되는 것이다.
차라리 이 방식이 더 나은걸수도… 다음에는 이렇게 해봐야지…
깃허브 전체 코드 : https://github.com/ckstn0777/elasticsearch-test