logosvg한 입 크기로 잘라먹는 리액트
Search

9. 비동기 처리

비동기 처리를 이용하면 오래 걸리는 작업이 종료될 때까지 기다리지 않고 다음 작업을 수행하는 등 유연한 프로그래밍이 가능합니다. 이번 절에서는 자바스크립트의 비동기 처리에 대해 알아보겠습니다.

동기와 비동기

자바스크립트에서 코드는 기본적으로 작성한 순서에 따라 위에서부터 아래로 순차적으로 실행합니다.
코드를 불러오는 중 입니다 ...
이처럼 순차적으로 코드를 실행하는 것을 동기(Synchronous)라고 합니다. 동기는 은행 창구 시스템에 비유할 수 있습니다. 은행 창구에서는 한 번에 한 명의 고객만 응대합니다. 따라서 은행을 방문했을 때 누군가 이미 창구에 있다면 상담이 종료되기까지 기다려야 합니다. 이렇듯 동기는 앞의 작업을 완료해야 다음 작업을 실행할 수 있습니다. 자바스크립트는 기본적으로 동기적으로 동작합니다. 동기적으로 동작하는 코드는 작성된 순서에 따라 작업이 진행되므로 작업의 흐름을 파악하기 쉽습니다.
그런데 다음과 같이 오래 걸리는 작업을 빨리 끝날 작업보다 먼저 실행하게 되면 지연 문제가 생깁니다.
코드를 불러오는 중 입니다 ...
이 코드에서 함수 shortTask는 빨리 끝나는 작업이지만 longTask가 완료되어야 실행할 수 있습니다. 따라서 진행할 모든 작업의 속도는 전체적으로 느려질 수밖에 없습니다.
이 문제를 해결하려면 앞의 작업과 관계없이 다른 작업을 별도로 진행해야 합니다.
이렇듯 특정 작업을 다른 작업과 관계없이 독립적으로 동작하게 만드는 것을 비동기(asynchronous)라고 합니다.
함수 setTimeout을 이용하면 작업을 비동기적으로 처리할 수 있습니다.
코드를 불러오는 중 입니다 ...
함수 setTimeout은 두 번째 인수로 전달된 시간(밀리초)만큼 기다린 다음, 첫 번째 인수로 전달 한 콜백 함수를 실행합니다. 따라서 이 코드에서는 3초(3000밀리초)만큼 기다린 다음 콜백 함수를 실행합니다.
이 코드는 다음과 같이 화살표 함수를 이용해 더 간결하게 작성할 수 있습니다.
코드를 불러오는 중 입니다 ...
함수 setTimeout은 비동기적으로 동작하는 함수입니다. 따라서 setTimeout이 종료 될 때까지 기다리지 않고 바로 다음 코드를 실행할 수 있습니다.
비동기는 카페에 비유할 수 있습니다. 카페의 종업원은 한 번에 여러 주문을 받습니다. 그리고 고객은 제조 순서대로 음료를 받게 됩니다. 고객은 앞서 주문한 음료가 모두 제조되기까지 기다렸다 주문할 필요가 없습니다. 앞선 작업이 종료되기까지 기다려야 했던 은행의 작업 방식과는 다릅니다.
다음은 비동기적으로 동작하는 카페를 자바스크립트 코드로 구현한 예입니다.
코드를 불러오는 중 입니다 ...
① 함수 orderCoffee는 제조할 커피 이름인 coffee와 제조에 걸리는 시간 time, 두가지를 매개변수로 저장합니다. 이 함수에서는 함수 setTimeout을 호출해 time만큼 기다린 다음 콘솔에 커피 제조가 완료되었다고 출력합니다.
코드에서는 달콤한 커피, 레몬 티, 시원한 커피 순으로 주문하였으나, 제조 시간이 빠른 순으로 음료가 출력됩니다. 따라서 제조가 빠른 순인 레몬티, 시원한 커피, 달콤한 커피 순으로 출력됩니다. 이렇듯 비동기 작업은 동기 작업과는 달리 작업의 실행 순서와 완료 순서가 일치하지 않습니다.

콜백 함수로 비동기 처리하기

