'jsonp'에 해당되는 글 3건

  1. 2011.08.19 ContentNegotiatingViewResolver 활용 : 하나의 RequestMapping 으로 JSP, JSON, JSONP 처리하기
  2. 2011.08.11 CrossDomain Ajax 호출 #2 : jQuery JSONP를 이용한 Daum OpenAPI 활용 2
  3. 2011.08.10 CrossDomain Ajax 호출 #1 : jQuery JSONP ajax 호출 이용. 4

ContentNegotiatingViewResolver 활용 : 하나의 RequestMapping 으로 JSP, JSON, JSONP 처리하기

뭔가를 맨들다 보면 데이터를 가져와서 대부분 JSP 를 이용해서 랜더링을 하게 되지만 똑같은 데이터를 ajax 호출을 통해 json 형식으로 받고 싶은 경우도 종종 있다.

그리고 JSONP 형식으로 같은 도메인이 아닌 다른 도메인으로 데이터를 제공하고 싶은 경우도 있다.

고럴때 ContentNegotiatingViewResolver 를 활용하면 하나의 리퀘스트 핸들러를 맹글어 놓고 맨 뒤에 URL 만 살짝 바꾸면 URL 에 따라 다른 View 를 이용해서 랜더링 시킬 수 있다.

예를 들어 

http://127.0.0.1:8080/test/view                  요럴땐 jsp 로 랜더링
http://127.0.0.1:8080/test/view.json           요럴땐 json 형식으로 랜더링
http://127.0.0.1:8080/test/view.jsonp         요럴땐 jsonp 형식으로 랜더링~

또 약간의 노력을 더해주면 xml 형식이나 rss 형식으로도 랜더링 시킬 수 있다.

죠런 기능을 구현하기 위해서 ContentNegotiatingViewResolver 를 사용하도록 설정해 주고 필요에 따라 AbstractView 클래스를 구현해서 원하는 View를 맹글면 된다.



스프링 설정파일
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
	<!-- ViewResolver 우선순위 설정 -->
	<property name="order" value="1" />
	<property name="mediaTypes">
		<!-- 맵핑될 확장자 정의 -->
		<map>
			<entry key="json" value="application/json" />
			<entry key="jsonp" value="javascript/jsonp" />
		</map>
	</property>

	<property name="defaultViews">
		<list>
			<!-- JSON 요청을 처리할 뷰 -->
			<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
			
			<!-- JSONP 요청을 처리할 뷰 -->
			<bean class="stove99.views.JSONPView">
				<property name="contentType" value="javascript/jsonp"/>
			</bean>
		</list>
	</property>
	<property name="ignoreAcceptHeader" value="true" />
</bean>

<!-- 맵핑되는 확장자가 없을때 JSP 로 뷰 처리 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="order" value="2" />
	<property name="prefix" value="/WEB-INF/views/"/>
	<property name="suffix" value=".jsp"/>
</bean>
mediaTypes에 정의된 entry 들은 URL 맨 뒤에 붙은 확장자와 mediaType 을 설정한다. 여기에서 설정된 mediaType 정보를 바탕으로 아래쪽 defaultViews 목록에 정의된 View 들 중에서 랜더링 가능한 View 를 찾아서 그 View 로 랜더링 시킨다. 뭐 설명이 허접하긴 하지만 내가 이해한 바로는 그렇다.



stove99.views.JSONPView 클래스 소스
package stove99.views;

import java.io.Writer;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.web.servlet.view.AbstractView;

public class JSONPView extends AbstractView{

	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, 
                                               HttpServletRequest request,
                                               HttpServletResponse response) throws Exception {
		String callback = request.getParameter("callback");
		ObjectMapper om = new ObjectMapper();
		String json = om.writeValueAsString(model);
		
		Writer out = response.getWriter();
		out.append(callback).append("(").append(json).append(")");
	}
}


