순서
1. 프론트 엔드에서 비동기를 어떻게 처리해야 할까?
2. Promise와 Async Function은 어떻게 사용할까?
유튜브 [10분 테코톡] 📖 카일의 프론트엔드의 비동기
영상에서는 비동기를 가장 잘 나타낼 수 있는 단어를 간극(시간 사이의 틈)이라 한다
즉 현재시점과 나중의 시점에서 그 사이 어떤 틈이 존재할 수 있다는 얘기다
프론트 엔드에서는 이 틈이 엄밀히 다루어져야 하는 이유를 생각해보면, 과거에 단방향의 정보 전달로만 하는 페이지에서 현재 사용자와 상호작용을 이끄는 앱으로 넘어온 흐름이다
그래서 사용자와 가장 밀접하게 닿아있는 프론트 엔드 영역에서는 주기적으로 발생하는 인터렉션을 처리, 연속적으로 변경되는 정보를 실시간으로 사용자에게 보여줄 수 있어야 했다
그러기 위해서는 모든 인터렉션을 들어온 그대로만 처리할 수는 없었다
타이머를 사용한 이벤트, 서버와의 네트워크 통신, 애니메이션 등
간극을 예측할 수 없는 여러 요인들이 생겨나면서 대기시간이 발생하게된다
이러한 반복적이고 긴 대기시간은 답답함을 느낀 유저의 이탈률은 빠르게 증가하고, 웹 이용률의 하락, 경제적 손실이 발생한다
그래서 프론트엔드에서 비동기 처리가 필요한 이유는, '무언가를 기다려야 하는건 유저가 아닌 브라우저의 역할'이 되기 때문이다
자바스크립트 개발자들은 꽤 오랜 시간 동안 비동기 처리를 위해 위와같은 콜백 함수를 사용해왔다
하지만 요즘은 단순히 이 방법으로만 비동기를 처리하지는 않는다
영상에서의 제어의 역전 예시는 기업과 하청 업채에 비유했다
한 기업이 사내 프로젝트를 진행할 때 원자재를 하청 업체에 수주를 맡기는 방식을 채택(기업에서는 흐름에 집중할 수 있다)했을 때 , 여기서 기업은 하청 업체에 견적서를 통해 그 원자재로 수행해야할 기업의 다음 업무를 요청했다고 가정한다
이러한 상황에서 하청업체가 그 일을 부담하는 것을 약속한 상태이기 때문에 문제가 없어보이지만, 하청업체 내부의 변수로 중간 과정에 차질이 생겨도 기업은 처리 과정을 확실히 알 방법이 없다
만약 원하는 결과를 얻을 수 없어도, 그 사실을 알 수만 있다면 적절한 대비법을 알 수도 있었겠지만 불가능하다 = 신뢰할 수 없음
위의 문제점을 해결하기 위해
이런한 매커니즘이 Promise이다
아래는 callBack을 사용했을 때의 코드이다
// callBack을 사용했을 때
function browserTasks(){
// browerTask가 호출될 때 console.log(~)는 제어의 대상이 된다
console.log('sync task');
// 그에비해 비동기 요청과 함께 전달된 콜백은
// 외부라이브러리에 대한 의존성을 가지게 되어 제어권의 주체가 뒤바뀌게 된다
asyncRequest(asyncTask);
}
function asyncRequest(callBackFn){
ajax('url',function(data){
callBackFn(data);
// 이러한 비동기 요청,콜백 호출로 이어지는 흐름은 외부에서 관찰, 제어가 불가능하다
})
}
function asyncTask(data){
console.log(data)
}
아래는 promise를 사용해 변환한 코드이다
function request(){
// promise를 만들 때 인자로 들어오는 함수를
// executor 함수 라고 하는데, 이 함수는 비동기 요청의 수행 결과에 따라
// 넘겨줄 데이터를 콜백으로 받는다
// !! 유의할 점은 execute함수는 Promise객체가 만들어지는 즉시 실행된다는 점이다
// 그래서 버튼 클릭같은 반복적인 일에는 이와같은 방법이 적절하지 않다
return new Promise(function(resolve, reject){
ajax('url',function (data){
if (data) {
resolve(data);
} else {
reject('Error!');
}
});
});
}
function asyncTask(){
const promise = request();
promise
.then(function (data){
// data를 이용한 작업수행
})
.catch(function(error){
// error를 이용한 작업수행
})
}
콜백의 단점을 말할 때 가장 쉬운 예시인, 콜백지옥 이라 불리는 코드를 볼 수 있다
그러면 콜백 지옥은 단순히 코드가 길어서 읽기 힘든 것일까?
일정 부분을 차지하지만 오히려 비동기 방식 자체의 한계점이라 생각한다고 한다
카일님은 사람이 생각하는게 순차적이기 때문에 코드를 읽는 과정에서 중요한 전제라고 생각하고, 비동기 코드는 동작 방식의 특정상 직선적인 추론을 제시하지 못하기 때문에 "비동기 방식 자체의 한계점"이라 표현했다고 한다
function A(callback){
console.log('A');
setTimeout(() => callback(),0);
}
function B(){
console.log('B');
}
function C(){
console.log('C');
}
A(B);
C();
// 코드는 A - C - B 의 순서로 이루어 진다
이와같은 코드의 진행을 추론하기 위한 개발자의 디버깅 과정을 피로하게 한다
ES6 시기에 Promise의 구조화된 콜백으로 조금은 가독성을 챙길 수 있지만 비동기 코드 자체의 가독성 문제를 해결하진 못해서 개발자들은 동기코드처럼 보일 수 있도록 생각했다
Async Await이 등장하기 전, 이와같은 고민을 1차적으로 해결했던 방법은 Generator이다
Generator함수는 온전히 비동기 처리를 위해 나온 것은 아니지만, 비동기를 동기적으로 보이도록 하는데에 꽤 유용했다
Generator의 꼭 알아야 하는 특징은
*
: gernerator함수를 작성하기 위한 규칙, function키워드 뒤나 식별자 앞에 선언lterator
: generator 호출로 반환된 객체, next()를 가지고 있다.next()
: generator 함수 안의 yield문으로 넘어가기 위한 메서드yield
: next()가 호출 되었을 때, 1.중간에 멈추고 2.데이터를 받는 지점function *asyncTask(){
const data = yield request();
// 받아온 data를 이용한 일련의 작업 수행
}
function request(){
ajax('url',function(data){
it.next(data);
});
}
const it = asyncTask();
it.next();
generator를 이용하면 함수를 중간에 중단할 수 있다는 특징과, 함수의 중간 지점에서 값을 보낼 수 있따는 특징이 있어서 외부의 값을 기다렸다가, 받은 시점에서 함수가 실행되도록 만들었다
그래서 ES6개발자들은 generator와 promise를 같이 사용해서 비동기 코드의 가독성과 신뢰성을 향상시켰다
하지만 이 두 문법을 매번 섞어 써야하는 불편함이 있었고, 더 직관적이고 간결한 사용을 원하게 되었다
async function asyncTask(){
// await은 뒤에 있는 주체가 Promise일 때만 간극을 기다린다
// 만약 함수 수행과정 중 에러가 난다면 해당 에러를 throw한 것과 동일한 동작 수행
const data = await request();
// 받아온 데이터를 이용한 일 수행
}
function request(){
return new Promise(resolve => {
ajax('url',function(data){
resolve(data);
})
})
}
Async function의 특징을 보면 generator함수의 매커니즘과 닮아 있는 것을 확인할 수 있다
주석에 있는 await의 특징을 보면 async function
을 사용하면 프로미스의 흐름을 관찰할 수 있기 때문에 가독성 + 폭넓은 예외처리가 가능해진 것을 확인할 수 있다
Promise
를 병렬적으로 처리할 수 없다결국 모든 상황에 적용 가능한 완벽한 비동기 처리는 없다
현재 많이 사용되는 async function
를 사용하기 전 비동기처리 과정을 순차적으로 설명을 들은 느낌이라 현제 사용되는 async function
에 대해 이해하지 못했던 것('코드가 순차적으로 진행되는 것을 위한 거면 "동기"를 위한거 아닌가?'하는 고민 등) 들이 조금은 이해되는 것 같다
비동기 처리를 많이 할 일이 없어서 그냥 async await
을 사용했었는데, 비동기 처리를 위해 발전해온 과정에서 장점만이 아닌 문제점도 같이 알 수 있어서 좋았다