본문 바로가기
Spring/ETC

공공데이터포털 API 데이터를 DB로 가져오는 법

by pms93 2023. 1. 16.

공공데이터포털API의 데이터를 DB에 저장하는 방법에 대해서 알아보도록 한다.

API마다 요청경로가 각각 다르며 활용신청하여 서비스키를 받은 후 하단 사진과 같이 진행하면 된다.

(활용신청 즉시 바로 활용 가능한 데이터가 있는 반면 시간이 조금 걸리는 데이터도 있으며 트래픽 제한 또한 제공하는 데이터마다 다르므로 확인해야 한다.)

 

JSP 생성 후 버튼 클릭시 getData로 요청을 하게끔 설정했다.

package com.ms.data.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ms.data.DTO.FacilityDTO;
import com.ms.data.service.FacilityService;

@Controller
public class HomeController {
	
	@Autowired private FacilityService svc;
	
	@RequestMapping("home")
	public void home() {}
	
	@GetMapping("getData")
	public void getData() throws IOException, ParseException {
		int pageNo = 1;
		
		while (true) {
			// java를 통해 요청할 경로를 path변수에 초기화한다.
			// pageNo는 필자가 사용하는 API의 필수요청 파라미터이며 while문을 통해 데이터가 끝나는 시점까지 받기 위해 선언한 변수다.
			String path = 
				"http://apis.data.go.kr/B551011/KorService/areaBasedList?" + 
				"serviceKey=sjZvXvdQDKdHPWHE4Dvpln53BiU6rRHxTl3iFuP9t5L5qrWYFtbK65ogD5iUEjhBMNQa5ueqcaqyw9eNI%2B8mwg%3D%3D" +
				"&cat1=C01" + 
				"&pageNo=" + pageNo + 
				"&numOfRows=1000" + 
				"&MobileApp=AppTest" + 
				"&MobileOS=ETC" + 
				"&_type=json";

			// url객체를 인스턴스 후 기본 생성자의 인자값으로 위에서 초기화한 path변수를 넣어준다.
			URL url = new URL(path);
			
			// 요청한 URL과 연결을 맺는다.
			URLConnection uc = url.openConnection();
			
			// 요청한 URL의 결과를 BufferedReader클래스의 변수에 저장한다.
			// InputStreamReader클래스에 위에서 선언한 uc변수의 .getInputStream()메서드를 인자값으로 담아준다.
			BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));

			// JSON작업을 하기 위해 Library를 Dependency에 추가해야 한다.
			// 필자는 json.simple Library를 사용한다.
			// 아래 response, body, items, item은 필자가 사용하는 API의 데이터 반환형태에 따라 선언한 변수이며
			// 필요한 변수는 해당API에 맞게 선언하면 된다.
			JSONParser jp = new JSONParser();
			JSONObject jo, response, body, items;
			JSONArray item = new JSONArray();

