공공데이터 API 활용하기

Heejun Byun·2023년 4월 8일
0

개념정리

목록 보기
3/4
post-thumbnail

💡 공공데이터 포털 이란?

공공데이터포털은 공공기관이 생성하고 관리하고 있는 데이터를
모든 국민들이 쉽고 편리하게 이용할 수 있도록 파일데이터, 오픈API, 시각화 등 다양한 방식으로 제공하는 사이트이다.


💡 공공데이터 API 신청하기

공공데이터 오픈 API를 활용하기전 신청을 해보자.
👉 공공데이터 포털 홈페이지
API를 활용하려면 위 홈페이지에 회원가입을 하고 로그인이 필요하다.

로그인 후 원하는 오픈 API를 선정 후 활용신청을 작성한다.
미세먼지가 좋지 않은 요즘 한국환경공단에서 제공하는 전국 대기오염정보 API를 사용해보자.

활용신청 버튼을 클릭하면 활용목적 / 상세API / 동의여부 등 간단한 정보를 입력한 후 신청을 할 수 있다.
(참고문서는 개발에 필요한 정보들이 많기 때문에 정독하는 것을 추천한다.)

개발 시 참고할 수 있는 샘플코드를 각 언어별로 제공하고있다.

상세목록으로 이동하면 개발에 필요한 인증키와 함께 데이터를 미리 확인할 수 있는 호출 Form 을 확인할 수 있다.
(단, API는 신청 후 1~2시간 뒤에 호출이 가능하기 때문에 바로 확인 할 경우 데이터가 조회되지 않는다.)


💡 공공데이터 API 활용하기


Spring Boot와 Thymeleaf를 사용하여 위 샘플소스를 이용해 전국의 실시간 미세먼지 정보를 조회할 수 있는 화면을 만들어보자

👉 ApartmentDetailController.java

@Controller
@RequestMapping("/api/apartment")
public class ApartmentDetailController {

    @Autowired
    ApartmentDetailService apartmentDetailService = new ApartmentDetailService();

    @GetMapping("/view")
    public String view() {
        return "public/apartment";
    }

    @PostMapping("/search")
    public String search(Model model, @RequestBody(required = false) String searchDate) {
        List<Map<String, Object>> result = new ArrayList<>();
        try {
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = new Date();
            if (searchDate == null) searchDate = sf.format(date);
            System.out.println("searchDate = " + searchDate);
            result = apartmentDetailService.detailApiCall(searchDate);
            model.addAttribute("apiData", result);
        } catch (IOException | ParserConfigurationException | SAXException e) {
            System.out.println("e.getMessage() = " + e.getMessage());
        }
        return "public/apartment :: #apiTable";
    }

}

👉 ApartmentDetailService .java

@Service
public class ApartmentDetailService {

