비동기 콜백함수, 콜백지옥 예시 _ 유튜브 드림코딩

라용·2022년 11월 5일
0

유튜브 드림코딩 영상을 보고 정리한 내용입니다.

자바스크립트는 기본적으로 동기적으로 동작합니다. 호이스팅이 된 이후부터 우리가 작성한 순서대로 코드가 하나하나 실행되는데, 호이스팅은 변수나 함수 선언이 자동적으로 위로 올라가는 것을 말합니다. 아래처럼 콘솔로그를 작성하면 순서대로 숫자가 찍힙니다.

console.log("1");
console.log("2");
console.log("3");

만약 아래처럼 setTimeout 함수에 콜백함수를 넣어서 비동기적 실행을 한다면, 1, 3, 2 순서로 함수가 출력됩니다.

console.log("1");
setTimeout(function () {
  console.log("2");
}, 1000);
console.log("3");

setTimeout 은 브라우저 api 이므로, 브라주에게 1초 뒤에 실행해달라고 요청하고 응답을 기다리지 않고 아래 명령으로 넘어갑니다. 이런 것이 비동기적인 실행방법입니다. 이때 setTimeout 의 첫번째 인자로 넣어준 함수를, 바로 실행하지 않고 이후에 1초 후에 실행해라, 다시 불러달라라는 의미로 콜백함수라고 부릅니다. 보통은 화살표 함수로 간결하게 작성합니다.

setTimeout(() => {
  console.log("2");
}, 1000);

콜백이 비동기일때만 쓰이는 것은 아닙니다. 콜백을 파라미터 인자로 받아서 처리하는 함수가 아래처럼 있을 때,

console.log("1");
setTimeout(() => {
  console.log("2");
}, 1000);
console.log("3");

// Synchronous callback

function printImmediately(print) {
  print();
}

printImmediately(() => {
  console.log("hello");
});

자바스크립트 엔진에 따라 함수 선언은 호이스팅 되므로 아래처럼 함수 선언을 위로 올리고 이후 아래 순서대로 실행되었을 것입니다.

function printImmediately(print) {
  print();
}

console.log("1");
setTimeout(() => {
  console.log("2");
}, 1000);
console.log("3");

// Synchronous callback

printImmediately(() => {
  console.log("hello");
});

이제 비동기로 동작하는 방식을 살펴보면, 아래 추가된 함수는 콜백함수와 시간을 인자로 전달받아 setTimeout 함수에 전달합니다.

console.log("1");
setTimeout(() => {
  console.log("2");
}, 1000);
console.log("3");

// Synchronous callback

function printImmediately(print) {
  print();
}

printImmediately(() => {
  console.log("hello");
});

// Asynchronous callback

function printWithDelay(print, timeout) {
  setTimeout(print, timeout);
}

printWithDelay(() => console.log("async callback"), 2000);

이렇게 되면, 함수 선언은 호이스팅 되고 남은 것들이 순서대로 실행됩니다. 이때 hello 는 콜백이지만 동기적으로 호출되고 2와 async callback 이 비동기적으로 호출됩니다. 이런 콜백함수를 계속 묶어나가서 쌓이게 되는 것을 콜백 지옥이라고 표현합니다.

(콜백 지옥 예시 보여주기)
아래 코드는 사용자에게 id 와 password 입력을 받아와서 해당 값으로 로그인을 하고, 이에 성공하면 사용자 id 를 받아서 그 id 로 역할을 요청해서 받아 옵니다.

class UserStorage {
  loginUser(id, password, onSuccess, onError) {
    setTimeout(() => {
      if (
        (id === "ellie" && password === "dream") ||
        (id === "coder" && password === "academy")
      ) {
        onSuccess(id);
      } else {
        onError(new Error("not found"));
      }
    }, 2000);
  }

  getRoles(user, onSuccess, onError) {
    setTimeout(() => {
      if (user === "ellie") {
        onSuccess({ name: "ellie", role: "admin" });
      } else {
        onError(new Error("no access"));
      }
    }, 1000);
  }
}

위와 같은 class 함수를 만들고 아래처럼 실행할 수 있습니다.

const userStorage = new UserStorage();
// 클래스를 만들고 

const id = prompt("enter your id");
const password = prompt("enter your password");
// id, password 입력값 받고

userStorage.loginUser(id, password, (user) => { // 로그인 성공시 실행
  userStorage.getRoles( // 로그인 성공하면 유저 역할 요청해서 받기
    user, // 유저 데이터 받고
    (userWithRole) => { // 이것을 처리하는 콜백 하나
      alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role`);
    },
    (error) => { // 에러 시 처리할 콜백
      console.log(error);
    }
  );
  (error) => { // 로그인 실패시 실행
    console.log(error);
  };
});

이렇게 콜백 체인이 이어지는 것은 콜백 지옥이라고 합니다. 가독성이 떨어지고 디버깅을 하기도 쉽지 않습니다. 그래서 promise 와 async 를 통해 이를 해결합니다.

profile
Today I Learned

0개의 댓글