본문 바로가기

Programming/Java

[Java] 공공데이터포털 Open API 사용하기 - 동네예보 (1)

반응형

공공데이터포털(www.data.go.kr)은 공공기관이 생성 또는 취득하여 관리하고 있는 다양한 종류의 공공데이터를 한 곳에 모아서 사용자들에게 제공하는 행정안전부 산하 포털사이트이다. 

 

이번 포스팅에서는 공공데이터포털의 서비스 중 동네예보조회 서비스를 이용하여 우리 동네의 날씨를 조회할 수 있는 프로그램을 만들어보기로 한다.

 

0. 준비물 : Internet Explorer

크롬에서는 일부 기능이 지원이 안된다. (빠직)

공공데이터포털에 접속할 때는 IE를 사용하자.

 

1. 서비스 신청

공공데이터포털에 접속하여 회원가입을 한다.

공인인증서등록 란이 있는데 공인인증서는 로그인 할 때 필요하다고 하는데 비밀번호로 로그인해도 본 포스팅에서 소개하는 기능을 사용하는데에는 문제 없으므로 생략해도 좋다.

회원가입 완료후 메인 페이지 검색창에 '동네예보정보조회서비스'를 검색한다.

 

 

'동네예보정보조회서비스'를 클릭한다.

 

 

 

(신)동네예보정보조회서비스 의 '활용신청'을 클릭한다.

 

 

 

 

OpenAPI를 사용하려면 공공데이터포털에 본 API를 활용하려는 시스템의 유형과 활용목적을 알려주어야한다.

본 포스팅에서는 시스템 유형을 일반, 활용목적을 앱개발 로 하였다.

동네예보정보조회서비스에서는 4가지 기능을 제공하는데 우리는 동네예보조회 서비스만 사용한다.

 

라이센스표시에 동의합니다를 체크하고 신청 버튼을 클릭하면 신청이 완료된다.

 

 

위와같은 메시지 박스가 뜨며 확인을 누르면 자동으로 신청내역 페이지로 넘어간다.

 

 

 

 

 

사용자의 계정에 할당된 서비스는 발급일로부터 2년동안 사용 가능하다.

동네예보정보조회서비스는 REST API이다. HTTP 프로토콜을 통해 EndPoint로 GET method를 호출할 때 인증키를 헤더에 포함시키고 형식에 맞게 파라미터를 전달하면 json으로 예보 정보를 반환하도록 되어있다.

REST API를 잘 알고 있다면 위 이야기가 이해가 되겠지만 REST에 대한 이해가 부족하더라도 본 포스팅을 따라오는데에는 문제 없다.

 

 

2. 기상청 동네예보 좌표 얻기

위 화면에서 참고문서 란에 첨부된 압축파일을 열어보면 "OpenAPI 사용자 활용가이드(기상청_신규 동네예보정보조회서비스)_v1.3.hwp", "동네예보조회서비스_격자_위경도.xlsx" 두개의 파일이 있다.

기상청에서는 지역별 예보의 편의를 위해 자체적으로 전국 지역별 좌표를 지정하였다. 예보를 조회하고 싶은 지역의 동네예보 좌표를 얻으려면 3가지 방법이 있다.

1) "동네예보조회서비스_격자_위경도.xlsx" 에서 찾기

2) "OpenAPI 사용자 활용가이드(기상청_신규 동네예보정보조회서비스)_v1.3.hwp" 의 맨 마지막 소스코드를 컴파일하여 취득하기

3) 기상청에서 제공하는 지역 코드 API URL 사용하기

 

본 포스팅에서는 3번 방법을 따라 지역코드를 얻어 좌표를 취득하는 클래스를 먼저 만들 것이다.

우선 서울특별시 서초구 반포1동 의 동네예보 좌표를 찾아보기로 한다.

 

http://www.kma.go.kr/DFSROOT/POINT/DATA/top.json.txt

 

위 URL에 접속하면 다음과 같이 나온다.

