Callback Hell (콜백 지옥)

seul_velog·2021년 12월 29일
1

JS note

목록 보기
4/9
post-thumbnail

콜백 함수(Callback function)

콜백함수는 다른 함수에 매개변수로 넘겨준 함수를 말한다. 함수를 명시적으로 호출하는 방식이 아니라 특정 이벤트가 발생했을 때 시스템에 의해 호출된다.

  • 콜백이란 다른 함수(A)의 전달인자(argument)로 넘겨주는 함수(B)를 말한다.
  • 매개변수를 넘겨 받은 함수(A)는 callback 함수(B)를 필요에 따라 즉시 실행(synchronously) 할 수도 있고, 아니면 나중에 (asynchronously) 실행할 수도 있다.
  • CallBack



콜백 지옥(Callback Hell)

콜백 지옥은 JavaScript를 이용한 비동기 프로그래밍시 발생하는 문제로서, 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상을 말한다.

  • 주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 이런 형태가 자주 등장하는데, 이와 같은 코드는 가독성이 떨어지고, 코드를 수정하기 어려워 진다.

  • 예시 ▼

step1(function (value1) {
    step2(function (value2) {
        step3(function (value3) {
            step4(function (value4) {
                step5(function (value5) {
                    step6(function (value6) {
                        // Do something with value6
                    });
                });
            });
        });
    });
});
  • step1에서 어떤 처리 이후 그 결과를 받아와, 인자로 전달된 익명 함수의 매개변수로 넘겨준다. 이후 step2에서 또 어떤 처리를 하고, 다음 익명 함수가 실행된다. 이를 반복하다보면 코드가 피라미드 모양으로 기술되게 된다.





콜백지옥 예제

  • 사용자 데이터를 백엔드(서버)에서 받아오는 클래스를 만든다고 가정한다.
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')}
);

1. promise 를 활용해서 콜백지옥 탈출하기

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);
  • 1) userStorage에서 로그인을 한다.
  • 2) 로그인이 성공하면 user가 전달되므로 전달된 user를 이용해서 getRoles,를 호출한다.
  • 3) getRoles 까지 성공적으로 수행 된다면 최종적으로 받아온 user를 이용해서 alert창을 나타낸다.
  • 모두 성공적으로 마쳤을 때 결과와, 도중에 실패했을 때▼
  • 마지막행의 .catch(console.log); 를 작성하지 않고 에러가 발생했을 때 ▼



2. async & await 적용하기

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)할 경우의 응답을 지정한다.
  • MDN_try...catch
try {
  nonExistentFunction();
} catch (error) {
  console.error(error);
}

✍️ 다른 콜백 예제들도 접해보고, 코드가 점점 복잡해지는 이유와 promise 혹은 async & await를 활용해서 어떻게 콜백지옥을 벗어날 수 있는지 보면서 더 익숙해져야 겠다.




HANAMON-callback
yujo-callback
librewiki
dream-coding

profile
기억보단 기록을 ✨

0개의 댓글