ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 콜백을 이용한 비동기 자바스크립트
    Web/자바스크립트 2021. 2. 20. 19:30
    반응형

     콜백함수를 이용하는 방법은 비동기 코드를 다루는 가장 기본적인, 그리고 오래된 방법이라고 할 수 있다.

     

     보통 비동기 코드는 데이터를 불러오거나 할 때 자주 사용되는데, 

    많은 예제들에서 AJAX 호출을 setTimeout 함수를 이용해서 흉내를 내곤 한다.

    그래서 이 글에서도 마찬가지로 setTimeout 함수를 이용해서 

    '데이터를 전부 불러오는 데에 몇 초가 걸리더라' 하는 것을 시뮬레이션할 것이다.


     자. 이제 웹 서버로부터 '레시피'를 가져오는 코드를 작성해야 하는 상황이라고 상상을 해보자. 🍕🍕 

     

    우리는 우선, 웹 서버에게 요청을 보내서 'recipeID'의 목록을 받는다.

    이 번호는 각각의 레시피들을 구분해주는 숫자이고, 이 숫자를 서버로 보내면 그 숫자에 맞는 레시피가 넘어온다.

    그러면 처음에 recipeID를 받을 때 한 번 지연이 생길 것이고,

    그 다음에 그 ID에 해당하는 레시피를 받을 때 또 한 번 지연이 생길 것이다. 

    function getRecipe() {
        setTimeout(() => {
            const recipeID = [523, 883, 432, 974]; // an array of recipe IDs
            console.log(recipeID); // 1.5초 후 [523, 883, 432, 974]
    
            setTimeout(id => {
                const recipe = {title : 'Fresh tomato pasta',
                                publisher : 'Jonas'};
                console.log(`${id}: ${recipe.title}`); // 1초 후 432: Fresh tomato pasta
            }, 1000, recipeID[2]);
        }, 1500);
    }
    
    getRecipe();

     

     setTimeout의 세 번째 인자는 콜백 함수의 인자를 의미한다.

    즉, 내가 setTimeout의 세 번째 인자로 특정 값을 넣어주면,

    그 값은 setTimeout의 콜백 함수의 인자로 들어가게 된다.

     

    여기서 보면 콜백 함수의 인자로 recipeID[2]를 넣어준 것을 알 수 있는데,

    그 말은 곧 '432'라는 recipeID에 해당하는 레시피 데이터를 받기로 결심했다는 것을 의미한다.

     

     결과적으로, 첫 번째 setTimeout에서 받았던 [523, 883, 432, 974] 라는 배열이 먼저 출력되고

    그 다음 우리가 넣어준 recipeID에 해당하는 레시피인 '432: Fresh tomato pasta' 라는 문자열이 출력된다.

    이렇게 우리는 하나의 타이머 안에 또 다른 타이머를 넣어줌으로써, 두 타이머를 연쇄적으로 연결했다.


     이제 조금 더 복잡해진다.

    이번에도 레시피를 받아오긴 하는데, 그 레시피를 작성한 publisher가 작성한 또 다른 레시피를 하나 더 받아올 것이다.

    즉, publisher를 특정해야하기 때문에 해당 publisher 정보를 인자로 받는 또 다른 setTimeout 단계가 필요해졌다.

    function getRecipe() {
        setTimeout(() => {
            const recipeID = [523, 883, 432, 974]; 
            console.log(recipeID); // 1.5초 후 [523, 883, 432, 974]
    
            setTimeout(id => {
                const recipe = {title : 'Fresh tomato pasta',
                                publisher : 'Jonas'};
                console.log(`${id}: ${recipe.title}`); // 1초 후 432: Fresh tomato pasta
         
                setTimeout(publisher => {
                    const recipe = {title : 'Italian Pizza', 
                                    publisher : 'Jonas'};
                    // 사실 여기에 Jonas 정보를 이용해서 다른 recipe를 찾는 과정은 없슴다 ㅎㅎ
                    // 그냥 publisher 인자를 활용해서 서버에서 recipe를 받아온거라고 치십쇼 ㅎㅎ
                    console.log(recipe); // 1.5초 후 {title: "Italian Pizza", publisher: "Jonas"}
                }, 1500, recipe.publisher);
            }, 1000, recipeID[2]);
        }, 1500);
    }
    
    getRecipe();

     

    Fresh tomato pasta에 관한 레시피를 작성한 'Jonas'군의 또 다른 레시피를 받아오기 위해

    세 번째 setTimeout을 만들어서, 그 콜백함수에 Jonas군의 정보를 넣어줬다.

    이렇게, 총 세 번 nesting된 세 개의 콜백 함수를 가지게 되었다.

     

     이것은 흡사, 서버로부터 데이터를 받아오기 위해 연결된 세 개의 AJAX 호출과 같다.

    사실 지금은 예제이니까 로직도 간단하고, 단계도 세 단계밖에 없지만

    실제로는 이것보다 콜백함수 갯수도 훨씬 많아질테고, 안에 포함된 로직도 더 복잡하겠지.

    그러면 그럴수록 코드를 감당하기가 점점 힘들어지게 된다. 😭

     

     예를 들어 내가 원하는 특정 데이터를 뽑아내기 위해 총 10번의 AJAX 호출이 필요하다면

    하나의 콜백 안에 또 다른 콜백 안에 또... 이런 식으로 총 10번이나 반복해야 한다.

    이렇게 콜백의 nesting이 너무 많아져서 빡시게 스트레스 받는 상황을 'Callback Hell 😈😈'이라고 부른다.


    예전에는 이 Callback Hell을 그저 버텨내며 코딩을 할 수 밖에 없었지만.

    지금은 2020년대. 당연히 이미 오래전에 해결책이 나왔다.

    이 Callback Hell을 해결하기 위해서 ES6에 들어서면서 등장한 개념이 바로 'Promise 😇😇'이다.

    Promise를 사용하면 비동기 자바스크립트를 다룰 때 Callback Hell을 피할 수 있으며, 코드가 훨씬 깔끔해진다.

    반응형

    댓글

Designed by Tistory.