[Spring] HashMap json mapping

2024. 10. 10. 01:41Spring

공공데이터 포털에 가면 오픈 되어 있는 여러가지 공공데이터들이 있다.

오늘은 그 오픈 json데이터를 HashMap으로 mapping 해볼 것이다.


 home.jsp 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

<h1>day09</h1>
<hr>

<h3>JSON : JavaScript Object Notation</h3>
<h3>자바스크립트에서 객체를 표현하는데 사용하는 문법</h3>

<ul>
	<li><a href="ex01">ex01 - 부산 축제 정보 서비스 연습</a></li>
	<li><a href="ex02">ex02 - 부산 축제 정보 서비스 (AJAX)</a></li>
</ul>

</body>
</html>

 

ex01.jsp 

json 파일을 자바 객체로 변환하여 출력하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="cpath" value="${pageContext.request.contextPath }" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ex01.jsp</title>
<style>
	#root {
		width: 900px;
		margin: 20px auto;
	}
	.item {
		width: 800px;
		margin: 10px auto;
		border: 1px solid grey;
		padding: 10px;
	}
</style>
</head>
<body>

<h1>ex01 - JSON을 자바 객체로 변환하여 출력하기</h1>
<hr>

<p>
	<a href="${cpath }/ex01/js"><button>JS로 처리하기</button></a>
</p>

<div id="root">
	<c:forEach var="dto" items="${list }">
	<div class="item">
		<div><h3>${dto.UC_SEQ }. ${dto.TITLE } (${dto.GUGUN_NM })</h3></div>
		<div>${dto.HOMEPAGE_URL }</div>
		<div><img src="${dto.MAIN_IMG_NORMAL }" height="300"></div>
		<div>
			<details>
				<summary>상세보기</summary>
				<span>${dto.ITEMCNTNTS }</span>
			</details>
		</div>
	</div>
	</c:forEach>
</div>


</body>
</html>

 

해당 api의 내용을 알고 있어야 한다.

필드명이 반드시 일치해야 불러올 수 있다.

 

ex01- js.jsp 

json 파일을 자바 스크립트로 변환하여 출력하기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ex01-js</title>
<style>
	#root {
		width: 900px;
		margin: 20px auto;
	}
	.item {
		width: 800px;
		margin: 10px auto;
		border: 1px solid grey;
		padding: 10px;
	}
</style>
</head>
<body>

<h1>ex01 - JSON을 자바스크립트로 처리하여 출력하기</h1>
<hr>

<div id="root"></div>

<script>
	const jsonObject = ${json}
	
// 	console.log(jsonObject.getFestivalKr.item)
	const arr = jsonObject.getFestivalKr.item
	const root = document.getElementById('root')
	
	root.innerHTML = ''
	for(let i = 0; i < arr.length; i++) {
		let tag = ''
		tag += '<div class="item">'
		tag += '	<div><h3>' + arr[i].UC_SEQ + '. ' + arr[i].TITLE + '(' + arr[i].GUGUN_NM + ')</h3></div>'
		tag += '	<div>' + arr[i].HOMEPAGE_URL + '</div>'
		tag += '	<div><img src="' + arr[i].MAIN_IMG_NORMAL + '" height="300"></div>'
		tag += '	<div>'
		tag += '		<details>'
		tag += '			<summary>상세보기</summary>'
		tag += '			<span>' + arr[i].ITEMCNTNTS + '</span>'
		tag += '		</details>'
		tag += '	</div>'
		tag += '</div>'
		root.innerHTML += tag
	}
</script>

</body>
</html>

 

 Ex01Controller 

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itbank.service.Ex01Service;

@Controller
public class Ex01Controller {
	
	@Autowired private Ex01Service service;
	private ObjectMapper objectMapper = new ObjectMapper();
	
	@GetMapping("/ex01/js")
	public ModelAndView ex01Js() throws MalformedURLException, IOException {
		ModelAndView mav = ex01();
		mav.setViewName("ex01-js");
		return mav;
	}

	@GetMapping("/ex01")
	public ModelAndView ex01() throws MalformedURLException, IOException {
		ModelAndView mav = new ModelAndView();
		
		String json = service.getFestivalJson();	// JSON 데이터는 문자열이다
		mav.addObject("json", json);
		System.out.println(json);
		
		// JSON 형식의 문자열을 자바 객체로 변환하기 위한 코드
		JsonNode node = objectMapper.readTree(json);
		JsonNode item = node.get("getFestivalKr").get("item");
		System.out.println("item : " + item.toPrettyString());
		
//		DTO로 맵핑하기 (필드이름이 복잡하여 제대로 맵핑되지 않았다)
//		List<FestivalDTO> list = Arrays.asList(
//				objectMapper.readValue(item.toPrettyString(), FestivalDTO[].class)
//		);
//		System.out.println(list.get(0).getMAIN_TITLE());
		
//		HashMap으로 맵핑하기
		@SuppressWarnings("unchecked")
		List<HashMap<String, Object>> list = Arrays.asList(
				objectMapper.readValue(item.toPrettyString(), HashMap[].class)
		);
		System.out.println(list.get(0).get("MAIN_TITLE"));
		
		mav.addObject("list", list);
		return mav;
	}
}

 

