콜백함수는 다른 함수에 매개변수로 넘겨준 함수를 말한다. 함수를 명시적으로 호출하는 방식이 아니라 특정 이벤트가 발생했을 때 시스템에 의해 호출된다.
콜백 지옥은 JavaScript를 이용한 비동기 프로그래밍시 발생하는 문제로서, 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상을 말한다.
주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 이와 같은 코드는 가독성이 떨어지고, 코드를 수정하기 어려워 진다.
예시 ▼
step1(function (value1) {
step2(function (value2) {
step3(function (value3) {
step4(function (value4) {
step5(function (value5) {
step6(function (value6) {
// Do something with value6
});
});
});
});
});
});
콜백지옥 예제
class UserStorage{
loginUser(id, password, onSuccess, onError){
setTimeout(() =>{
if(
(id === 'seul' && password === '123') ||
(id === 'kim' && password === '456')
) {
onSuccess(id);
} else{
onError(new Error('error'));
}
}, 2000);
}
getRoles(user, onSuccess, onError){
setTimeout(()=> {
if (user === 'seul') {
onSuccess({name: 'seul', role: 'admin'});
} else {
onError(new Error('error'));
}
}, 1000);
}
};
const userStorage = new UserStorage();
const id = prompt('아이디를 입력해 주세요!');
const password = prompt('비밀번호를 입력해 주세요!!');
userStorage.loginUser(
id,
password,
user => {
userStorage.getRoles(
user,
userWithRole => {
alert(`hello ${userWithRole.name}, you have a ${userWithRole.role} role`)
},
error => {
console.log('에러2')
}
);
},
error => {console.log('에러1')}
);
class UserStorage{
loginUser(id, password){
return new Promise((resolve, reject) => {
setTimeout(() =>{
if(
(id === 'seul' && password === '123') ||
(id === 'kim' && password === '456')
) {
resolve(id);
} else{
reject(new Error('에러1'));
}
}, 2000);
})
}
getRoles(user){
return new Promise((resolve, reject) => {
setTimeout(()=> {
if (user === 'seul') {
resolve({name: 'seul', role: 'admin'});
} else {
reject(new Error('에러2'));
}
}, 1000);
})
}
};
const userStorage = new UserStorage();
const id = prompt('아이디를 입력해 주세요!');
const password = prompt('비밀번호를 입력해 주세요!!');
userStorage.loginUser(id, password)
.then(userStorage.getRoles)
// .then(user => userStorage.getRoles(user)); 인자가 똑같으니 생략 가능하다.
.then(user => alert(`hello ${user.name}, you have a ${user.role} role`))
.catch(console.log);
class UserStorage{
loginUser(id, password){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(
(id === 'seul' && password === '123') ||
(id === 'kim' && password === '456')
) {
resolve(id);
} else{
reject(new Error('에러1'));
}
}, 2000);
})
}
getRoles(user){
return new Promise((resolve, reject) => {
setTimeout(()=> {
if (user === 'seul') {
resolve({name: 'seul', role: 'admin'});
} else {
reject(new Error('에러2'));
}
}, 1000);
})
}
};
const userStorage = new UserStorage();
const id = prompt("아이디를 입력해 주세요!");
const password = prompt("비밀번호를 입력해 주세요!!");
async function checkUser() {
try {
const userId = await userStorage.loginUser(id, password);
const user = await userStorage.getRoles(userId);
alert(`Hello ${user.name}, you have a ${user.role}`);
} catch (error) {
console.log(error);
}
}
checkUser();
📌 try...catch
try...catch
문은 실행할 코드블럭을 표시하고 예외(exception)가 발생(throw)할 경우의 응답을 지정한다.try {
nonExistentFunction();
} catch (error) {
console.error(error);
}
✍️ 다른 콜백 예제들도 접해보고, 코드가 점점 복잡해지는 이유와 promise 혹은 async & await를 활용해서 어떻게 콜백지옥을 벗어날 수 있는지 보면서 더 익숙해져야 겠다.