<Tutorial> OPEN API 로 날씨 기능 구현하기

kukjunLEE·2022년 3월 17일
1

Tutorial

목록 보기
1/2

1. 사용한 Open API


공공 데이터 포털에 있는 기상청_단기예보 조회 서비스를 사용.
기상청 단기예보 조회서비스 링크


먼저 사용하기 전에 활용 신청을 통해서 해당 API의 서비스 키를 받아와야 함.

4가지 기능중에 단기예보조회를 사용해보았고 이를 쉽게 사용하는 방법에 대해 설명.

필요한 파라미터

  • url
  • key
  • x
  • y
  • baseDate
  • baseTime

요청 방식

  • HTTP GET 요청

API 사용 테스트

연결 절차 후 문자열로 변환하는 과정 정리

  1. URL을 만들어서 GET 요청문 작성
  2. URL을 기반으로 Connection 생성
  3. Connection 세부 설정
  4. BufferReaderInputStreamConnectionInputStream으로 등록
  5. BufferReader로 버퍼에 있는 값들을 읽어서 StringBuilder를 통해 문자열로 변환

사용하는 코드

APITest.java

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class APITest {
    public static void main(String[] args) throws Exception {

        // 변수 설정
        String apiURL = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
        String authKey = ""; // 본인 서비스 키

		// 구하고자 하는 시간과 좌표 대입
        String nx = "69";
        String ny = "100";
        String baseDate = "20220317";
        String baseTime = "1200";

        String dataType = "JSON";

        StringBuilder urlBuilder = new StringBuilder(apiURL);
        urlBuilder.append("?" + URLEncoder.encode("serviceKey", "UTF-8") + "=" + authKey);
        urlBuilder.append("&" + URLEncoder.encode("numOfRows=10", "UTF-8"));    // 표 개수
        urlBuilder.append("&" + URLEncoder.encode("pageNo=1", "UTF-8"));    // 페이지 수
        // JSON 형식으로 반환을 원하면 주석 제거
        // urlBuilder.append("&" + URLEncoder.encode("dataType", "UTF-8") + "=" + URLEncoder.encode(dataType, "UTF-8")); // 받으려는 타입
        urlBuilder.append("&" + URLEncoder.encode("base_date", "UTF-8") + "=" + URLEncoder.encode(baseDate, "UTF-8")); // 조회하고 싶은 날짜
        urlBuilder.append("&" + URLEncoder.encode("base_time", "UTF-8") + "=" + URLEncoder.encode(baseTime, "UTF-8")); // 조회하고싶은 시간
        urlBuilder.append("&" + URLEncoder.encode("nx", "UTF-8") + "=" + URLEncoder.encode(nx, "UTF-8")); // x좌표
        urlBuilder.append("&" + URLEncoder.encode("ny", "UTF-8") + "=" + URLEncoder.encode(ny, "UTF-8")); // y좌표

        URL url = new URL(urlBuilder.toString());
        System.out.println(url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-type", "application/json");
        System.out.println("Response code: " + conn.getResponseCode());
        BufferedReader rd;
        if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        rd.close();
        conn.disconnect();
        String result = sb.toString();

		// 테스트를 위해 출력
        System.out.println(result);

    }
}

결과 분석

  • XML 형태로 값을 제공해주는데, 이 정보를 사용하려면 XML에서 파싱을 통해 원하는 값을 추출해낼 수 있어야 한다.



2. Open API에서 얻은 XML, JSON 파싱해서 원하는 값 얻기


해당하는 VO 객체 만들기

값의 이름으로 가질 수 있는 것은 PTY, REH, RN1, T1H, UUU, VEC, VVV, WSD으로 총 8가지이다. 한번 api를 호출하면, 8개의 값을 얻을 수 있으므로 이를 쉽게 사용할 수 있는 Weather VO 객체를 만든다.

package main.java.vo;


public class Weather {
    private int locationCode;
    private String date;
    private String time;
    private double PTY;
    private double REH;
    private double RN1;
    private double T1H;
    private double UUU;
    private double VVV;
    private double VEC;
    private double WSD;

	... getter setter

    @Override
    public String toString() {
        return "locationCode = " + locationCode +
                "\ndate = " + date +
                "\ntime = " + time +
                "\nPTY = " + PTY +
                "\nREH = " + REH +
                "\nRN1 = " + RN1 +
                "\nT1H = " + T1H +
                "\nUUU = " + UUU +
                "\nVEC = " + VEC +
                "\nVVV = " + VVV +
                "\nWSD = " + WSD;
    }
}




XML 파싱해서 원하는 값 얻기

XML 파싱해서 원하는 값을 얻으려면 org.w3c.dom 라이브러리의 객체를 사용해야 한다.

  • 해당 라이브러리는 jdk11 버전에 내장되어 있다.

  • 만들어지는 XML 값의 일부를 보면, item 내부의 category가 값의 이름이고, obsrValue가 그 이름에 해당하는 값임을 알 수 있다.
  • category로 나올 수 있는 값이 정해져 있으므로, 미리 열거형을 사용해서 정의해놓고 switch문으로 처리함.
package main.java;

import main.java.vo.Weather;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.management.modelmbean.XMLParseException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class XMLIntegrateTest {

	// 열거형으로 정의 후 사용
    enum WeatherValue {
        PTY, REH, RN1, T1H, UUU, VEC, VVV, WSD
    }

    public static void main(String[] args) throws Exception {

        // 입력받을 weather 객체
        Weather weather = new Weather();

        // 변수 설정
        String apiURL = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
        String authKey = ""; // 본인 Service 키 등록

        String nx = "69";
        String ny = "100";
        String baseDate = "20220310";
        String baseTime = "1800";

        StringBuilder urlBuilder = new StringBuilder(apiURL);
        urlBuilder.append("?" + URLEncoder.encode("serviceKey", "UTF-8") + "=" + authKey);
        urlBuilder.append("&" + URLEncoder.encode("numOfRows=10", "UTF-8"));    // 숫자 표
        urlBuilder.append("&" + URLEncoder.encode("pageNo=1", "UTF-8"));    // 페이지 수
        urlBuilder.append("&" + URLEncoder.encode("base_date", "UTF-8") + "=" + URLEncoder.encode(baseDate, "UTF-8")); /* 조회하고싶은 날짜*/
        urlBuilder.append("&" + URLEncoder.encode("base_time", "UTF-8") + "=" + URLEncoder.encode(baseTime, "UTF-8")); /* 조회하고싶은 시간 AM 02시부터 3시간 단위 */
        urlBuilder.append("&" + URLEncoder.encode("nx", "UTF-8") + "=" + URLEncoder.encode(nx, "UTF-8")); //경도
        urlBuilder.append("&" + URLEncoder.encode("ny", "UTF-8") + "=" + URLEncoder.encode(ny, "UTF-8")); //위도

        URL url = new URL(urlBuilder.toString());
        System.out.println(url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-type", "application/json");
        System.out.println("Response code: " + conn.getResponseCode());
        BufferedReader rd;
        if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        rd.close();
        conn.disconnect();
        String result = sb.toString();

        System.out.println(result);

        // 문자열 Document 로 변경해서 List 형태로 가져와서 객체에 파싱함.
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        InputSource is = new InputSource(new StringReader(result));
        Document document = db.parse(is);

        try {
            document.getDocumentElement().normalize();
            System.out.println("Root Element :" + document.getDocumentElement().getNodeName());
            NodeList nList = document.getElementsByTagName("item");
            System.out.println("--------------------------");
            for (int temp = 0; temp < nList.getLength(); temp++) {
                Node nNode = nList.item(temp);
                // 현재 Element 확인하려면 아래 주석 해제
//                System.out.println("\nCurrent Element :" + nNode.getNodeName());
                if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element eElement = (Element) nNode;

                    String category = eElement.getElementsByTagName("category").item(0).getTextContent();
                    double value = Double.parseDouble(eElement.getElementsByTagName("obsrValue").item(0).getTextContent());

                    WeatherValue weatherValue = WeatherValue.valueOf(category);
                    // 변환한 값이 몇번째인지 확인하려면 아래 주석 해제
//                    System.out.println(WeatherValue.valueOf(category).ordinal());

                    switch (weatherValue) {
                        case PTY:
                            weather.setPTY(value);
                            break;
                        case REH:
                            weather.setREH(value);
                            break;
                        case RN1:
                            weather.setRN1(value);
                            break;
                        case T1H:
                            weather.setT1H(value);
                            break;
                        case UUU:
                            weather.setUUU(value);
                            break;
                        case VEC:
                            weather.setVEC(value);
                            break;
                        case VVV:
                            weather.setVVV(value);
                            break;
                        case WSD:
                            weather.setWSD(value);
                            break;
                        default:
                            throw new XMLParseException();
                    }
                }
            }

            System.out.println(weather.toString());
        } catch (XMLParseException e) {
            e.printStackTrace();
        }
    }

}



JSON을 파싱해서 원하는 값 얻기

JSON을 파싱해서 원하는 값을 얻으려면 org.json 라이브러리의 객체를 사용해야 한다.

  • 해당 라이브러리는 외부에서 다운로드를 하거나, maven, gradle에서 라이브러리 의존 설정에 추가해주어야 한다.

  • 만들어지는 XML 값의 일부를 보면, item 내부의 category가 값의 이름이고, obsrValue가 그 이름에 해당하는 값임을 알 수 있다.
  • category로 나올 수 있는 값이 정해져 있으므로, 미리 열거형을 사용해서 정의해놓고 switch문으로 처리함.
package main.java;

import main.java.vo.Weather;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

public class JSONIntegrateTest {

    enum WeatherValue {
        PTY, REH, RN1, T1H, UUU, VEC, VVV, WSD
    }

    public static void main(String[] args) throws Exception {

        // 입력받을 weather 객체
        Weather weather = new Weather();
        // 변수 설정
        String apiURL = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
        String authKey = ""; // 본인 서비스 키 입력

        String nx = "69";
        String ny = "100";
        String baseDate = "20220316";
        String baseTime = "1800";
        String dataType = "JSON";

        StringBuilder urlBuilder = new StringBuilder(apiURL);
        urlBuilder.append("?" + URLEncoder.encode("serviceKey", "UTF-8") + "=" + authKey);
        urlBuilder.append("&" + URLEncoder.encode("numOfRows=10", "UTF-8"));    // 숫자 표
        urlBuilder.append("&" + URLEncoder.encode("pageNo=1", "UTF-8"));    // 페이지 수
        urlBuilder.append("&" + URLEncoder.encode("dataType", "UTF-8") + "=" + URLEncoder.encode(dataType, "UTF-8")); // 받으려는 타입
        urlBuilder.append("&" + URLEncoder.encode("base_date", "UTF-8") + "=" + URLEncoder.encode(baseDate, "UTF-8")); /* 조회하고싶은 날짜*/
        urlBuilder.append("&" + URLEncoder.encode("base_time", "UTF-8") + "=" + URLEncoder.encode(baseTime, "UTF-8")); /* 조회하고싶은 시간 AM 02시부터 3시간 단위 */
        urlBuilder.append("&" + URLEncoder.encode("nx", "UTF-8") + "=" + URLEncoder.encode(nx, "UTF-8")); //경도
        urlBuilder.append("&" + URLEncoder.encode("ny", "UTF-8") + "=" + URLEncoder.encode(ny, "UTF-8")); //위도

        URL url = new URL(urlBuilder.toString());
        System.out.println(url);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-type", "application/json");
        System.out.println("Response code: " + conn.getResponseCode());
        BufferedReader rd;
        if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        rd.close();
        conn.disconnect();
        String result = sb.toString();

        System.out.println(result);

        JSONParser jsonParser = new JSONParser();
        JSONObject jsonObject = (JSONObject) jsonParser.parse(result);
        JSONObject parse_response = (JSONObject) jsonObject.get("response");
        JSONObject parse_body = (JSONObject) parse_response.get("body"); // response 로 부터 body 찾아오기
        JSONObject parse_items = (JSONObject) parse_body.get("items"); // body 로 부터 items 받아오기
        // items 로 부터 itemList : 뒤에 [ 로 시작하므로 jsonArray 이다.
        JSONArray parse_item = (JSONArray) parse_items.get("item");
        System.out.println("--------------------------");

        // item 들을 담은 List 를 반복자 안에서 사용하기 위해 미리 명시
        JSONObject object;
        // item 내부의 category 를 보고 사용하기 위해서 사용
        String category;
        Double value;

        // jsonArray를 반복자로 반복
        for (int temp = 0; temp < parse_item.size(); temp++) {
            object = (JSONObject) parse_item.get(temp);
            category = (String) object.get("category"); // item 에서 카테고리를 검색

            // Error 발생할수도 있으며 받아온 정보를 double이 아니라 문자열로 읽으면 오류
            value = Double.parseDouble((String) object.get("obsrValue"));

            WeatherValue weatherValue = WeatherValue.valueOf(category);

            switch (weatherValue) {
                case PTY:
                    weather.setPTY(value);
                    break;
                case REH:
                    weather.setREH(value);
                    break;
                case RN1:
                    weather.setRN1(value);
                    break;
                case T1H:
                    weather.setT1H(value);
                    break;
                case UUU:
                    weather.setUUU(value);
                    break;
                case VEC:
                    weather.setVEC(value);
                    break;
                case VVV:
                    weather.setVVV(value);
                    break;
                case WSD:
                    weather.setWSD(value);
                    break;
                default:
                    break;
            }
        }
        weather.setDate(baseDate);
        weather.setTime(baseTime);
        // 잘 출력되는지 확인하고 싶으면 아래 주석 해제
//        System.out.println(weather);

    }
}
profile
Backend Developer

0개의 댓글