프로젝트를 진행하는데 공공데이터 OpenAPI를 활용할 일이 생겼다.
올해 초에도 공공데이터 OpenAPI를 사용해서 프로젝트를 한 경험이 있는데 요번에 또 사용하게 되었다.
전의 기억을 더듬어 다시 사용해보고 이를 기록하고자한다!
아래의 데이터를 사용
공공데이터는 대표적으로 공공데이터 포털에서 많이 줍줍할 수 있으니 참고하길 바랍니다 ^__^
데이터에 따라 인증키를 요청해야하는 경우도 있고 없는 경우도 있는데,
이번에 사용할 공공데이터는 인증키를 발급받아야한다.
인증키를 야무치게 받았으면 잘 메모해둔다.
OPEN API 정보
인증키를 발급 받고 위의 표에서 사용할 데이터들만 골라본다.
나는 상호명, 업종명, 시군명, 정제도로명주소, 정제지번주소, 상세주소, 영업시간, 위도, 경도 데이터만 받아올 생각이다.
TMI: 위도 경도를 사용해서 카카오맵에서 마커로 띄울 것이기때문!!
준비가 되었다면 Springboot에서 위의 데이터들을 DB에 저장하기 위한 작업을 해볼 것이다.
엔티티
- 공공데이터의 출력명과 동일하게 작성하는 것을 추천한다.
- Getter와 Setter도 열어준다.
Store.java
@Entity
@Getter@Setter
public class Store {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String CMPNM_NM; // 상호명
private String INDUTYPE_NM; // 업종명
private String SIGUN_NM; // 시군명
private String REFINE_ROADNM_ADDR; // 정제도로명주소
private String REFINE_LOTNO_ADDR; // 정제지번주소
private String DETAIL_ADDR; // 상세주소
private String BSN_TM_NM; // 영업시간
private Double REFINE_WGS84_LAT; // 위도
private Double REFINE_WGS84_LOGT; // 경도
}
레포지토리
- 나는 Jpa를 사용하기 때문에 JpaRepository를 extends해줬다.
StoreRepository.java
public interface StoreRepository extends JpaRepository<Store, Long> {
}
서비스 계층
- service 계층에서는 공공데이터를 DB에 저장하는 로직을 구현한다.
@Service
@RequiredArgsConstructor
public class StoreService {
private final StoreRepository goodInfluenceRepository;
private static final Logger logger = LoggerFactory.getLogger(StoreService.class);
@Transactional
public void saveAllStore(List<Store> StoreList) {
for (Store store : StoreList) {
validateStore(store);
}
goodInfluenceRepository.saveAll(StoreList);
logger.info("가게 정보 {}건 저장됨", StoreList.size());
}
private void validateStore(Store store) {
// 필수 필드 체크
if (store.getCMPNM_NM() == null || store.getCMPNM_NM().isEmpty()) {
logger.error("상호명은 필수입니다: {}", store);
throw new IllegalArgumentException("상호명은 필수입니다.");
}
if (store.getINDUTYPE_NM() == null || store.getINDUTYPE_NM().isEmpty()) {
logger.error("업종명은 필수입니다: {}", store);
throw new IllegalArgumentException("업종명은 필수입니다.");
}
}
}
컨트롤러 계층
- 공공데이터 Open API를 json 형식으로 파싱하고 DB에 저장한다.
@RestController
@RequiredArgsConstructor
public class StoreController {
private final StoreService goodInfluenceStoreService;
private static final Logger logger = LoggerFactory.getLogger(StoreController.class);
@GetMapping("/api/Store")
public String callStoreApi() {
StringBuilder result = new StringBuilder();
// API URL 하드코딩
String urlStr = "https://openapi.gg.go.kr/GGGOODINFLSTOREST?KEY=[발급받은 인증키]&Type=json&pIndex=1&pSize=1000";
HttpURLConnection urlConnection = null;
BufferedReader br = null;
try {
// API 요청 설정
URL url = new URL(urlStr);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(10000); // 10초 연결 타임아웃
urlConnection.setReadTimeout(10000); // 10초 읽기 타임아웃
// 응답을 BufferedReader로 읽기
br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
String returnLine;
while ((returnLine = br.readLine()) != null) {
result.append(returnLine).append("\n");
}
// JSON 파싱
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(result.toString());
JsonNode itemsNode = rootNode.path("GGGOODINFLSTOREST").get(1).path("row");
logger.info("Items count: " + itemsNode.size());
List<Store> storeList = new ArrayList<>();
for (JsonNode itemNode : itemsNode) {
Store store = new Store();
// 각 필드에 값 설정
store.setCMPNM_NM(itemNode.path("CMPNM_NM").asText());
store.setINDUTYPE_NM(itemNode.path("INDUTYPE_NM").asText());
store.setSIGUN_NM(itemNode.path("SIGUN_NM").asText());
store.setREFINE_ROADNM_ADDR(itemNode.path("REFINE_ROADNM_ADDR").asText());
store.setREFINE_LOTNO_ADDR(itemNode.path("REFINE_LOTNO_ADDR").asText());
store.setDETAIL_ADDR(itemNode.path("DETAIL_ADDR").asText());
store.setBSN_TM_NM(itemNode.path("BSN_TM_NM").asText());
store.setREFINE_WGS84_LAT(itemNode.path("REFINE_WGS84_LAT").asDouble());
store.setREFINE_WGS84_LOGT(itemNode.path("REFINE_WGS84_LOGT").asDouble());
logger.info("파싱된 가게: " + store);
storeList.add(store);
}
// 데이터베이스에 저장
goodInfluenceStoreService.saveAllStore(storeList);
logger.info("가게 정보가 성공적으로 저장되었습니다.");
} catch (IOException e) {
logger.error("API 호출 중 오류가 발생했습니다: " + e.getMessage());
return "API 호출 중 오류가 발생했습니다: " + e.getMessage();
} finally {
// 자원 정리
if (br != null) {
try {
br.close();
} catch (IOException e) {
logger.error("BufferedReader 닫기 중 오류 발생: " + e.getMessage());
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return "데이터가 성공적으로 저장되었습니다.";
}
}
이렇게 구현을 완료한 뒤
/api/Store 엔드포인트로 Get 요청을 하면 DB에 1000개의 데이터가 저장되는 것을 확인 할 수 있다!!
'개발일지 > Java & Springboot' 카테고리의 다른 글
Spring 과 SpringBoot의 차이점 (0) | 2024.07.02 |
---|---|
[Java] 컬렉션 Collection (0) | 2024.06.15 |
[Java] StringTokenizer 클래스 (0) | 2024.03.08 |
[Java] BufferedReader & BufferedWriter와 Scanner 차이 (수정 중) (0) | 2024.03.07 |
[SpringBoot] @Transactional 이란?? (0) | 2024.02.02 |