[{"code":"11","value":"서울특별시"},{"code":"26","value":"부산광역시"},
{"code":"27","value":"대구광역시"},{"code":"28","value":"인천광역시"},
{"code":"29","value":"광주광역시"},{"code":"30","value":"대전광역시"},
{"code":"31","value":"울산광역시"},{"code":"41","value":"경기도"},
{"code":"42","value":"강원도"},{"code":"43","value":"충청북도"},
{"code":"44","value":"충청남도"},{"code":"45","value":"전라북도"},
{"code":"46","value":"전라남도"},{"code":"47","value":"경상북도"},
{"code":"48","value":"경상남도"},{"code":"50","value":"제주특별자치도"}]

만약 위와같이 나오지 않는다면, 이 포스팅을 참고하면 된다.

 

기상청의 지역코드는 top : 시도단위 / mdl : 시구군단위 / leaf : 동읍면리단위 이렇게 3단계로 나누어서 지역코드를 부여하고 이를 json형태로 제공한다. 그리고 leaf의 값에 동네예보 좌표가 있다.

이를 정리하면 아래 사진과 같다.

 

 

따라서 서울특별시 서초구 반포1동의 지역코드를 얻기 위해서는 아래와 같이 3단계를 거치면된다.

1) http://www.kma.go.kr/DFSROOT/POINT/DATA/top.json.txt 에서 "서울특별시" 에 해당하는 지역코드(11)를 얻는다.

2) http://www.kma.go.kr/DFSROOT/POINT/DATA/mdl.11.json.txt 에서 "서초구"에 해당하는 지역코드(11650)를 얻는다.

3) http://www.kma.go.kr/DFSROOT/POINT/DATA/leaf.11650.json.txt 에서 "반포1동"에 해당하는 동네예보 좌표(x=60,y=125)를 얻는다.

 

위 알고리즘대로 동네예보 좌표를 구하는 기능의 클래스 LocationCodeFetcher를 java로 구현해보자.

json parsing 을 위해서는 외부 라이브러리가 필요하다.

json-simple-1.1.1.jar
다운로드

위 jar 파일을 java 프로젝트의 build path에 포함시켜주면 된다.

그리고 해당 프로그램에 사용되는 모든 Json 파싱 처리를 담당하는 JakeJsonParser 클래스도 구현하였다.

 

/* JakeJsonParser.java */
package pkgWeatherMonitor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

public class JakeJsonParser {
    private JakeJsonParser() {}
    private static class IODHI {
        private static final JakeJsonParser instance = new JakeJsonParser();
    }
    public static JakeJsonParser getInstance() {
        return IODHI.instance;
    }

    public JSONArray getRemoteJSONArray(String url) {
        StringBuffer jsonHtml = new StringBuffer();
        try {
            URL u = new URL(url);
            InputStream uis = u.openStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(uis, "UTF-8"));

            String line = null;
            while ((line = br.readLine()) != null) {
                jsonHtml.append(line + "\r\n");
            }
            br.close();
            uis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        JSONArray jsonArr = (JSONArray) JSONValue.parse(jsonHtml.toString());
        return jsonArr;
    }

    public JSONArray getWeatherJSONArray(String url) {
        StringBuffer jsonHtml = new StringBuffer();
        JSONArray jsonArr = null;
        JSONObject jsonObj = null;
        String[] saAttribName = { "response", "body", "items" };
        try {
            URL u = new URL(url);
            InputStream uis = u.openStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(uis, "UTF-8"));
            String line = null;
            while ((line = br.readLine()) != null) {
                jsonHtml.append(line + "\r\n");
            }
            br.close();
            uis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        System.out.println(jsonHtml.toString());

        jsonObj = (JSONObject) JSONValue.parse(jsonHtml.toString());
        for (int i = 0; i < saAttribName.length; i++)
            jsonObj = (JSONObject) jsonObj.get(saAttribName[i]);
        jsonArr = (JSONArray) jsonObj.get("item");

        return jsonArr;
    }

