ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [node.js] 동기와 비동기(2) Callback과 async의Waterfall
    node.js(노드) 2020. 8. 10. 16:53

    지난 글에는 동기와 비동기의 차이에 대해 알아보았다.

    이번 글에는 지난 글의 마지막의 문제인 연속된 함수에서 동기 처리하는 방법에 대해 알아보려고 한다.

     

    1. callback 처리

    callback 모형

     

     

    callback글에서 callback이 어떤 순서대로 작동하는지 알아봤다면 이번에는 실제로 어떻게 활용되는지 알아보자.

     

    *화살표 함수를 실제로도 많이 사용하니 화살표 함수에 대해 간단하게 알면 좋다.

     

    지난 글의 마지막부분을 callback으로 해결해보자.

     

    var result = false;
    
    var playA = function() {
        setTimeout(function () {
            result=true;
        },1000);
    }
    
    var playB = function () {
        if(result){
            console.log("성공");
        }
        else{
            console.log("실패");
        }
    }
    
    playA();
    playB();

    이곳에서는 playA함수가 끝나기 전에 playB의 if문이 실행되는 게 문제였다.

     

    그래서 매개변수 안에 함수를 넣어(callback) 실행시켜 동기화를 시켜준다.

     

    var result = false;
    
    var playA = function(callback) {
        setTimeout(function () {
            result=true;
            callback();
        },1000);
    }
    
    var playB = function () {
        if(result){
            console.log("성공");
        }
        else{
            console.log("실패");
        }
    }
    
    playA(playB);

    해당 함수에서 필요한곳에 callback으로 받은 함수를 넣어 실행시켜주면 된다.

    그러나 계속해서 기다려야한다면 어떻게 될까? 

    setTimeout이라는 함수가 여러개 들어있다면 아마 이렇게 될 것이다.

     

    var result = false;
    
    var playA = function(callback) {
        setTimeout(function () {
            setTimeout(function(){
                setTimeout(function(){
                    setTimeout(function(){
                        setTimeout(function(){
                            setTimeout(function(){
                                result=true;
                                callback();
                            },500);
                        },500);
                    },500);
                },500);
            },500);
        },500);
    }
    
    var playB = function () {
        if(result){
            console.log("성공");
        }
        else{
            console.log("실패");
        }
    }
    
    playA(playB);

    이렇게 되면 얼마나 지저분하겠는가?

     

    그래서 해당 문제로 발전한 것들 중 하나가 waterfall방식이다.

     

    waterfall 방식은 생성한 함수를 배열에 넣어 순서대로 진행시켜주는 것이다.

    우선 동기화 모듈인 async 모듈이 필요하다.

     

    2.waterfall 방식

    waterfall 모형

     

    아마 기존 java와 c를 사용한 사람은 위에서 하나씩 처리하면서 내려오는 것을 원할 때가 있다.

    여러 방법도 있지만 우리는 그중 하나 waterfall방식을 알아보자

    (실제 코드가 waterfall로 되어있어서 배우는 것이지 개인적으로는 async await방식을 추천한다. 그게 더 깔끔해 보이고 더 발전된 방식이다.)

     

    waterfall방식이란 위에서부터 천천히 함수 하나씩 내려오는 방식이다.

    크게 두 가지 방식이 있는데

    waterfall 안에 작업 함수를 직접 구현하는 방식

    const async = require('async');
    
    async.waterfall([
        function (callback) {
            callback(null, 'one', 'two');
        },
        function (arg1, arg2, callback) {
            console.log(arg1);       //one
            console.log(arg2);       //two
            // arg1 now equals 'one' and arg2 now equals 'two'
            callback(null, 'three');
        },
        function (arg1, callback) {
            console.log(arg1);       //three
            // arg1 now equals 'three'
            callback(null, 'done');
        }
    ], function (err, result) {
        console.log(result);    //done
        // result now equals 'done'
    });

    이 방식은 waterfall 안에 함수를 배열처럼 넣어서 순서대로 작동하게 하는 방식이다.

    가장 직관적 이기는 하지만 정리가 잘 안되어있기도 하다.

     

     

    전체적인 틀은 동일하다.

     

    우선 함수는 순서대로 진행하되 마지막에 callback은 다음 함수가 진행할지 말지를 정해준다. 

    callback의 가장 첫 번째 자리(null)에 0, undefined, null을

     

    다음 함수 function(arg1, arg2, callback)에서 one의 위치가 arg1이고 two의 위치가 arg2의 위치이다.

    이런 식으로 마지막 함수까지 진행시키면 된다.

     

    다만 꼭 다음 함수의 매개변수의 숫자와 callback의 매개변수 숫자가 같아야 한다.

    waterfall 안에 함수 이름만 달아놓고 원형은 밖에 제공하는 방식

    async.waterfall([
        myFirstFunction,
        mySecondFunction,
        myLastFunction,
    ], function (err, result) {
        // result now equals 'done'
    });
    function myFirstFunction(callback) {
        callback(null, 'one', 'two');
    }
    function mySecondFunction(arg1, arg2, callback) {
        // arg1 now equals 'one' and arg2 now equals 'two'
        callback(null, 'three');
    }
    function myLastFunction(arg1, callback) {
        // arg1 now equals 'three'
        callback(null, 'done');
    }

    이 방식은 함수 자체를 밖에다 놓고 그 안에 해당 함수가 어떤 순서대로 돌아갈지만 정하면 된다.

    나는 그 두 개를 합치는 방식으로 하겠다.

     

    배열로 함수 선언 후 waterfall 안에 해당 배열을 넣는 방식

    var task = [
        function(callback) {
            callback(null, 'one', 'two');
        },
        function(arg1, arg2, callback) {
            // arg1 now equals 'one' and arg2 now equals 'two'
            callback(null, 'three');
        },
        function(arg1, callback) {
            // arg1 now equals 'three'
            callback(null, 'done');
        }
    ];
    
    async.waterfall(task,(err,data)=>{
        if(err)
        {
            console.log(err);
        }else{
            console.log(data);
        }
    
    });
    
    

    실제 현업에서 사용한 방식은 이렇다.(내가 만들건 아님)

    일단 배열로 어떤 함수를 사용할지 순서대로 적어 놓는 다음에 마지막에 waterfall에 넣고 화살표 함수로 에러 값과 데이터를 리턴해준다.

     

     

    그렇다면 지난번 마지막 콜백 지옥을 벗어나기 위해 사용해본다면

    const async = require('async');
    
    var result = false;
    
    var task = [
        function(callback) {
            setTimeout(function (){
                result=true;
                callback(null,result);			//callback을 안에 넣어준다.
            },100)
        },
        function(arg1,callback) {
            if(arg1){			//true
                console.log("성공");
            }
            else{
                console.log("실패");
            }
            callback(null,"done");
        }
    ];
    
    async.waterfall(task,(err,data)=>{
        if(err)
        {
            console.log(err);
        }else{
            console.log(data);
        }
    
    });

    먼저 위에서  result의 값이 true로 바꾸고 위에 함수 작업이 끝난 뒤 그다음 함수가 작동하게 되기 때문에 "성공"값이 나오게 된다.

     

     

    이미지 : https://bcho.tistory.com/1083

    참조 : https://jybaek.tistory.com/745

    댓글

Designed by Tistory.