LHJ

I'm a FE developer.

14.2.4 콜백 헬

20 May 2020 » js_lj

14.2.4 콜백 헬

콜백을 사용해 비동기적으로 실행할 수 있긴 하지만, 현실적인 단점이 있습니다.
한 번에 여러가지를 기다려야 한다면 콜백을 관리하기가 상당히 어려워집니다.
노드 앱을 만든다고 합시다.
이 앱은 세 가지 파일의 컨텐츠를 읽고, 60초가 지난 다음 이들을 결합해 네 번째 파일에 기록합니다.

const fs = require('fs');

fs.readFile('a.txt', function(err, dataA) {
    if (err) console.error(err);
    fs.readFile('b.txt', function(err, dataB) {
        if (err) console.error(err);
        fs.readFile('c.txt', function(err, dataC) {
            if (err) console.error(err);
            setTimeout(function() {
                fs.writeFile('d.txt', dataA+dataB+dataC, function(err) {
                    if (err) console.error(err);
                })
            }, 60 * 1000)
        })
    })
})

프로그래머들은 이런 코드를 콜백 헬이라 부릅니다.
중괄호로 둘러싸여 끝없이 중첩된 삼각형의 코드 블록들은 마치 버뮤다 삼각지대처럼 보일 지정입니다.
더 골치 아픈 문제는 에러 처리입니다.
이 예제에서는 에러를 기록하기만 했지만, 예외를 일으키려 했다면 더더욱 골치가 아팠을 겁니다.
다음 예제를 보십시오.

const fs = require('fs');
function readSketchyFile() {
    try {
        fs.readFile('does_not_exist.txt', function(err, data) {
            if (err) throw err;
            console.log(data);
        });
    } catch (err) {
        console.log('warning: minor issue occurred, program continuing');
    }
}
readSketchyFile();

이 코드는 얼핏 타당해 보이고, 예외 처리도 수행하는 방어적인 코드처럼 보입니다.
동작하지 않는다는 것만 빼면 말입니다.
직접 실행해 보십시오.
예상되는 에러가 문제를 일으키지 않도록 대비했는데도 프로그램은 멈춥니다.
예외 처리가 의도대로 동작하지 않는 이유는 try ... catch 블록은 같은 함수 안에서만 동작하기 때문입니다.
try ... catch 블록은 readSketchyFile 함수 안에 있지만, 정작 예외는 fs.readFile이 콜백으로 호출하는 익명 함수 안에서 일어났습니다.

또한, 콜백이 우연히 두 번 호출되거나, 아예 호출되지 않는 경우를 방지하는 안전장치도 없습니다.
콜백이 정확히 한 번만 호출될 것을 가정하고 코드를 작성한다면, 애석하지만 자바스크립트는 그런 걸 보장하지는 않습니다.

이런 문제가 해결할 수 없는 문제는 아닙니다.
하지만 비동기적 코드가 늘어나면 늘어날수록 버그가 없고 관리하기 쉬운 코드를 작성하기는 매우 어려워집니다.
그래서 프로미스가 등장했습니다.