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는 고유번호가 붙은 함수를 자동생성해줍니다.
(※이건 결론에서 써먹을 예정※)
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 호출을 차단합니다.
반면에 "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 |