javascript - callback hell(콜백 헬)

콜벡헬이란

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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);
setTimetout(function(){
fs.writeFile('d.txt', dataA+dataB+dataC, function(err){
if(err) console.error(err);
}, 60 * 100);
});
});
});
});

위 코드를 손으로 직접 타이핑 해보길 바란다. 콜벡이 여러개 겹쳐 있지만 콜백 자체 코드는 굉장히 심플하다. 하지만 뻥 안치고 괄호랑 중괄호 재대로 짝이 맞는지 확인을 수도없이 하게 된다.

위와 같은 코드를 콜백 헬이라고 부른다.

콜백헬의 가장큰 문제는 위처럼 콜백이 중첩될 경우 코드 자체를 알아보기 어렵다는 것이다. 더 골치 아픈 문제는 콜벡에서의 에러 처리이다.

위 예제에서는 에러를 기록하기만 했지만, 예외를 일으키려 했다면 더더욱 골치가 아파진다.

콜벡헬에서의 예외발생

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
function readSketchyFile(){
try{
fs.readFile('dose_not_exist.txt', function(err, data) {
if(err) throw err;
});
} catch(err) {
console.log('warning : minor issue occurred, program continuing');
} // _try
} // _fs

readSketchyFile();

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

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

콜백헬의대안

대표적으로 async라이브러리, Promise 등이으로 콜벡헬의 문제점을 해결할 수 있다. async라이브러리를 사용하기 보다는 callback의 발전된 형태인 Promise를 사용하는 것이 더 좋아보인다. 요즘에는 Promise보다 더 강력한 async ...await라는 것도 있다.

음 프로미스는 다음글에...