[javascript] 콜백 이해하기

Roiana·2021년 9월 6일
4
post-thumbnail

자바스크립트는 동기적이다. 바로 호이스팅이 된 이후부터 코드가 동기적으로 실행된다는 뜻이다.

호이스팅

var, 함수 선언들이 자동적으로 제일 위로 올라가는 것을 의미한다. 호이스팅이 된 이후부터 코드가 나타나는 순서대로 자동적으로 실행된다.

console.log('1');
console.log('2');
console.log('3');
// 1 2 3 순차 출력
console.log('1');
setTimeout(() => console.log('2'), 1000);	// 1초 후 실행
console.log('3');
// 1 3 2

💡 setTimeout을 만나면 브라우저 api니까 브라우저야 1초후에 실행해줘!
브라우저 API는 무조건 브라우저에게 요청을 보내게 된다.


동기적 콜백 vs 비동기적 콜백

Synchronous callback

/* ---- 기존 코드 ---- */
console.log('1');
setTimeout(() => console.log('2'), 1000);	// 1초 후 실행
console.log('3');

/* ---- 추가된 코드 ---- */
function printImmediately(print) {
  print();
}

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

위 코드를 실행하면 자바스크립트 엔진은 아래와 같이 동작될 것이다.

// 함수의 선언은 호이스팅이 된다.
function printImmediately(print) {
  print();	// ①
}

console.log('1');	// ② 출력
setTimeout(() => console.log('2'), 1000); // ③ 브라우저에게 요청 ⑥ 2 출력
console.log('3'); // ④ 출력

printImmediately(() => console.log("hello"));	// ⑤ 함수호출

// 출력 : 1 3 hello 2

Asynchronous callback

/* ---- 기존 코드 ---- */
console.log('1');
setTimeout(() => console.log('2'), 1000);	// 1초 후 실행
console.log('3');

function printImmediately(print) {
  print();
}

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

/* ---- 추가된 코드 ---- */
function printWithDelay(print, timeout) {
    setTimeout(print, timeout);
}

printWithDelay(() => console.log('async callback'), 2000);	// 2초후 출력

위 코드를 실행하면 자바스크립트 엔진은 아래와 같이 동작될 것이다.

// 함수의 선언은 호이스팅이 된다.
function printImmediately(print) {
  print();
}
function printWithDelay(print, timeout) {
    setTimeout(print, timeout);
}

console.log('1');	// 동기
setTimeout(() => console.log('2'), 1000); // 비동기
console.log('3'); // 동기

printImmediately(() => console.log("hello"));	// 동기
printWithDelay(() => console.log('async callback'), 2000);	// 비동기
// 출력 : 1 3 hello 2 async callback

😱 콜백 지옥 체험

서버는 따로 없기 때문에 타이머로 가상의 서버 통신 딜레이 시간을 부여했다.
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);   //서버와 통신이 없기에 우리가 임의로 로그인이 2초 걸린다고 해보자.
    }

    // 사용자 데이터로 역할을 다시 서버에 요청해서 데이터 받아오는 함수
    getRoles(user, onSuccess, onError) {    
        setTimeout(() => {
            if(user === 'ellie') {
                onSuccess({name: 'ellie', role: 'admin'});
            } else {
                onError(new Error('no access'));
            }
        }, 1000);
    }
}

아래는 콜백 체험 순서이다.
1. 사용자에게 id, password 입력 받기
2. 입력받은 값으로 서버에게 login 요청
3. 로그인 성공적이라면 로그인한 사용자 id를 다시 받아와서 Roles를 다시 요청
4. 역할이 성공적으로 받아와진다면 사용자의 이름, 역할을 출력

const userStorage = new UserStorage();	//클래스 선언

const id = prompt('enter your id'); // 사용자에게 id 입력받기
const password = prompt('enter your password'); // 사용자에게 password입력받기

// 받아온 값으로 로그인
userStorage.loginUser(id, password, (user) => {
      userStorage.getRoles(user, (userWithRole) => {
          alert(`Hello! ${userWithRole.name}, you have a ${userWithRole.role}`);
      }, (error) => {
          console.log(error);	// 실패
      });
  }, 
  error => {
      console.log(error)	// 실패
  }
);

콜백안에서 또다른 콜백을 전달하고, 그 안에서 또 전달 전달.. 이게 바로 콜백지옥이다 😨

😡 콜백 체인의 문제점

  1. 가독성이 너무 많이 떨어져서 한눈에 코드 파악이 어렵다.
  2. 에러가 발생하면 디버깅이 굉장히 어렵다.
  3. 유지보수할 때 지옥을 겪을 수 있다.
    이것이 바로 콜.백.지.옥

그럼 다음 포스팅에서 이 콜백지옥을 보기좋게 수정해보자.

💕 참고: 드림코딩 by 엘리
profile
꾸준히 공부하려고 노력하고있는 새싹 개발자 Roiana 입니다 😊

0개의 댓글