다음은 1초를 기다린 다음 전달한 인수에 2를 곱해 콘솔에 출력하는 함수입니다.
코드를 불러오는 중 입니다 ...
이 코드를 실행하면 1초 후에 20이 출력됩니다. 그런데 이때 함수 double의 결과를 반환하게 하려면 어떻게 해야 할까요?
코드를 불러오는 중 입니다 ...
함수 setTimeout에서 인수로 전달한 콜백 함수가 변수 doubleNum을 반환합니다.
이 코드를 실행하면 10의 2배인 20이 아닌, 알 수 없는 숫자가 출력됩니다. 심지어 다시 실행하면 또 다른 숫자가 출력됩니다. 이는 반환값이 함수 setTimeout에서 인수로 전달한 콜백 함수가 반환하는 게 아니기 때문입니다. 함수 setTimeout은 타이머의 식별 번호를 반환합니다. 따라서 콘솔에 출력된 알 수 없는 숫자는 인수로 전달한 콜백 함수의 반환값이 아니라 타이머의 식별 번호일 뿐입니다.
✋🏼
setTimeout은 무슨 함수인가요?
함수 setTimeout은 자바스크립트의 내장 함수입니다. 그리고 콜백 함수는 함수 setTimeout에서 전달하는 인수일 뿐입니다. 따라서 콜백 함수의 반환값과 함수 setTimeout의 반환값은 무관합니다.
콜백 함수의 인수로 2를 곱한 결괏값을 전달하면, 간단하게 비동기 작업의 결괏값을 반환값으로 사용할 수 있습니다.
코드를 불러오는 중 입니다 ...
함수 double을 호출하며 두 번째 인수로 화살표 함수로 만든 콜백 함수를 전달합니다. 콜백 함수는 함수 double의 매개변수 cb에 저장되며, 호출되면 인수로 전달한 값을 콘솔에 출력합니다.  비동기 작업이 완료되면 콜백 함수를 호출해 연산의 결괏값을 인수로 전달합니다.  앞서 호출한 함수 double이 비동기 작업이므로 해당 작업의 종료를 기다리지 않고 이 먼저 실행됩니다.
이렇듯 콜백 함수를 이용하면 비동기 작업의 결괏값을 사용할 수 있습니다. 이것은 카페에서 음료 제조가 완료되면 주문 번호로 고객에게 알리는 것과 유사합니다. 고객은 음료를 주문한 다음, 주변을 둘러보거나 핸드폰을 보는 등 제조가 완료될 때 까지 다른 일을 하며 기다립니다. 종업원이 제조가 완료되었음을 알리면 그때 비로소 음료를 받습니다. 음료 주문은 비동기 작업을 요청하는 것이고 음료가 완성되어 고객을 호출하는 것은 비동기 작업이 완료된 후 콜백 함수를 호출하는 것과 같습니다.

프로미스 객체를 이용해 비동기 처리하기

프로미스(Promise)는 비동기 처리를 목적으로 제공되는 자바스크립트 내장 객체입 니다. 프로미스는 Date 객체처럼 특수한 목적을 위해 다양하게 기능이 추가된 객체 입니다. 프로미스를 이용하면 콜백 함수를 이용한 비동기 처리보다 더 쉽게 비동기 작업을 수행할 수 있습니다.
프로미스는 비동기 작업을 진행 단계에 따라 3가지 상태로 나누어 관리합니다
  • 대기(Pending) 상태: 작업을 아직 완료하지 않음 
  • 성공(Fulfilled) 상태: 작업을 성공적으로 완료함 
  • 실패(Rejected) 상태: 작업이 모종의 이유로 실패함
