jsonp라는 놈을 처음 접한건 instagram API에 대해서 스터디 하고 있었을 때였다.
jquery의 ajax매소드($.ajax(인자))를 써서 api를 써보는데, datatype속성을 json으로 하면 브라우저가 크로스도메인 에러를 뿌린다.. 에러내용은 아래와 같다.

XMLHttpRequest cannot load http://받는주소. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://보낸주소‘ is therefore not allowed access.

즉, 도메인이 달라서 안된다는 것인데, 이것저것 찾아보다가 몇시간을 허비했다… 결국 찾은 해결책은 datatype속성을 jsonp로만 바꿔주면 되는 것이었다!

이는 교차출처(Cross-Origin) HTTP요청에 대한 정책때문이었다. ajax요청을 받는 서버의 헤더에 크로스도메인 요청을 승인(CORS)이 되어 있지 않으면 서로다른 도메인간에 ajax요청은 승인되지 않는다. 그리고 CORS가 되어 있더라도 ie8 같은 낮은버젼의 브라우저에서는 이를 지원하지 않는다. 그래서 나온 해결책이 jsonp다. 결국 jsonp는 우회해서 크로스도메인 요청을 할 수 있는 방식인 것이다.

jsonp가 생소할수 있지만, 사실 jsonp은 script태그를 생성하고 해당 url을 호출하는 방식이다. 우리가 아주 많이쓰고 있는 방식이다. 예를 들면 우리가 jquery js파일을 호출할때 아래와 같이 할 수 있다.

1
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>

생각해보면 script태그로 호출하는 url주소는 도메인이 어디든지 영향을 받지 않는다. jsonp는 이 방법을 활용해서 해당 파일을 호출한다. 응답하는 파일의 내용에는 함수를 호출하는 자바스크립트 문장이 있으며, 인자로 json형태의 객체를 넘긴다. 예를 들면 다음과 같다.

1
callback([1, 2, {"name" : "owen"}])//여기서 함수명(callback)은 개발자가 정하기 나름이다.

그리고 이 함수에 대한 선언을 미리 해놓아서, jsonp으로 파일이 응답할때 해당 함수가 실행되도록 하는 것이다. 예를 들어.

1
2
function callback(res){console.log(res.name)}
//jsonp로 파일을 가져오면 위 'callback'함수가 실행된다.

Jqueryajax를 활용해서 jsonp를 사용하는건, 아래와 같이 쓴다.

1
2
3
4
5
6
7
8
$.ajax({
dataType: "jsonp",
url: "http://textsite.com/jsonp",
type: "GET",
data: {'s':s},
success: function(data){
}
});

아주쉽다. jsonjsonp로만 바꾸어주면 된다..
여기서 주의할 점은 위에서도 봤지만, 요청 받는 서버(http://textsite.com/jsonp)에서 자바스크립트 함수 호출하는 문장 형태로 값을 넘겨줄 준비가 되어 있어야 한다. 즉 아무 사이트나 jsonjsonp로 바꾼다고 값을 넘겨받을 수 있는건 아니라는 것이다. 다행히 요새 대부분의 sns, 포털사이트의 api들이 jsonp방식을 지원하고 있어서 큰 진행함에 있어서 큰 문제는 없다.

보다 정확히 jsonp를 이해하기 위해서 Jquery를 쓰지 말고 직접 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getJSONP(url){
if(url.indexOf("?") === -1) url +="?callback=cbFn"; //url에 파라미터가 있으면 '&' 없으면 '?'
else url += "&callback=cbFn";
var script = document.createElement('script');//스크립트 엘리먼트를 생성한다.
window.cbFn = function(res){//호출될 함수를 미리 선언한다.
try{
console.log(res);
}
finally{
delete window.cbFn;//임시로 추가한 함수므로 함수 실행이 끝나고 나서 지운다.
script.parentNode.removeChild(script);//추가한 스크립트 테그도도 지운다.
}
}
script.src = url;
document.body.appendChild(script);//DOM에 script가 추가됨에 따라 파일 안의 함수(cnFn)가 호출된다.
}
getJSONP('jsonp.php');

그리고 호출되는 jsonp.php파일은 아래와 같이 작성하였다.

1
2
3
<?php echo $_GET['callback'];?>([
[1,2,{"name" : "owen"}]
]);

결국 jsonp.php파일안의 cbFn([[1,2,{"name" : "owen"}]]) 를 통해 전역함수인 cbFn함수가 호출되면서 콘솔 함수(console.log(res);)가 실행되는 것이다~