-
비동기 프로그래밍에 관하여 - 1. 콜백(Callback)front-end/ES6 2022. 2. 21. 10:23728x90
자바스크립트는 동기적(synchronous)이다.
호이스팅이 된 이후 순차적으로 코드가 실행된다. 정해진 순서대로 코드가 실행되기 때문에 예측가능하다.
반대로 비동기적(asynchoronous)이라는 것은 언제 코드가 실행될 지 예측할 수 없다는 뜻이다.
가장 대표적인 예로 WebAPI인 setTimeout()가 있다.
console.log(1); setTimeout(function () { console.log(2); }, 1000); console.log(3);
2는 바로 출력되지 않고 지연된 후 출력이 되었다. setTimeout은 비동기적 함수이기 때문에 해당 함수를 처리할 때 까지 기다리는 것이 아니라 바로 다음 라인으로 넘어가서 코드를 실힝하게 되었다.
그리고 setTimeout안에 function() {console.log(2)}와 같이
어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 호출되는 함수를 'callback' 함수라고 한다.
그렇다면 콜백은 항상 비동기일 때만 쓰일까?
아니다.
콜백함수는 크게 동기적 콜백함수와 비동기적 콜백함수로 볼 수 있다.
다음은 동기적 콜백함수의 예시이다.
console.log(1); setTimeout(function () { console.log(2); }, 1000); console.log(3); //Syncronous callback function printImmediately(print) { print(); } printImmediately(() => console.log("hello"));
printImmediately()가 먼저 실행이 된 후 setTimeout()이 실행되었다. 자바스크립트는 실행될 때 호이스팅이 일어난다.
변수와 함수의 선언이 맨 위로 올라가게 된다.
function printImmediately(print) { print(); } console.log(1); setTimeout(function () { console.log(2); }, 1000); console.log(3); printImmediately(() => console.log("hello"));
다음과 같은 순서로 코드가 실행되었을 것이다.
다음은 비동기적 콜백함수의 예시이다.
console.log(1); setTimeout(function () { console.log(2); }, 1000); console.log(3); //Syncronous callback function printImmediately(print) { print(); } printImmediately(() => console.log("hello")); //Asyncronous callback function printWithDelay(print, timeout) { setTimeout(print, timeout); } printWithDelay(() => console.log("async callback"), 2000);
setTimeout에 지정한 딜레이 시간에 의해 실행순서가 바뀌었다. 마찬가지로 호이스팅이 일어나기 때문에 다음과 같이 자바스크립트 코드가 실행되었을 것이다.
//Syncronous callback function printImmediately(print) { print(); } //Asyncronous callback 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);
콜백 함수는 상황에 맞게 로직을 처리할 수 있기 때문에 유용하지만,
만약 함수안에 콜백 함수가 있고,
또 그 안에 콜백 함수가 있고,
또 그 안에 콜백 함수가 있고,
또 그 안에 콜백 함수가 있고,,,,,
콜백지옥(callback hell) 현상이 나타나버린다.
콜백 지옥이란,
콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상이다.
주로 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행할 때 나타날 수 있는 현상이다.
콜백 지옥 함수를 만들어보고 또 해결해 나가보도록 하자.
먼저 UserStorage라는 class를 선언한다. 여기에는 총 2가지의 api가 있다.
사용자가 로그인할 때 사용할 loginUser()
id, password를 받아오고
로그인이 정상적으로 수행되었을 때 onSuccess를, 에러가 발생했을 때 onError를 콜백함수로 실행한다.
그리고 사용자의 계정정보를 받아오는 getRoles
사용자 계정정보(admin, user)를 받아오고
마찬가지로 로그인이 정상적으로 수행되었을 때 onSuccess를, 에러가 발생했을 때 onError를 콜백함수로 실행한다.
(백엔드에 요청을 보낼 때 사용자 정보를 일괄적으로 받아오는 것이 맞으나 여기서는 따로 받아온다고 예시를 들어보자)
class UserStorage { loginUser(id, password, onSuccess, onError) { } getRoles(user, onSuccess, onError) { } }
실제 백엔드가 구현되지 않았기 때문에 setTimeout을 이용하여 딜레이를 주어 마치 통신하는 듯한 형태로 구현해 보자.
//Callback Hell ex class UserStorage { loginUser(id, password, onSuccess, onError) { setTimeout(() => { if ( (id === "user" && password === "user") || (id === "admin" && password === "admin") ) { onSuccess(id); } else { onError(new Error("not found")); } }, 2000); } getRoles(user, onSuccess, onError) { setTimeout(() => { if (user === "admin") { onSuccess({ name: "admin", role: "admin" }); } else { onError(new Error("no access")); } }, 1000); } } const userStorage = new UserStorage(); const id = prompt("enter user id"); const password = prompt("enter user password"); userStorage.loginUser( id, password, (user) => { userStorage.getRoles( user, (userRole) => { alert(`hello ${userRole.name}, you have a ${userRole.role} role`); }, (error) => { console.log(error); } ); }, (error) => { console.log(error); } );
promt를 통해 id와 password를 받아온 후,
loginUser()를 통해 로그인 성공여부를 판단하고, 만약 로그인이 성공했다면
getRoles()를 콜백함수로 호출하여 admin 계정 사용 여부(admin 계정만)를 알려준다.
어려운 로직은 전혀 아니지만 콜백이 콜백을 불러오는 현상때문에 가독성이 현저히 떨어진다.
또한 어떤 일을 수행하기 위한 것인지 비즈니스 로직 파악도 어렵다.
콜백 체인이 길어지면 길어질 수록 에러가 발생한 포인트를 찾거나 디버깅하는 것이 어려워 질 것이다.
당연히 그에 따라 유지 보수도 어려워진다.
위와 같은 콜백 지옥은 어떻게 해결할 수 있을까?
다음 포스팅인 비동기처리를 위한 Promise에서 확인해보자.
728x90'front-end > ES6' 카테고리의 다른 글
개체 참조 및 복사 (0) 2022.02.21 비동기 프로그래밍에 관하여 - 3. async / await (0) 2022.02.21 비동기 프로그래밍에 관하여 - 2. 프로미스(Promise) (0) 2022.02.21 화살표 함수(Arrow Function) (0) 2022.02.21 스코프에 관하여 (var, let, const) (0) 2022.02.21