대기 상태에서 작업을 성공적으로 완료하는 것을 해결(resolve)이라고 합니다. 작업을 해결하면 해당 작업은 성공 상태가 됩니다. 반대로 대기 상태에서 작업이 모종의 이유(오류 발생 등)로 실패하는 것을 거부(reject)라고 합니다. 작업이 거부되면 해당 작업은 실패 상태가 됩니다.
프로미스의 3가지 상태
프로미스의 3가지 상태
유튜브로 동영상을 시청하는 상황을 떠올리면 쉽게 이해할 수 있습니다. 시청자가 특정 영상을 시청하기 위해 클릭하면 해당 영상을 불러옵니다. 영상 로딩 작업은 비동기적으로 동작합니다. 즉, 영상이 로딩되기까지 사용자는 다른 영상을 탐색하거나 댓글을 다는 등의 행동을 할 수 있습니다.
영상 로딩 과정에서 영상이 로딩 중인 상태를 ‘대기 상태’라고 할 수 있습니다. 그리고 정상적으로 영상이 로딩되었다면 이를 ‘해결’이라고 할 수 있으며, 이 상태를 ‘성공 상태’라고 말할 수 있습니다. 만약 영상이 모종의 이유로 로딩되지 못했다면 이를 ‘거부’라고 할 수 있으며, 이 상태는 ‘실패 상태’라고 말할 수 있습니다.
유튜브에 빗댄 프로미스 객체의 3가지 상태
유튜브에 빗댄 프로미스 객체의 3가지 상태
프로미스 객체는 다음과 같이 만듭니다.
코드를 불러오는 중 입니다 ...
프로미스 객체를 생성하여 상수 promise에 저장합니다.
프로미스 객체를 만들 때 인수로 실행 함수(executor)를 전달합니다. 실행 함수란 비동기 작업을 수행하는 함수입니다. 이 함수는 프로미스 객체를 생성함과 동시에 실행되며 2개의 매개변수를 제공받습니다.
다음은 프로미스 객체를 생성해 간단한 실행 함수를 인수로 전달하는 예입니다
코드를 불러오는 중 입니다 ...
이 함수는 새롭게 생성된 프로미스 객체의 실행 함수입니다. 프로미스 객체를 생성함과 동시에 실행되며 2개의 매개변수를 제공받습니다. 프로미스 객체는 생성과 동시에 실행 함수를 실행해 콘솔에 “안녕”이라고 출력합니다.
실행 함수가 제공받는 2개의 매개변수는 다음과 같습니다.
  • resolve: 비동기 작업의 상태를 성공으로 바꾸는 함수 
  • reject: 비동기 작업의 상태를 실패로 바꾸는 함수
앞서 살펴보았듯이 프로미스는 비동기 작업의 상태를 대기(Pending), 성공(Fulfilled), 실패(Rejected)로 나누어 관리합니다. 실행 함수가 제공받는 2개의 매개변수는 대기 상태의 비동기 작업을 성공 또는 실패 상태로 변경하는 역할을 합니다.
다음은 실행 함수에서 매개변수로 제공된 resolve를 호출하여 작업 상태를 성공 상태로 변경하는 예입니다.
코드를 불러오는 중 입니다 ...
실행 함수에 매개변수로 제공된 resolve를 호출해 비동기 작업의 성공을 알리며 작업의 결괏값을 인수로 전달합니다.
실행 함수는 0.5초 기다린 다음 resolve를 호출해 이 비동기 작업의 상태를 성공 상태로 변경합니다. 이때 resolve를 호출하며 인수로 전달한 값 “성공”은 비동기 작업의 결괏값이 됩니다. 이 결괏값을 비동기 작업이 아닌 곳에서 이용하려면 다음과 같이 프로미스 객체의then 메서드를 이용하면 됩니다. then 메서드는 인수로 전달한 콜백 함수의 비동기 작업이 성공했을 때 실행합니다.
코드를 불러오는 중 입니다 ...
then 메서드를 호출하고 인수로 콜백 함수를 전달합니다. 이 콜백 함수는 비동기 작업이 성공했을 때, 즉 실행 함수가 resolve를 호출했을 때 실행됩니다.
실행 함수에서 0.5초 기다린 다음 resolve를 호출하고 인수로 "성공"을 전달합니다.
따라서 then 메서드에 인수로 전달한 콜백 함수가 실행됩니다. 이때 콜백 함수의 매개변수에는 "성공"이라는 문자열이 전달됩니다. 실행 함수에서 reject를 호출하면 비동기 작업의 상태를 실패로 변경합니다. 이때 then 메서드에 전달한 콜백 함수는 실행되지 않습니다.
코드를 불러오는 중 입니다 ...
실행 함수에서 reject를 호출하여 이 작업이 실패했음을 알리고 인수로 “실패”를전달합니다.  비동기 작업이 실패했으므로 then 메서드에 인수로 전달한 콜백 함수는 실행되지 않습니다.
then 메서드는 작업이 성공했을 때 실행할 콜백 함수를 설정합니다. 따라서 작업이 실패했을 때 실행할 콜백 함수를 설정하려면 다음과 같이 catch 메서드를 사용해야 합니다.
코드를 불러오는 중 입니다 ...
비동기 작업이 실패하면 catch 메서드에 인수로 전달한 콜백 함수가 실행됩니다. 이때 실행 함수에서 reject에 전달한 인수가 매개변수로 제공됩니다.
이렇듯 프로미스의 then과 catch 메서드를 이용하면 작업이 성공하거나 실패했을때 실행할 콜백 함수를 별도로 설정할 수 있어 좀 더 유연하게 비동기 작업을 처리 할 수 있습니다.
PREV8. Date 객체와 날짜