ajax 개미지옥 callback hell !!!






javascript 에서 ajax를 통한 비동기 식으로 개발을 하다 보면 callback 함수로 인해 다들 엄청난 스트레스를 받았을것으로 예상된다. 이 callback함수로 인해 코드의 복잡성이 증가하고 가독성은 말할것도 없다. 다음과 같은 시나리오를 보자


1. /test/returnValue.jsp?value=1 을 호출 

2. 리턴 값이 1이면 /test/returnValue.jsp?value=2 호출 

3. 리턴 값이 2이면 /test/returnValue.jsp?value=3 호출 

4. 리턴 값이 3이면 정상 alert

5. 각 상황시 조건이 맞지 않으면 실패를 alert


이와 같은 형태의 시나리오는 우리가 개발을 하다 보면 종종 맞닥뜨리는 상황이다. 다음의 상황을 jQuery의 ajax를 통해 일반적으로 구현 하면서 어떠한 상황이 펼쳐지는지 확인해 보도록 하자. 

(error에 대한 처리는 코드가 복잡하게 보일 수 있으므로 개념이해를 위해 에러에 대한 처리는 무시 하도록 한다.)


우선적으로 returnValue.jsp는 최대한 단순히 구성한다 . 이 jsp의 용도는 단순히 parameter로 넘어온 value값을 그대로 전달한다.

1
2
3
4
5
<%@ page language="java" contentType="application/json; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String value = request.getParameter("value");
response.getWriter().println("{\"value\":\""+value+"\"}");
%>
cs




1. jQuery의 $.ajax를 통한 처리


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$.ajax({
    url:'/test/returnValue.jsp?value=1'
    ,success:function( data ){
        if(data['value'== '1'){
            $.ajax({
                url:'/test/returnValue.jsp?value=2'
                ,success:function( data ){
                    if( data['value'== '2' ){
                        $.ajax({
                            url:'/test/returnValue.jsp?value=3'
                            ,success:function( data ){
                                if( data['value'== '3' ){
                                    alert('성공');
                                }else{
                                    alert('실패3');
                                }
                            }
                        });
                    }else{
                        alert('실패2');
                    }
                }
            });
        }else{
            alert('실패1');
        }
    }
});
cs


위의 현상이 흔히 Callback Hell 이라고 부르는 현상이다. > 형태로 호출되어야 하는 단계가 깊어질수록 깊이도 계속 깊어진다. 조건이 복잡해지면 어디서 빠져나가고 어디로 들어가는지 분석조차 하기 힘들게 된다. 무엇보다 보기가 싫다......


이러한 Callback Hell 현상을 해결할수 있는 방법은 여러가지가 있지만 이 글에서는 Promise 패턴을 통해 해결해 보고자 한다.


Promise 패턴의 스펙은 다음 사이트에서 정의하고 있다.


http://promises-aplus.github.io/promises-spec


Promise 패턴은 스펙일 뿐이며 실제 사용을 위해서는 그것을 javascript로 구현한 구현체가 있어야 하며 이를 구현한 구현체들은 이미 많이 존재한다. q.js, when.js 등등.... 이미 수많은 라이브러리들이 존재 하지만 이 글은 어느 라이브러리가 좋냐가 아니라 Promise의 개념을 이해하는 글이기 때문에 간단히 jQuery를 통해 구현해보았다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$.ajax({
    url:'/test/returnValue.jsp?value=1'
}).then(function(data){
    if(data['value'== '1'){
        return $.ajax({
            url:'/test/returnValue.jsp?value=2'
        });
    }else{
        alert('실패1');
        throw new Error('실패1'); 
    }
}).then(function(data){
    if(data['value'== '3'){
        return $.ajax({
            url:'/test/returnValue.jsp?value=3'
        });
    }else{
        alert('실패2');
        throw new Error('실패2');
    }
}).then(function(data){
    if(data['value'== '3'){
        alert('성공')
    }else{
        alert('실패3')
    }
});
cs


첫번째 구현과 동일한 처리를 하는 로직이지만 callback으로 들어가는 depth가 현저히 줄었다. 또한 성공과 실패에 대한 처리도 한 라인에서 처리 되므로 가독성이 눈에 띄게 좋아진것을 확인 할 수 있다. 간단하게 동작 방식을 보도록 하자. 

jQuery의 $.ajax는 jqXHR 객체를 반환한다. 이 객체는 jQuery 1.5에서 Promise Interface객체를 구현한것으로 done, fail, always등을 사용 할 수 있지만 여기서는 then을 사용하여 처리하였다. then의 function의 인자로는 return 된 객체가 전달되므로 5라인과 같이 return $.ajax를 통해 chaining 처리가 가능하다. 5라인 ajax의 처리 결과는 다시 12라인의 then의 function(data)를 통해 전달된다. 값의 검증이 true인 경우는 return $.ajax로 매끄럽게 연결되지만 else의 경우는 throw new Error를 통해 로직의 흐름을 강제로 끊어야 했다.






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var step1 = function(data){
    if(data['value'== '1'){
        $.ajax({
            url:'/test/returnValue.jsp?value=2'
        }).then(step2);
    }else{
        alert('실패1');
    }
};
 
var step2 = function(data){
    if(data['value'== '2'){
        $.ajax({
            url:'/test/returnValue.jsp?value=3'
        }).then(step3);
    }else{
        alert('실패2');
    }
};
 
var step3 = function(data){
    if(data['value'== '3'){
        alert('성공');
    }else{
        alert('실패2');
    }
};
 
$.ajax({
    url:'/test/returnValue.jsp?value=1'
}).then(step1);
cs


then의 속성을 응용하여 다음과 같은 step형태로도 처리 할 수 있다. 29라인을 시작으로 각각의 $.ajax의 then을 통해 각각의 step을 연결 시키고 있다. 



Promise 패턴을 사용하면 더이상 callback을 두려워 할 필요가 없다. 물론 그렇다고 callback 패턴을 완전히 버리기는 힘들지만 callback과 promise를 적절히 활용한다면 훨씬 세련되고 깔끔한 코드를 작성 할 수 있을것이다.




Promises are a more abstract pattern of working with async code in JavaScript.
“Javascript 에서 비동기 코드를 동작시키는 더 추상적인 패턴.”

http://callbackhell.com








New Multi-Channel Dynamic CMS