    public List<Map<String, Object>> detailApiCall(String searchDate) throws IOException, ParserConfigurationException, SAXException {
        StringBuilder urlBuilder = new StringBuilder("http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMinuDustFrcstDspth"); /*URL*/
        urlBuilder.append("?" + URLEncoder.encode("serviceKey","UTF-8") + "=service Key"); /*Service Key*/
        urlBuilder.append("&" + URLEncoder.encode("returnType","UTF-8") + "=" + URLEncoder.encode("xml", "UTF-8")); /*xml 또는 json*/
        urlBuilder.append("&" + URLEncoder.encode("numOfRows","UTF-8") + "=" + URLEncoder.encode("100", "UTF-8")); /*한 페이지 결과 수(조회 날짜로 검색 시 사용 안함)*/
        urlBuilder.append("&" + URLEncoder.encode("pageNo","UTF-8") + "=" + URLEncoder.encode("1", "UTF-8")); /*페이지번호(조회 날짜로 검색 시 사용 안함)*/
        urlBuilder.append("&" + URLEncoder.encode("searchDate","UTF-8") + "=" + URLEncoder.encode(searchDate, "UTF-8")); /*통보시간 검색(조회 날짜 입력이 없을 경우 한달동안 예보통보 발령 날짜의 리스트 정보를 확인)*/
        urlBuilder.append("&" + URLEncoder.encode("InformCode","UTF-8") + "=" + URLEncoder.encode("PM10", "UTF-8")); /*통보코드검색(PM10, PM25, O3)*/
        URL url = new URL(urlBuilder.toString());
        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();
        // System.out.println(sb.toString());

        // Document Parsing
        /**
        DocumentBuilderFactory dbFactoty = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactoty.newDocumentBuilder();
        Document doc = dBuilder.parse(String.valueOf(url));
        // 파싱할 tag
        NodeList nList = doc.getElementsByTagName("item");
        // for 문..
        for (int temp = 0; temp < nList.getLength(); temp++) {
        Node nNode = nList.item(temp);
        Element eElement = (Element) nNode;
        if (nNode.getNodeType() == Node.ELEMENT_NODE) {
            map.put("dataTime", getTagValue("gradeType", eElement));
            ...
            // DB Insert
        }
        */

        //XML -> Json Parsing
        JSONObject jsonObj = XML.toJSONObject(sb.toString());
        String xmlObjectStr = jsonObj.toString();

        //item 항목 추출
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> map = objectMapper.readValue(xmlObjectStr, new TypeReference<Map<String, Object>>() {}); //모든 JSON 데티어를 MAP 형식으로 변경
        Map<String, Object> dataResponse = (Map<String, Object>) map.get("response");
        Map<String, Object> body = (Map<String, Object>) dataResponse.get("body");
        Map<String, Object> items  = (Map<String, Object>) body.get("items");
        List<Map<String, Object>> itemList = (List<Map<String, Object>>) items.get("item");
        return itemList;
    }

}

👉 apartment.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>공공데이터</title>
    <link th:href="@{/css/table.css}"
          href="../css/table.css" rel="stylesheet">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
    <br/>
    <label for="startDate">조회일자 </label>
    <input type="date" id="startDate" class="startDate" name="trip-start" value="">
    <button id="apiCallBtn" onclick="apiCall(document.getElementById('startDate').value);">조회</button>
    <br/><br/>
    <div id="apiTable">
    <table>
        <colgroup>
            <col style="width: 10%">
            <col style="width: 5%">
            <col style="width: 20%">
            <col style="width: 20%">
            <col style="width: 20%">
            <col style="width: 25%">
        </colgroup>
        <tr>
            <th>통보시간</th>
            <th>통보코드</th>
            <th>예보개황</th>
            <th>발생원인</th>
            <th>예보등급</th>
            <th>예측모델이미지</th>
        </tr>
        <th:block th:each="map : ${apiData}">
            <tr>
                <td th:text="${map.dataTime}"></td>
                <td th:text="${map.informCode}"></td>
                <td th:text="${map.informOverall}"></td>
                <td th:text="${map.informCause}"></td>
                <td th:text="${map.informGrade}"></td>
                <td>
                    <img id="imgId1" th:src="${map.imageUrl1}" alt="첨부이미지" th:if="${map.imageUrl1 != null}" />
                    <img id="imgId2" th:src="${map.imageUrl2}" alt="첨부이미지" th:if="${map.imageUrl2 != null}" />
                    <img id="imgId3" th:src="${map.imageUrl3}" alt="첨부이미지" th:if="${map.imageUrl3 != null}" />
                </td>
            </tr>
        </th:block>
    </table>
    </div>
</body>
<script th:inline="javascript">
    function apiCall(date) {
        //Ajax 호출
        var inputData = { searchDate : date };
        $.ajax({
            contentType:"application/json;charset=UTF-8",
            url: "/api/apartment/search",
            type: "POST",
            data: date,
            success: function(data){
               console.log(data);
               $("#apiTable").replaceWith(data);
            },
            error: function(xhr, status, error){
               alert(xhr.responseText);
            }
        });
    }
</script>
</html>

😂 미세먼지 나쁨

😊 미세먼지 좋음


profile
반갑습니다. 개발자 변희준입니다.

0개의 댓글