컨트롤러 소스
package stove99.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test/*")
public class TestController {

	@RequestMapping
	public void view(ModelMap model) {
		Map<String, String> data = new HashMap<String, String>();
		data.put("data1", "value1");
		data.put("data2", "value2");
		data.put("data3", "value3");

		model.addAttribute("model", data);
	}
}

테스트 결과 화면

JSP 로 랜더링


JSON 으로 랜더링


JSONP로 랜더링


ContentNegotiatingViewResolverTest.war


CrossDomain Ajax 호출 #2 : jQuery JSONP를 이용한 Daum OpenAPI 활용

CrossDomain Ajax 호출 #1 에서 연구해 보았던 jQuery JSONP 를 활용해 다음 OpenAPI 로 이미지를 검색하는 샘플을 제작해 보았다.

다행스럽게도 다음 오픈 API에서 제공하는 많은 API 들이 대부분 callback이라는 파라메터를 받는다. 그렇기 때문에 JSONP 로 ajax 호출을 통해 깔쌈하게 뭔가를 맨들수 있다.

callback 이라는 파라메터를 받는다!!



찾아봤는데 네이버 오픈 API는 아쉽게도 callback 요런 종류의 파라메터를 받는 API 가 하나도 없는것 같다. 요것은 JSONP 방식이 아닌 Proxy Servlet 방식을 이용해서 데이터를 받아야만 한다. 뭐 이것은 차후에 한번 해 봐야지~

먼저 샘플을 맨들기 전에 다음 OpenAPI 사이트(http://dna.daum.net/DNALatte/openapi/about)에 가서 Key를 발급받아야 한다. 샘플로 검색API 에 있는 이미지 검색 API를 활용할 것이기 때문에 검색 API용 키를 발급 받는다.

localhost:8080 에서 테스트를 할것이기 때문에 사용URL 란에는 http://localhost:8080 을 입력하고 확인 버튼을 클릭하자! 고러면 키가 발급된다.

키 발급화면




키를 발급 받았다면 샘플 제작 고고씽~

이미지 API 를 호출하는 jQuery 코드는 요렇게~
$.ajax({
	url : "http://apis.daum.net/search/image",
	dataType : "jsonp",
	type : "post",
	jsonp : "callback",
	data : {
		apikey : "발급받은 Key",        // API KEY
		q : "한예슬",                   // 검색어
		result : "10",                 // 한페이지에 출력될 결과수
		pageno : $("#pageno").val(),   // 페이지번호
		output : "json"                // JSONP 형식으로 호출하기 때문에 결과값 포맷은 json
	},
	success : function(r){
		//검색결과 처리
	}
});


검색된 결과는 json 형태로 리턴된다. json으로 보내달라고 요청했으니깐~
console에다 찍어보니 데이터 형태가 죠렇게 되있다. 검색결과에 대한 자세한 설명은 http://dna.daum.net/apis/search/image 요기에서 참고하면 된다.

검색결과 JSON 오브젝트 데이터구조


검색결과는 대충보니 channel 안에 item 에 배열로 들어가 있다~ 뭐 하여튼 저 검색결과를 활용해서 쫌 쓸만하게 제작을 하면 되는 것이다!

샘플로 맨들어본 api.html 의 전체소스
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ko">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>Daum OpenAPI with jQuery</title>
	
	<script type="text/javascript" src="/resource/js/jquery-1.6.2.min.js"></script>
	
	<script type="text/javascript">
		$(document).ready(function(){
			// 이미지 검색 할래염 버튼
			$("#searchBtn").click(function(){
				// 기존 검색결과랑 조건 지우기
				$("#imgList").empty();
				$("#pageInfo").val("1");
				
				searchImg();
			});
			
			// 더보기버튼
			$("#moreBtn").click(function(){
				$("#pageno").val(parseInt($("#pageno").val())+1);
				
				searchImg();
			});
			
			var searchImg = function(){
				$.ajax({
					url : "http://apis.daum.net/search/image",
					dataType : "jsonp",
					type : "post",
					jsonp : "callback",
					data : {
						apikey : "발급받은키",               // API KEY
						q : $("#keyword").val(),         // 검색어
						result : "10",                   // 한페이지에 출력될 결과수
						pageno : $("#pageno").val(),     // 페이지번호
						output : "json"                  // JSONP 형식으로 호출하기 때문에 결과값 포맷은 json
					},
					success : function(r){
						r = r.channel;
						$("#pageInfo").text("검색된 이미지는 " + r.totalCount +"개 에요~")
						
						$.each(r.item, function(idx, data){
							var img = $("").attr({
								src : data.thumbnail,
								title : data.title
							}).click(function(){
								window.open(data.image);
							}).appendTo("#imgList");
						});
					}
				});
			};
		});
	</script>
	
	<style type="text/css">
		#result {width:800px;}
		#imgList {width: 100%;border:2px solid #ccc; padding:5px}
		#imgList img{border:1px solid #cccccc;padding:2px;margin:5px;cursor:pointer}
		#moreBtn {width:100%;height:60px;}
	</style>
</head>
<body>
	<input type="hidden" id="pageno" value="1"/>
	<input type="text" id="keyword" />
	<button id="searchBtn">이미지 검색 할래염</button>
	
	<div id="result">
		<span id="pageInfo"></span>
		<div id="imgList">여기에 검색된 이미지가 나타날꺼임</div>
		<button id="moreBtn">더보기</button>
	</div>
</body>
</html>

[샘플소스]DaumAPI.war


실행결과 Demo 원하는 검색어를 입력후 "이미지 검색 할래염" 버튼을 클릭해 보셈~
여기에 검색된 이미지가 나타날꺼임

CrossDomain Ajax 호출 #1 : jQuery JSONP ajax 호출 이용.

Ajax 로 다른 도메인에 있는 url 을 호출해 데이터나 HTML을 가져오는건 보안상 안된다고 한다. 되면 참 좋을텐데;;

뭐 아무튼 이걸 해결하기 위한 방법으로 JSONP 라는 형식으로 호출하던가 아니면 Proxy Servlet 을 하나 맹글어 그 서블릿을 거쳐 HttpClient 로 가져오는 방법, 훌래쉬를 이용하는 방법이 있다고 한다.!

그중에서 이번엔 JSONP 형식으로 호출하는 방법을 연구해 보겠다.

JSONP 형식이란 뭐 별건 아닌것 같고 기존에 서버에서 리턴해 주던

{"key1" : "value1",  "key2" : "value2"} 요런 json 문자열을

callback({"key1" : "value1",  "key2" : "value2"}); 요런식으로 임의의 함수를 호출해 주는 형식의 문자열로 리턴해 주는것을 말하는것 같다.

즉, jsonp 로 ajax 호출을 하기 위해선 아무 url 이나 안되고  callback({"key1" : "value1",  "key2" : "value2"});  요런식으로 함수안에 json 문자열이 들어간 형식으로 서버에서 리턴을 해줘야 가능하다.

각설하고 jQuery를 활용해 맹글어 보자.



jQuery에서 jsonp 호출하는 방법#1
$.getJSON("http://127.0.0.1:8080/server/data.jsp?callback=?", function(d){
	//d.key;
});

jQuery에서 jsonp 호출하는 방법#2
$.ajax({
	url : "http://127.0.0.1:8080/server/data.jsp",
	dataType : "jsonp",
	jsonp : "callback",
	success : function(d){
		// d.key;
	}
});

방법#1 이나 방법#2는 작동하는게 동일하다. 취향에 맞게 쓰임새에 맞게 쓰면된다. 개인적으로 방법#2 를 여러가지 원하는 ajax 호출 옵션을 줄수 있어서 더 좋아한다.

살짝 설명해 보면 방법#2 에서 쓴 ajax 옵션중 jsonp는 데이터를 넘겨줄 서버에서 받는 callback 함수명 파라메터이다. 서버에 따라 원하는 callback 함수명을 적어줘야 한다. 방법#1을 보면 대충 이해할 수 있다.

jsonp 옵션에다 적어준 함수명은 ajax 호출 url 뒤에다 방법#1에서 처럼 파라메터 형식으로 자동 추가된다.



전체소스코드 client.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Jsonp test with jquery</title>
	
	<script type="text/javascript" src="/resource/js/jquery-1.6.2.min.js"></script>
	<script>
		$(document).ready(function(){
			$("#testBtn").click(function(){
				$.getJSON("http://127.0.0.1:8080/server/data.jsp?callback=?", function(d){
					$.each(d, function(k, v){
						$("#getjson").append("<div>" + k + " : " + v + "</div>");
					});
					$("#getjson").show();
				});
							
				$.ajax({
					url : "http://127.0.0.1:8080/server/data.jsp",
					dataType : "jsonp",
					jsonp : "callback",
					success : function(d){
						$.each(d, function(k, v){
							$("#ajax").append("<div>" + k + " : " + v + "</div>");
						});
						$("#ajax").show();
					}
				});
			});
		});
	</script>
	
	<style>
		div{margin-bottom:10px;padding:2px;}
		#getjson{border:1px solid red;display:none;}
		#ajax{border:1px solid blue;display:none;}
	</style>
</head>

<body>
	<button id="testBtn">테스트!</button>
	<div id="getjson"></div>
	<div id="ajax"></div>
</body>
</html>



다음으로 간단한 테스트를 위해 초간단 심플 데이터 제공용 jsp 파일을 하나 맹글어 보자. JSON 변환용 라이브러리로 Jackson JSON Processor를 사용했다.
<%@ page language="java" contentType="text/javascript; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.io.StringWriter"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>

<%@ page import="org.codehaus.jackson.map.ObjectMapper"%>

<%
	Map<String, String> dummyData = new HashMap<String, String>();
	dummyData.put("value1", "값1");
	dummyData.put("value2", "값2");
	dummyData.put("value3", "값3");
	dummyData.put("value4", "값4");
	
	StringWriter sw = new StringWriter();
	
	// Jackson JSON Mapper 를 사용해서 Map 을 JSON 문자열로 변환
	ObjectMapper mapper = new ObjectMapper();
	mapper.writeValue(sw, dummyData);
	
	request.setAttribute("sw", sw);
%>

<%-- ajax 에서 넘겨준 callback 함수 파라메터 가져오기 --%>
${param.callback}(${sw});
ajax로 호출한 url 경로에 맞게금 /server/data.jsp 로 저장하고 http://localhost:8080/client.html 로 접속해보자. 왜냐하면 localhost 랑 127.0.0.1 은 도메인이 다르니깐.  

음 잘 작동된다~~ 초간단 심플 예제이기 때문에 간단하게 JSP로 구현했는데 다음번에 쫌더 실제적으로 프로젝트에 써먹을수 있도록 Spring으로 예제를 하나 맹글어 봐야긋다~

[샘플소스]CrossDomain1.war
prev 1 next