티스토리 뷰

반응형

 

 

리액트를 공부하며 aync와 await에 대해 알아보고자 한 게 어떻게 해서 이런 편리한 API가 나오게 됐는지 궁금해 찾아보다 보니 콜백 지옥부터 시작하게 되었다. 개발 공부 극초반에 애용하던 드림 코딩 by엘리 유튜브 채널에서 관련 영상을 쭉 본 적이 있는데 그때는 이 비동기적 처리 부분에서 많이 헤맸고 결국 이해를 거의 하지 못하고 넘어갔었다. 하지만 이제는 직접 사용을 해보게 되는 시기가 왔고, 피할 수 없는 중요한 개념이라는 생각이 들어 이번 기회에 다시 공부해보기로 했다.

 

포스팅에 적힌 예시 코드는 엘리님 유투브 강의 기반으로 작성했고, 추가적인 설명은 모던 자바스크립트 deep dive책을 기반으로 덧붙였다. 앞으로 세 포스팅에 걸쳐 콜백 지옥을 직접 경험해보고 이의 대안으로 나온 프로미스(promise)와 이를 더욱더 간편하게 사용할 수 있는  async await에 대해 공부해 보겠다. 

 


 

자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜백 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 번거로워지며 여러 개의 비동기 처리를  한번에 처리하는 데도 한계가 있다. 

이의 대안으로 ES6에서는 비동기 처리를 위한 또 다른 패턴으로 프로미스(Promise) 를 도입했다. 프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 있다. 

 

이번 포스팅에서는 프로미스에 대해 알기 전 동기와 비동기를 가볍게 정리하고, 콜백지옥을 의도적으로 구현해보는 시간을 가지겠다. 

 

 


 

동기와 비동기

 

자바스크립트는 동기적(synchronous)이다. 

자바스크립트는 var 변수나 함수선언들이 제일 위로 선언되는 호이스팅이 된 이후로부터 코드가 우리가 작성한 순서에 맞춰 하나하나씩 동기적으로 실행된다. 주어진 태스크(tast)를 순서대로 처리하므로 실행 순서가 보장된다는 장점이 있지만 앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹되는 단점이 있다. 

 

반면 비동기적(Asynchronoues)처리는 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크(task)를 곧바로 실행할 수 있다. 블로킹이 되는 것은 막을 수 있지만 태스크의 실행 순서가 보장되지 않는다는 단점이 있다. 간단한 예로 브라우저 웹 API인 setTimeout()가 있다. 

 

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

// output:  1 > 3 > 2

 

 

 

콜백(Callback)도 비동기적 콜백, 동기적 콜백 두 가지의 경우로 나뉜다. 자바스크립트 엔진에서는 선언된 두 함수는 호이 스팅 되어서 제일 위로 올라가게 되고 출력은 1: 동기  > 3: 비동기 > helllo: 동기 > 2: 비동기 > async callback : 비동기 순서대로 출력된다.

 

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);

// output : 1 > 3 > hello > 2 > async callback

 

 

 

 

비동기 처리로 인한 콜백 지옥 체험

 

콜백 지옥을 만들어보자. 로그인을 위해 유저의 정보를 담고 있는 UserStorage라는 클래스를 우선 만들었다.  브라우저를 실행하면 아이디와 비밀번호를 받아오고(id: ellie, pw: admin), 데이터가 맞다면 "Hello ellie, you have a admin role"이라는 prompt 창이 뜨게 된다. 

// Callback Hell example

// 로그인 하면 로그인 유저 정보는 백엔드에서 받아오는게 맞지만 setTimeout()으로 네트워크통신을 하는 것처럼
// 정보를 받아오는걸로 가정하에 진행 
/// 사용자의 역할을 잘 받아온다면 onSuccess, onError 
// 
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);
  }
}

 

위 UserStorage 클래스를 이용해서 아래의 과정을 구현해보자.

 

1. 사용자에게 id, pw 입력 받음

2. login 성공하면 onSuccess(id) 받아옴 

3. 받아온 id를 이용해 role(역할) 요청 > 잘 받아온다면 onSucces() 출력 

 

const userStorage = new UserStorage();
// 사용자에게 id, pw 받아옴 
const id = prompt("enter your id");
const password = prompt("enter your passrod");

//loginUser 인자  id, password, onSuccess, onError
userStorage.loginUser(
  id,
  password,
  // onSuccess
  (user) => {
    // getRoles 인자 user, onSuccess, onError 
    // 로그인 되었다면 사용자의 역할을 받아와야 함 
    userStorage.getRoles(
      // user
      user,
      // 데이터 잘 받아왔을 시 onSuccess
      (userWithRole) => {
        alert(
          `Hello ${userWithRole.name}, you have a ${userWithRole.role} role`
        );
        // onError 
      (error) => {
        console.log(error);
      }
    );
  },
  // onError
  (error) => {
    console.log(error);
  }
);

 

 

 

콜백지옥 단점

이렇게 콜백 지옥을 의도적으로 구현해보았다. 콜백을 이용해 콜백 함수 안에서 또 다른 것을 호출하고, 또 그 안에서 호출되는 이런 과정이  많아졌다. 이처럼 콜백 함수를 통해 비동기 처리 결과에 대한 후속 처리를 수행하는 비동기 함수가 비동기 처리 결과를 가지고 또다시 비동기 함수를 호출해야 한다면 콜백 함수 호출이 중첩되어 복잡도가 높아지는 현상이 발생하는데 이를 콜백 지옥 (Callback hell)이라 한다. 

이는 가독성 또한 좋지 않고 비동기 함수 처리 중 오류가 생기면 에러 처리가 곤란해지는 상황이 발생할 수 있다.

 

 

이를 극복하기 위해 ES6에서 나온 프로미스는 아래 포스팅에서 다루었다. 

[자바스크립트 비동기 처리 ] 프로미스(Promise) 개념과 활용


 

 

 

 

Reaference:

[Youtube] 드림 코딩 엘리  

[위키북스] 자바스크립트 deep dive

반응형