본문 바로가기
JavaScript

JSONP 알고 쓰자

by Kingbbode 2016. 12. 18.

JSONP 알고 쓰자!

자바 스크립트는 서로 다른 도메인에 대한 요청을 보안상 제한합니다. 이 정책을 동일근원정책(Same-Origin Policy, SOP) 정책이라고 하며, 이러한 정책으로 인해 생기는 이슈를 cross-domain 문제라고 합니다. 
개발을 하다보면 어쩔 수 없이 다른 도메인으로부터 데이터를 가져와야 하는 경우가 많기에 많은 사람들이 cross-domain 이슈를 겪고 있습니다.

이런 경우 사용할 수 있는 것이 JSONP(JSON with Padding)입니다. 처음 JSONP를 접했을 때 잘 정리가 되지 않았던, 사용할 때 명확히 알아야 할 것들을 정리해보았습니다.

1. JSONP 요청은 일반 AJAX 요청과 다르다

처음 JSONP를 접할 때 우리를 혼란스럽게 하는 것은.. 다름아닌 Jquery 입니다.

우리에게 좋은 사용성을 주기 위해 Jquery는 JSONP 요청을 마치 XHR 리퀘스트의 한 방식인 것 처럼 묶어주었습니다.


$.ajax({
  url: url,
  dataType: 'json',
  data: data,
  success: callback
});

$.getJSON(url, data, callback);

위 요청은 일반적인 json을 요청하는 XHR 요청입니다.


$.ajax({
  url: url,
  dataType: 'jsonp',
  jsonpCallback: "myCallback",
  success: callback
});

$.getJSON(url + "?callback=?", data, callback);

위 요청은 jsonp 요청입니다.

dataType 만 변경하거나, 요청 url 뒤에 callback 파라미터 를 붙일 뿐입니다. 그렇지만 이것만으로도 완전히 다른 동작 을 하게 됩니다.

2. ‘JSONP’라는 데이터 타입 요청이 아닌 <script> 호출 방식

JSONP는 HTML의 script 요소로부터 요청되는 호출에는 보안상 정책이 적용되지 않는다는 점을 이용한 우회 방법 입니다.

다시 한번 확실히 하자면, script 요소는 src를 호출한 결과를 javascript를 불러와서 포함시키는 것이 아니고 실행시키는 태그입니다.

The <script> element (or HTML Script Element ) is used to embed or reference an <executable script within an HTML or XHTML document.

Scripts without async or defer attributes, as well as inline scripts, are fetched and executed immediately, before the browser continues to parse the page.

Mozilla 참고

아래는 일반적인 jsonp 응답입니다.

callback({name:'kingbbode', age:28})

<script>의 호출 후 즉시 실행하는 점을 이용하여 응답으로 온 Text Type의 결과를 javascript로 인식하여 바로 실행하게 됩니다.

아래는 Jquery를 사용하지 않은 jsonp 요청입니다.

var script = document.createElement('script');
script.src = '//kingbbode.com/jsonp?callback=myCallback'

document.getElementsByTagName('head')[0].appendChild(script);

function myCallback(data){
  //callback method
}

3. JSONP Callback은 서버에서 지원해줘야 한다

JSONP를 적용하면서 거의 대부분 사람들이 한번씩 봤을 법한 에러 메시지!

Error

이 메시지를 보았다는 것은 아마도 jsonp에 대한 처리가 없는 서버에 요청을 하였을 경우일 것 입니다. 어째서 이런 결과가 나오는지 알아보겠습니다.

알아두어야 할 중요한 것은 응답 데이터는 Text형 이란 것과, callback 함수명으로 감싸진다 는 점 입니다.

응답 예)

"myCallback({'name':'test','age':28})"

정의한 Callback 함수명으로 감싸주는 것은 jquery 혹은 브라우저, javascript 등 클라이언트 단에서 지원하여 처리해주는 것이 아닙니다!
서버에서 감싸진 형태의 Text로 내려주어야 하는 것 입니다.

Text로 내려진 데이터가 Javascript로 실행되는 과정을 Jquery에서 지원하여 Success 함수로 연결시켜 주는 것 입니다.

위 텍스트의 결과 값 부분을 파싱하고 $.parseJSON을 통해 json Object로 만들어주게 됩니다. 
jsonp 처리되지 않은 결과를 파싱하고, parseJSON하는 과정에서 Uncaught SyntaxError: Unexpected token : 에러가 발생하게 되는 것 입니다.

Spring Boot JSONP 응답 구현 소스

Spring에서 지원해주는 AbstractJsonpResponseBodyAdvice로 ControllerAdvice만 구현하면 끝! AbstractJsonpResponseBodyAdvice는 응답 Type과 Callback 파라미터 확인, 응답 데이터 확인과 가장 중요한 응답 결과 wrapping을 구현해놓은 추상 객체입니다.

@ControllerAdvice
public class JsonpAdviceController extends AbstractJsonpResponseBodyAdvice {
    public JsonpAdviceController() {
        super("callback");
    }
}

Object를 JSON String으로 변환하기 위한 메세지 컨버터까지만!

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
	MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
	ObjectMapper objectMapper = new ObjectMapper();
	objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	jsonConverter.setObjectMapper(objectMapper);
	return jsonConverter;
}

서버에서 처리대신 정적 파일 자체가 함수에 감싸져있는 형태여도 처리는 가능합니다. 그러나 $.getJSON은 사용할 수 없습니다.

4. 고마운 Jquery

처음부터 Jquery로 JSONP를 사용했던 것이 개념을 잡기 어렵게 했을 수도 있지만, Jquery는 너무나도 편하게 JSONP를 지원해주고 있습니다.

$.ajax({
  url: url,
  dataType: 'jsonp',
  jsonpCallback: "myCallback",
  success: callback
});

위 요청을 해석해보자면,

  • cross-domain 이슈를 회피하기 위하여 jsonp 요청을 한다
  • 응답 데이터는 myCallback(결과) 형태의 text로 wrapping 되어질 것이다.
  • myCallback이란 함수를 나의 success function에 mapping 시킬 것이다.

가 됩니다.

실행 결과는

ajax

더 간단한 형태로는

$.getJSON(url + "?callback=?", data, callback);

getJSON은 callback 이름을 임의로 생성해 알아서 success function에 매칭시켜줍니다. 위 $.ajax 요청보다 훨씬 간단한 것을 알 수 있습니다.

getJson





테스트에 사용된 Spring Boot 기반 서버와 테스트 HTML은 GITHUB에 올려두었습니다!

댓글