			while (true) {
				// URL요청을 통해 반환받은 데이터를 .readLine()메서드를 통헤 한줄씩 읽어올 수 있다.
				String data = br.readLine();
				if (data == null) {
					// URL요청을 통해 반환받은 데이터가 null일 경우
					// 1) 잘못된 요청  2) 데이터 끝
					// 이므로 while문을 중지시킨다.
					break;
				}

				try {
					/*
					 { 
					 "response": {
					 	"header": { "resultCode":"0000","resultMsg":"OK" },
					 	"body": { 
					 		"items": { 
					 			"item": [ 
					 						{ 
							 					"addr1":"서울 송파구 올림픽로 300¸ 2층", "addr2":"",
								 				"areacode":"1", "booktour":"", "cat1":"A04", "cat2":"A0401",
								 				"cat3":"A04011000", "contentid":"2924134", "contenttypeid":"38",
								 				"createdtime":"20221030182739", "firstimage":
								 				"http://tong.visitkorea.or.kr/cms/resource/00/2879100_image2_1.jpg",
								 				"firstimage2":
								 				"http://tong.visitkorea.or.kr/cms/resource/00/2879100_image3_1.jpg",
								 				"mapx":"127.1040305171","mapy":"37.5142459111", "mlevel":"6",
								 		  		"modifiedtime":"20221128170255", "readcount":0,"sigungucode":"18", "tel":"",
								 				"title":"가가밀라노 롯데백화점 에비뉴엘 월드타워점", "zipcode":"05551" 
						 			  		} 
						 			  	]
						 			  },
						 		"numOfRows":1,"pageNo":1,"totalCount":50271
						 		 }
						  		  }
					 }
					 
					 response -> body -> items -> item 내에 있는 데이터를 가져오기 위해서 아래와 같은 작업을 진행한다.
					 1) parse를 통해 (String)data를 (JSONObject)data로 형변환 한다.
					 2) jo내의 response의 value를 response에 담는다.
					 3) response내의 body의 value를 body에 담는다.
					 4) body내의 items의 value를 items에 담는다.
					 5) item은 중괄와 대괄호로 함께 묶인 JSONArray와 똑같은 형태로 반환받았으므로 JSONArray로 casting하여 담는다.
					 
					 */
					jo = (JSONObject) jp.parse(data);
					response = (JSONObject) jo.get("response");
					body = (JSONObject) response.get("body");
					items = (JSONObject) body.get("items");
					item = (JSONArray) items.get("item");
				} catch (Exception e) { 
					return; 
				}
				
				
				// 현재 item변수에 arrayList형태로 데이터가 담겨있다. for문과 JSONObject를 통해 데이터를 가공한다.
				// 준비된 DTO의 맴버필드들은 String자료형이므로 String으로 형변 환 후 setter를 통해 초기화한다.
				// 이후 service -> repository로 작업하면 된다.
				JSONObject tmp = new JSONObject();
				for (int cnt = 0; cnt < item.size(); cnt++) {
					tmp = (JSONObject) item.get(cnt);

					FacilityDTO fd = new FacilityDTO();
					fd.setAddr1((String) tmp.get("addr1"));
					fd.setAddr2((String) tmp.get("addr2"));
					fd.setAreaCode((String) tmp.get("areacode"));
					fd.setCat1((String) tmp.get("cat1"));
					fd.setCat2((String) tmp.get("cat2"));
					fd.setCat3((String) tmp.get("cat3"));
					fd.setContentId((String) tmp.get("contentid"));
					fd.setContentTypeId((String) tmp.get("contenttypeid"));
					fd.setCreatedTime((String) tmp.get("createdtime"));
					fd.setFirstImage((String) tmp.get("firstimage"));
					fd.setFirstImage2((String) tmp.get("firstimage2"));
					fd.setMapX((String) tmp.get("mapx"));
					fd.setMapY((String) tmp.get("mapy"));
					fd.setmLevel((String) tmp.get("mlevel"));
					fd.setModifiedTime((String) tmp.get("modifiedtime"));
					fd.setSigunguCode((String) tmp.get("sigungucode"));
					fd.setTel((String) tmp.get("tel"));
					fd.setTitle((String) tmp.get("title"));
					fd.setZipCode((String) tmp.get("zipcode"));

					System.out.println("동작_controller");
					svc.getData(fd);
					System.out.println("DataCount : " + cnt);
				}
			}
			
			pageNo++;
			}
		}
	
	
	
	
}

 

Mybatis
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace = "com.ms.data.DAO.FacilityDAO">
	<insert id = "getData" parameterType = "com.ms.data.DTO.FacilityDTO">
		INSERT INTO Facilities
		VALUES (
			#{ addr1 }, 
			#{ addr2 }, 
			#{ areaCode },
			#{ cat1 },
			#{ cat2 },
			#{ cat3 },
			#{ contentId },
			#{ contentTypeId },
			#{ createdTime },
			#{ firstImage },
			#{ firstImage2 },
			#{ mapX },
			#{ mapY },
			#{ mLevel },
			#{ modifiedTime },
			#{ sigunguCode },
			#{ tel },
			#{ title },
			#{ zipCode }
		)
	</insert>
</mapper>