    public Map<String, String> getJsonSubMap(JSONArray jsonArrSource) {
        Map<String, String> jsonMap = new LinkedHashMap<String, String>();

        // 기상청 API 에서 받아오는 JSON은 code:value형식만 갖추므로
        // 이 Algorithm으로 일관되게 mapping할 수 있음
        // (leaf단에서는 x,y값이 추가로 들어가므로 다른 method사용해야함)
        for (int i = 0; i < jsonArrSource.size(); i++) {
            JSONObject jsonObjItem = (JSONObject) jsonArrSource.get(i); // JSONArray에서 JSONObject하나씩 가져옴
            String code = (String) jsonObjItem.get("code"); // JSONObject에서 key, value 가져옴
            String value = (String) jsonObjItem.get("value");
            jsonMap.put(value, code); // 지역이름으로 code를 알아내길 원하므로 K,V를 바꿔서 mapping
        }
        return jsonMap;
    }

    public Map<String, Coord> getJsonLeafMap(JSONArray jsonArrSource) {
        Map<String, Coord> jsonMap = new LinkedHashMap<String, Coord>();
        for (int i = 0; i < jsonArrSource.size(); i++) {
            JSONObject jsonObjItem = (JSONObject) jsonArrSource.get(i);
            String value = (String) jsonObjItem.get("value");
            String x = (String) jsonObjItem.get("x");
            String y = (String) jsonObjItem.get("y");
            jsonMap.put(value, new Coord(x, y));
        }
        return jsonMap;
    }
}
/* LocationCodeFetcher.java */
package pkgWeatherMonitor;

import java.util.Map;

import org.json.simple.JSONArray;

public class LocationCodeFetcher {
    private final String sUrlDef = "http://www.kma.go.kr/DFSROOT/POINT/DATA/";
    private final String sUrlMdlHead = "mdl.";
    private final String sUrlLeafHead = "leaf.";
    private final String sUrlTail = ".json.txt";
    private Map<String, String> mapTop;
    private Map<String, String> mapMdl;
    private Map<String, Coord> mapLeaf;

    private JakeJsonParser jjp;

    public LocationCodeFetcher() {
        jjp = JakeJsonParser.getInstance();
    }

    private String getStrUrl(String s) {
        if (s.equals("top"))
            return sUrlDef + "top" + sUrlTail;
        else
            return sUrlDef;
    }

    private String getStrUrl(String s, String code) {
        String tmp = null;
        if (s.equals("mdl"))
            tmp = sUrlMdlHead;
        else if (s.equals("leaf"))
            tmp = sUrlLeafHead;
        return sUrlDef + tmp + code + sUrlTail;
    }

    public Coord fetchLocationCode(String[] saLocation) { // 인자: {시군구, 시도, 동면읍}
        JSONArray jsonArrTop = null;
        JSONArray jsonArrMdl = null;
        JSONArray jsonArrLeaf = null;

        jsonArrTop = jjp.getRemoteJSONArray(getStrUrl("top"));
        mapTop = jjp.getJsonSubMap(jsonArrTop);
        jsonArrMdl = jjp.getRemoteJSONArray(getStrUrl("mdl", mapTop.get(saLocation[0])));
        mapMdl = jjp.getJsonSubMap(jsonArrMdl);
        jsonArrLeaf = jjp.getRemoteJSONArray(getStrUrl("leaf", mapMdl.get(saLocation[1])));
        mapLeaf = jjp.getJsonLeafMap(jsonArrLeaf);
        return mapLeaf.get(saLocation[saLocation.length - 1]);
    }

    public Map<String, String> getMapTop() {
        return mapTop;
    }

    public Map<String, String> getMapMdl() {
        return mapMdl;
    }

    public Map<String, Coord> getMapLeaf() {
        return mapLeaf;
    }
}
/* Coord.java */
package pkgWeatherMonitor;

public class Coord {
    private String sx;
    private String sy;
    public Coord(String x, String y){
        sx = x;
        sy = y;
    }
    public String getSx() {
        return sx;
    }
    public void setSx(String sx) {
        this.sx = sx;
    }
    public String getSy() {
        return sy;
    }
    public void setSy(String sy) {
        this.sy = sy;
    }
}

 

반응형