IT공간/JavaScript

[jQuery] jsonp 특징, 그리고 버그

dkchae 2021. 4. 21. 20:38

jQuery로 ajax호출시, 타입이 "jsonp"라면 콜백함수를 적어주는건 위험합니다.

이 버그를 발견하고나서 서비스에 존재하는 모든 "jsonpCallback : ~ " 소스라인을 지워버렸네요.

[jQuery에서는 jsonp를 이렇게 호출합니다]

$.ajax({
	type: "GET",
	data: { "testParam":"123" }
	url: "https://www.myservice.com/getUserInfoJsonp.do",
	dataType: "jsonp",
	jsonpCallback: "jsonCallbackForAlert"
});

1. jsonp는 응답이후 실행될 콜백함수를 지정할 수 있습니다.

jsonpCallback: "jsonCallbackForAlert" 같은 식으로 지정하는데요

function jsonCallbackForAlert(data){
	console.log(data);
	alert("callback data : " + JSON.stringify(data));
}

 

콜백함수는 미리 선언되어있어야 실행됩니다. 그러나 정의되어있지 않아도 오류없이 실행된다는 함정이 있죠. 심지어 콘솔에 undefined 에러조차 안뜨다니... (어이없는 부분 첫번째!)


jsonpCallback: "undefinedJsonCallbackZZZZZ" → 이렇게 짜도 이상없다는 소리임;;


참고로 콜백함수를 안써주면 jQuery는 고유번호가 붙은 함수를 자동생성해줍니다.

(※이건 결론에서 써먹을 예정※)

 

jQuery에서 지멋대로 셋팅한 콜백함수

2. jsonp는 무조건 비동기 호출입니다.

동기식으로 하고싶어서 async : "false"를 아무리 써봤자 무시됩니다.

 

3. 서버단은 콜백함수를 포함한 문자열을 응답해주어야 합니다. 서버셋팅 필수!

[스프링 서버단]

@RequestMapping(value="/getUserInfoJsonp.do")
public String getLoginUserInfo(ModelMap model, HttpServletRequest request) throws Exception{
	String callBack = request.getParameter("callback");
	ObjectMapper mapper = new ObjectMapper();
	String result = null;
	Map<String, Object> paramMap = new HashMap<String, Object>();
	
	userVO userMap =  userService.selectUserInfo(request);
	paramMap.put("id", userMap.id);
	paramMap.put("name", userMap.name);

	try {
		result = mapper.writeValueAsString(paramMap);
	} catch (Exception e) {
	}
	
	return callBack+"("+result+")";
}

자바스크립트의 eval을 쓰는 느낌이네요. 리턴되는 문자는? jsonCallbackForAlert({id:"dkchae",name:채동규"})

 

4. jsonp는 스크립트 응답이라 리턴문자가 그대로 실행됩니다.

리턴문자만 실행되는줄 알았습니다. 그러나 success와 error처리가 되어있다면 이것 또한 실행된다는 함정이 있습니다. (어이없는 부분 두번째!)

응답이후 콜백함수를 실행해놓고 또 success 부분을 탈 수도 있다는 말이죠...

 

[혼종이 되어버린 소스. 응답이 성공하면 얼럿이 두번뜹니다]

$.ajax({
	type: "GET",
	url: "https://www.myservice.com/getUserInfoJsonp.do",
	dataType: "jsonp",
	jsonpCallback: "jsonCallbackForAlert",
	success: function(data) {
		console.log(data);
		alert("sucess data : " + JSON.stringify(data));
	},
	error: function(e){
		alert("error e : " + e);
	}
});

5. jsonp는 크로스 도메인 이슈를 무시한다.

자바스크립트는 Cross Origin Resource Sharing 정책 때문에 타 도메인으로의 ajax 호출을 차단합니다.

 

dataType이 "json"인 경우. CORS정책에 의해 브라우저가 응답을 차단함

반면에 "jsonp"를 쓰게되면 3번과 같이 서버단에서 문자열만 잘 리턴해주면 차단당하지 않습니다.

 

6. 치명적인 버그가 있다.

위 내용을 바탕으로 여기 끔찍한 혼종이 되어버린 소스가 있습니다. 각 함수는 개별 실행할때는 이상이 없습니다. 그러나 동시에 비동기로 호출해버리면 간헐적으로 고장나버립니다.

 

[버그를 유발시키는 예제소스]

function runFirst(){
	$.ajax({
		type: "GET",
		url: "https://www.myservice.com/getUserInfoJsonpFirst.do",
		dataType: "jsonp",
		jsonpCallback: "undefinedJsonCallbackZZZZZ",
		success: function(data) {
			console.log(data);
			alert("first success data : " + JSON.stringify(data));
		},
		error: function(e){
			alert("first error e : " + e);
		}
	});
}
function runSecond(){
	$.ajax({
		type: "GET",
		url: "https://www.myservice.com/getUserInfoJsonpSecond.do",
		dataType: "jsonp",
		jsonpCallback: "undefinedJsonCallbackZZZZZ",
		success: function(data) {
			console.log(data);
			alert("second success data : " + JSON.stringify(data));
		},
		error: function(e){
			alert("second error e : " + e);
		}
	});
}
window.onload = function(){
		runFirst();
		runSecond();
    };

페이지 로드시 두 함수를 비동기로 실행시켰는데요, 두 호출 결과 모두 success를 탈 줄 알았습니다. 그러나!? 간헐적으로 둘 중 하나는 error가 떨어집니다. (어이없는 부분 세번째!)
원인은 ajax 호출시 콜백함수명이 동일했기 때문입니다. 서로 이름을 다르게하면 문제가 없었습니다.

추측컨대, 콜백함수명이 없을때 jQuery가 고유번호가 붙은 함수를 자동으로 만들어 준 이유는 콜백함수 재호출을 피하기 위함이었을 것입니다.

그러나 왜 콜백함수를 재호출하면 안되는지는 밝혀내지 못했습니다. jQuery 만든사람만 알겠죠.. (그냥 그런갑다;;)

이렇게 되면 콜백함수는 도대체 무슨의미가 있는걸까요?? success와 error에서만 잘 처리해줘도 전혀 문제가 없습니다.
'jsonp는 크로스 사이트 이슈를 피하기 위한 편법수단일뿐이구나!' 이걸로 마무리 지어야겠네요.


[요약]

jsonp 쓸 때 콜백함수 적어주는건 위험한 짓이다. 콜백함수 쓰지말고 success, error에서만 처리하세요.

'IT공간 > JavaScript' 카테고리의 다른 글

비구조화 할당  (0) 2022.05.16
Truthy와 Falsy  (0) 2022.05.16
JSON key 쉽게 사용해보자  (0) 2021.04.14
크롬 디버깅 2 : 쿠팡 장바구니 바꿔치기  (0) 2021.04.11
크롬 디버깅 1 : 웹 페이지 조작하기  (0) 2021.04.11