💡 개발 환경
Java 17, Spring 3.3.X, Gradle 8.8, MySQL
앞선 게시물에서 서울시 공공데이터를 활용해 openAPI에 데이터를 요청하는 방법을 알아봤다.
이제 요청해 받아온 데이터를 직접 DB에 저장하는 방법을 알아보자.
> ApiServiceImpl.java
...
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class OpenApiService {
private final RawRestaurantRepository rawRestaurantRepository;
private final RestaurantRepository restaurantRepository;
private final ObjectMapper objectMapper;
@Value("${API.KEY}")
private String apiKey; // API 인증키
/**
* @param startIndex 요청 시작 위치
* @param endIndex 요청 종료 위치
*/
@Transactional
public void getRawRestaurants(String startIndex, String endIndex) {
// 공공데이터 요청을 위한 URL 구성
String apiUrl = "http://openapi.seoul.go.kr:8088"; // URL
String responseType = "json"; // 요청파일 타입
String serviceName = "LOCALDATA_072404"; // 서비스명
URI uri = UriComponentsBuilder.fromHttpUrl(apiUrl)
.pathSegment(apiKey, responseType, serviceName, startIndex, endIndex)
.build()
.encode()
.toUri();
// RestTemplate을 사용하여 GET 요청 전송
RestTemplate template = new RestTemplate();
String jsonResponse;
try {
jsonResponse = template.getForObject(uri, String.class);
log.info("JSON 응답:" + jsonResponse);
} catch (RestClientException e) {
log.error("API 요청 실패: {}", uri, e);
return; // 요청 실패 시 메서드 종료
}
// JSON 응답 데이터를 파싱하여 엔티티로 변환
try {
LocalDataResponse localDataResponse = objectMapper.readValue(jsonResponse, LocalDataResponse.class);
List<RawRestaurantResponse> rows = localDataResponse.getLocalData().getRawRestaurantResponses();
// 각 데이터를 원본 맛집 테이블에 저장
for (RawRestaurantResponse raw : rows) {
RawRestaurant restaurant = getRawRestaurantBuilder(raw);
rawRestaurantRepository.save(restaurant);
}
} catch (JsonProcessingException e) {
log.error("JSON 파싱 실패: {}", jsonResponse, e);
}
}
/**
* @param raw 공공데이터의 맛집 응답 데이터
* @return 맛집 응답 데이터가 저장된 원본 맛집 Entity 객체
*/
private RawRestaurant getRawRestaurantBuilder(RawRestaurantResponse raw) {
return RawRestaurant.builder()
.mgtno(raw.getMgtno())
.bplcnm(raw.getBplcnm())
.uptaenm(raw.getUptaenm())
.dtlstategbn(raw.getDtlstategbn())
.sitewhladdr(raw.getSitewhladdr())
.rdnwhladdr(raw.getRdnwhladdr())
.lon(raw.getLon())
.lat(raw.getLat())
.lastmodts(raw.getLastmodts())
.dcbymd(raw.getDcbymd())
.build();
}
}
openAPI로 데이터를 저장하기 위해 알아야 할 것!
- RedisTemplate
: spring 프레임워크에서 지원하는 RedisTemplate 을 활용해 openAPI를 호출하였고, 이는 클라이언트 즉, 현재 시점으로서는 데이터 수집을 원하는 개발자의 요청을 대신 해준다.
위 코드에서처럼 요청 URI 형식을 만들어 데이터를 불러온다.
뿐만 아니라 헤더에 인증키를 넣어야 하는 openAPI의 경우
// RestTemplate을 사용하여 GET 요청 전송
RestTemplate template = new RestTemplate();
String jsonResponse;
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "KakaoAK " + apiKey);
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
try {
jsonResponse = template.exchange(url.toURI(), GET, httpEntity, String.class).getBody();
log.info("JSON 응답:" + jsonResponse);
} catch (RestClientException e) {
// HTTP 요청 관련 예외 처리
log.error("API 요청 실패: {}", url, e);
return; // 요청 실패 시 메서드 종료
}
HttpHeaders를 이용하여 헤더에 값을 넣어 전달하면 된다.
- ObjectMapper
: openAPI에서 응답으로 내려주는 데이터의 형태가 json 이므로 이를 우리 DB에 저장하기 위해서는 자바 객체로 변환하는 로직이 필요하다.
DB에 원본 데이터를 저장했으면, 원하는 형태로 가공하여 사용하면 된다!
공부하면서 느낀점
openAPI를 활용해 프로젝트를 처음 진행해봐서 걱정이 많았는데, 처음이 어렵지 한 번 이해하면 생각보다 재밌는 기능이라는 것을 알게됐다. 다만 까다롭다고 느낀 건 데이터를 받아올 때 해당 json 형태의 객체들을 모두 생성하여 각각 매핑해야한다는 점이었다.
이 json 응답을 보면
가장 상위에는 LOCALDATA_072404 항목이 있고, 그 안에 list_total_count, 응답 코드와 메세지가 담긴 Response, 뽑고자 하는 데이터의 리스트인 row가 있다.
하여, 각각의 항목들을 dto로 생성하여 매핑한 뒤 타고타고 가서 row안의 데이터를 추출했다.
> LocalDataResponse.java
@Getter
@Setter
public class LocalDataResponse { // JSON 응답 데이터를 파싱할 클래스 1
@JsonProperty("LOCALDATA_072404")
private LocalData localData; // 공공데이터 응답 최상위 객체
}
> LocalData.java
@Getter
@Setter
public class LocalData { // JSON 응답 데이터를 파싱할 클래스 2
@JsonProperty("list_total_count")
private int listTotalCount; // 공공데이터 총 데이터 건수
@JsonProperty("RESULT")
private Result result; // 공공데이터 요청결과 객체
@JsonProperty("row")
private List<RawRestaurantResponse> rawRestaurantResponses; // 공공데이터 맛집 응답 객체 리스트
}
> Result.java
@Getter
@Setter
public class Result { // JSON 응답 데이터를 파싱할 클래스 3
@JsonProperty("CODE")
private String code; // 공공데이터 요청결과 코드
@JsonProperty("MESSAGE")
private String message; // 공공데이터 요청결과 메시지
}
> RawRestaurantResponse.java
@Getter
public class RawRestaurantResponse { // JSON 응답 데이터를 파싱할 클래스 4
@JsonProperty("MGTNO")
private String mgtno; // 공공데이터 관리번호
@JsonProperty("DTLSTATEGBN")
private String dtlstategbn; // 공공데이터 상세영업코드
@JsonProperty("BPLCNM")
private String bplcnm; // 공공데이터 사업장명
@JsonProperty("UPTAENM")
private String uptaenm; // 공공데이터 업태구분명
@JsonProperty("DCBYMD")
private String dcbymd; // 공공데이터 폐업일자
@JsonProperty("SITEWHLADDR")
private String sitewhladdr; // 공공데이터 지번주소
@JsonProperty("RDNWHLADDR")
private String rdnwhladdr; // 공공데이터 도로명주소
@JsonProperty("LASTMODTS")
private String lastmodts; // 공공데이터 최종 수정일자
@JsonProperty("X")
private String lon; // 공공데이터 경도
@JsonProperty("Y")
private String lat; // 공공데이터 위도
}
참고한 블로그
- [API] URL을 통한 클라이언트 - 서버 통신 과정 : URL 구성부터 URLConnection과 HttpURLConnection의 API 사용법까지
- [SpringBoot+JPA] 공공데이터 db저장과 API만들기-1
- Open API와 통신하여 Spring 서버 개발하기
'Spring & SpringBoot' 카테고리의 다른 글
[Java/SpringBoot] 로그인 시 JWT 인증토큰, RefreshToken 발행하기 (2) | 2024.09.01 |
---|---|
[Java/Springboot] OpenAPI를 활용해 데이터 수집하기_(1) (0) | 2024.08.31 |
Java/SpringBoot 게시판 기능 구현_Querydsl을 활용한 카테고리별 조회, 필터링 (0) | 2023.10.19 |
Java/SpringBoot 게시판 기능 구현_Token 관련 예외 처리 (0) | 2023.07.05 |
SpringBoot 게시판_이미지 대표이미지 출력 (0) | 2023.05.12 |