위 코드에서 어려웠던 부분

처음에는 DTO로 처리하려고 했으나, 필드명이 너무 복잡해서 mapping이 잘 되지 않았다.

그래서 mapping을 HashMap으로 구현하였다.

 

FestivalDTO

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

// JSON데이터에서 DTO에 명시되지 않은 속성(알수없는 속성)은 무시하겠다
@JsonIgnoreProperties(ignoreUnknown = true)

// JSON의 속성이름을 자바 네이밍 컨벤션(camelCase)으로 변환하는 과정이 맞아야 한다
public class FestivalDTO {

	@JsonProperty("UC_SEQ")				private int UC_SEQ;
	@JsonProperty("MAIN_TITLE")			private String MAIN_TITLE;
	@JsonProperty("GUGUN_NM")			private String GUGUN_NM;
	@JsonProperty("HOMEPAGE_URL")		private String HOMEPAGE_URL;
	@JsonProperty("MAIN_IMG_NORMAL")	private String MAIN_IMG_NORMAL;
	@JsonProperty("ITEMCNTNTS")			private String ITEMCNTNTS;
	
	public int getUC_SEQ() {
		return UC_SEQ;
	}
	public void setUC_SEQ(int uC_SEQ) {
		UC_SEQ = uC_SEQ;
	}
	public String getMAIN_TITLE() {
		return MAIN_TITLE;
	}
	public void setMAIN_TITLE(String mAIN_TITLE) {
		MAIN_TITLE = mAIN_TITLE;
	}
	public String getGUGUN_NM() {
		return GUGUN_NM;
	}
	public void setGUGUN_NM(String gUGUN_NM) {
		GUGUN_NM = gUGUN_NM;
	}
	public String getHOMEPAGE_URL() {
		return HOMEPAGE_URL;
	}
	public void setHOMEPAGE_URL(String hOMEPAGE_URL) {
		HOMEPAGE_URL = hOMEPAGE_URL;
	}
	public String getMAIN_IMG_NORMAL() {
		return MAIN_IMG_NORMAL;
	}
	public void setMAIN_IMG_NORMAL(String mAIN_IMG_NORMAL) {
		MAIN_IMG_NORMAL = mAIN_IMG_NORMAL;
	}
	public String getITEMCNTNTS() {
		return ITEMCNTNTS;
	}
	public void setITEMCNTNTS(String iTEMCNTNTS) {
		ITEMCNTNTS = iTEMCNTNTS;
	}
	

}

 

Ex01Service 

Ex01Controller의 호출을 받고,

요청된 내용을 처리한 후

결과값을 호출한 장소로 반환한다.

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Scanner;

import org.springframework.stereotype.Service;

@Service
public class Ex01Service {
	// 공공데이터포털, 부산 축제 정보

	private final String serviceKey = "K7G5hCA%2FRqnmALDK%2F7POZXDGSgTgQFRIcOqpF8HUf9rqLn17QSaJ4Q0Ox732h%2BF%2FgxuyB3bXrdEWApNVwrOtWA%3D%3D";
	
	public String getFestivalJson() throws MalformedURLException, IOException {
		// 1) 요청 주소 및 파라미터 준비
		String url = "https://apis.data.go.kr/6260000/FestivalService/getFestivalKr";
		HashMap<String, String> param = new HashMap<>();
		param.put("pageNo", "1");
		param.put("numOfRows", "10");
		param.put("resultType", "json");
		param.put("serviceKey", serviceKey);
		url += "?";
		for(String key : param.keySet()) {
			url += key + "=" + param.get(key) + "&";
		}
		
		// 2) 요청을 전송하여 응답을 받아서 저장
		Scanner sc = null;
		String response = "";
		HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
		
		if(conn.getResponseCode() == 200) {	// 200 = 정상
			sc = new Scanner(conn.getInputStream());
			while(sc.hasNextLine()) {
				response += sc.nextLine();
			}
			sc.close();
			conn.disconnect();
		}
		
		return response;
	}

}

 

여기서 주의할 점

공공데이터 포털 사이트에서 url과 serviceKey 오타를 항상 조심해야 함.

 


DTO로 매핑을 계속 시도 했는데 잘 되지도 않고, 비효율적인 것 같아서 Chat gpt의 도움을 조금 받았다.

그래서 HashMap을 사용하게 되었고, 훨씬 효율적인 로직이 완성되었다.

 

언제나 간결성, 가독성 그리고 효율성이 좋은 코드를 작성하도록 노력이 필요하다.