공공데이터포털은 공공기관이 생성하고 관리하고 있는 데이터를
모든 국민들이 쉽고 편리하게 이용할 수 있도록 파일데이터, 오픈API, 시각화 등 다양한 방식으로 제공하는 사이트이다.
공공데이터 오픈 API를 활용하기전 신청을 해보자.
👉 공공데이터 포털 홈페이지
API를 활용하려면 위 홈페이지에 회원가입을 하고 로그인이 필요하다.
로그인 후 원하는 오픈 API를 선정 후 활용신청을 작성한다.
미세먼지가 좋지 않은 요즘 한국환경공단에서 제공하는 전국 대기오염정보 API를 사용해보자.
활용신청 버튼을 클릭하면 활용목적 / 상세API / 동의여부 등 간단한 정보를 입력한 후 신청을 할 수 있다.
(참고문서는 개발에 필요한 정보들이 많기 때문에 정독하는 것을 추천한다.)
개발 시 참고할 수 있는 샘플코드를 각 언어별로 제공하고있다.
상세목록으로 이동하면 개발에 필요한 인증키와 함께 데이터를 미리 확인할 수 있는 호출 Form 을 확인할 수 있다.
(단, API는 신청 후 1~2시간 뒤에 호출이 가능하기 때문에 바로 확인 할 경우 데이터가 조회되지 않는다.)
Spring Boot와 Thymeleaf를 사용하여 위 샘플소스를 이용해 전국의 실시간 미세먼지 정보를 조회할 수 있는 화면을 만들어보자
@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";
}
}
@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;
}
}
<!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>
😂 미세먼지 나쁨
😊 미세먼지 좋음