개요
전 직장 이사님이 논문을 준비하시는데 공공데이터 자료가 필요하시다고 하셔서 조회 후 저장하는 작업을 해보았습니다.
공공데이터는 https://www.data.go.kr/ 에서 API 신청 후 serviceKey를 지급받아 REST로 요청하면 됩니다. 응답값은 XML 또는 JSON 이지만, 논문에 사용할 자료라서 CSV로 변환하였습니다. 응답포멧은 조금 다르지만, 처리로직은 거의 동일하여http://www.smartfarmkorea.net/ 의 데이터도 처리해봤습니다.
공공데이터를 사용함에 있어서 가장 큰 문제점은.. 명세서가 hwp 포멧이여서 문서 확인이 힘들었습니다.
토마토 생산량
REST로 API 호출하고, 응답값인 JSON을 Parsing하여 Item만 취하고, 이를 CSV로 변환하는 코드를 작성해 봤습니다.

해당 API는 농가 코드를 입력받아 생산량을 응답합니다.
Request / Response는 Pagenation 되어 있지만, 귀찮아서 1000개 정도씩 요청하고 1 Page의 정보만 취합했습니다.
package kr.kimstar.opendata;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import kr.kimstar.opendata.constant.SmartFarmConst;
import kr.kimstar.opendata.dto.DataGoKrResponse;
import kr.kimstar.opendata.dto.Item;
import kr.kimstar.opendata.dto.TomatoItem;
import kr.kimstar.opendata.helper.APIConnector;
import kr.kimstar.opendata.helper.FileWriter;
import kr.kimstar.opendata.helper.JsonHelper;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.util.*;
public class Tomato {
public static void main(String[] args) throws IOException {
// request
List<Item> items = new ArrayList<>();
for (String code : SmartFarmConst.SEARCH_FRMHS_CODE_LIST) {
List<Item> resultItems = fetchItems(code);
items.addAll(resultItems);
}
if (items.size() == 0) {
return;
}
// save
List<String> csvItems = new ArrayList<>();
csvItems.add(items.get(0).getTitle());
items.forEach(i -> csvItems.add(i.getCSV()));
FileWriter.write(csvItems, "C:\\tomato.csv");
}
private static List<Item> fetchItems(String frmhsCode) {
try {
int pageSize = 1000;
String apiUrl = "http://apis.data.go.kr/1390000/SmartFarmDATA/prddatarqst";
Map<String, String> params = new HashMap<>();
params.put("searchFrmhsCode", frmhsCode);
params.put("pageNo", "1");
params.put("pageSize", String.valueOf(pageSize));
params.put("returnType", "json");
params.put("serviceKey", "블라블라블라");
String response = APIConnector.fetch(apiUrl, params);
JsonObject jsonObj = JsonHelper.convert(response);
// JsonHelper.printJson(jsonObj);
Type type = new TypeToken<DataGoKrResponse<TomatoItem>>() {}.getType();
DataGoKrResponse dto = (new Gson()).fromJson(jsonObj, type);
if (!"OK".equalsIgnoreCase(dto.getResponse().getHeader().getResultMsg())) {
return Collections.EMPTY_LIST;
}
if (dto.getResponse().getBody().getTotalCount() > pageSize) {
System.out.println("!!!!!!!!! totalCount is " + dto.getResponse().getBody().getTotalCount());
JsonHelper.printJson(jsonObj);
}
Thread.sleep(100);
return dto.getResponse().getBody().getItems().getItem();
} catch (Exception e) {
return Collections.EMPTY_LIST;
}
}
}
REST 로 호출하기 위해 HttpURLConnection 를 사용하였습니다. 지금 보니 try-with-resource 형태로 수정이 필요해 보이네요. 한글이 포함된 정보가 있어서 UTF-8로 처리하였습니다.
package kr.kimstar.opendata.helper;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
public final class APIConnector {
private static final String ENC_UTF8 = "UTF-8";
public static String fetch(String apiUrl, Map<String, String> params) throws IOException {
// request
URL url = new URL(buildUrl(apiUrl, params));
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-type", "application/json");
System.out.println("Response code: " + conn.getResponseCode());
// response
BufferedReader reader;
if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
}
// result
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
conn.disconnect();
return sb.toString();
}
private static String buildUrl(String apiUrl, Map<String, String> params) throws UnsupportedEncodingException {
if (StringUtils.isEmpty(apiUrl)) {
return StringUtils.EMPTY;
}
StringBuilder sb = new StringBuilder(apiUrl);
for (String key : params.keySet()) {
sb.append("&" + URLEncoder.encode(key, ENC_UTF8) + "=" + params.get(key));
}
return sb.toString().replaceFirst("&", "?");
}
}
데이터는 JSON 형태로 처리하였으며, Parsing 및 Mapping을 위해 Gson 을 사용하였습니다.
package kr.kimstar.opendata.helper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public final class JsonHelper {
public static JsonObject convert(String jsonString) {
return JsonParser.parseString(jsonString).getAsJsonObject();
}
public static void printJson(JsonObject jsonObject) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(jsonObject));
}
}
data.or.kr 의 데이터들은 표준화된 포멧을 사용하고 items 내용만 다르므로, Generic으로 item을 처리하여, 다양한 응답값을 처리할 수 있도록 하였습니다.
@Getter
@Setter
@ToString
public class DataGoKrResponse<T> {
private Response<T> response;
private String resultCode;
private String resultMessage;
}각 Item은 CSV 생성시 사용할 title과 value 값들을 응답하는 method를 정의하였습니다.
package kr.kimstar.opendata.dto;
public interface Item {
String getTitle();
String getCSV();
}@Getter
@Setter
@ToString
public class TomatoItem implements Item {
private String frmYear;
private String frmMonth;
private String frmAr;
private String frmhsId;
private String outtrn;
private String frmWeek;
@Override
public String getTitle() {
return "frmYear,frmMonth,frmAr,frmhsId,outtrn,frmWeek";
}
@Override
public String getCSV() {
StringBuilder sb = new StringBuilder();
sb.append(frmYear + ",");
sb.append(frmMonth + ",");
sb.append(frmAr + ",");
sb.append(frmhsId + ",");
sb.append(outtrn + ",");
sb.append(frmWeek);
return sb.toString();
}
}처리결과는 다음과 같이 CSV 파일로 저장됩니다.
frmYear,frmMonth,frmAr,frmhsId,outtrn,frmWeek
2017,8,3400.0,9,0.0,34
2017,8,3400.0,9,0.0,35
2017,9,3400.0,9,0.0,36
2017,9,3400.0,9,0.0,37



