- 자바스크립트의 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다.
- 비동기 처리의 가장 흔한 사례는 제이쿼리의 ajax입니다. 제이쿼리로 실제 웹 서비스를 개발할 때 ajax 통신을 빼놓을 수가 없습니다. 보통 화면에 표시할 이미지나 데이터를 서버에서 불러와 표시해야 하는데 이때 ajax 통신으로 해당 데이터를 서버로부터 가져올 수 있기 때문입니다.
- 특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것이 비동기 처리입니다. 자바스크립트에서 비동기 처리가 필요한 이유를 생각해보면, 화면에서 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모르는데 마냥 다른 코드를 실행 안 하고 기다릴 순 없기 때문입니다.간단한 요청이아니라 요청의 갯수가 많아지고, 비동기 처리가 아니고 동기 처리라면 코드 실행하고 기다리고, 실행하고 기다리고 아마 웹 애플리케이션을 실행하는데 수십 분은 걸릴 겁니다.
- 아래 코드는 setTimeout()으로 Web API의 한 종류입니다. 코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행합니다.
비동기 처리에 대한 이해가 없는 상태에서 위 코드의 결과값을 예상해보면
- "Hello" 출력
- 3초 있다가 "Bye"출력
- "hello Again" 출력
입니다 하지만 실제로 나온 결과 값은 다릅니다- "Hello"출력
- "Hello Again"출력
- 3초있다가 "Bye"출력
- setTimeout() 역시 비동기 방식으로 실행되기 때문에 3초를 기다렸다가 다음 코드를 수행하는 것이 아니라 일단 setTimeout()을 실행하고 나서 바로 다음 코드인 console.log('Hello Again');으로 넘어갔습니다. 따라서, ‘Hello’, ‘Hello Again’를 먼저 출력하고 3초가 지나면 ‘Bye’가 출력됩니다.
- 먼저 비유로 쉽게 콜백 함수 동작 방식을 설명하겠습니다
콜백 함수의 동작 방식은 일종의 식당 자리 예약과 같습니다. 일반적으로 맛집을 가면 사람이 많아 자리가 없습니다. 그래서 대기자 명단에 이름을 쓴 다음에 자리가 날 때까지 주변 식당을 돌아다니죠. 만약 식당에서 자리가 생기면 전화로 자리가 났다고 연락이 옵니다. 그 전화를 받는 시점이 여기서의 콜백 함수가 호출되는 시점과 같습니다. 손님 입장에서는 자리가 날 때까지 식당에서 기다리지 않고 근처 가게에서 잠깐 쇼핑을 할 수도 있고 아니면 다른 식당 자리를 알아볼 수도 있습니다.
자리가 났을 때만 연락이 오기 때문에 미리 가서 기다릴 필요도 없고, 직접 식당 안에 들어가서 자리가 비어 있는지 확인할 필요도 없습니다. 자리가 준비된 시점, 즉 데이터가 준비된 시점에서만 저희가 원하는 동작(자리에 앉는다, 특정 값을 출력한다 등)을 수행할 수 있습니다.
- 콜백 지옥은 비동기 처리로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제입니다.
웹 서비스를 개발하다 보면 서버에서 데이터를 받아와 화면에 표시하기까지 인코딩, 사용자 인증 등을 처리해야 하는 경우가 있습니다. 만약 이 모든 과정을 비동기로 처리해야 한다고 하면 위와 같이 콜백 안에 콜백을 계속 무는 형식으로 코딩을 하게 됩니다. 이러한 코드 구조는 가독성도 떨어지고 로직을 변경하기도 어렵습니다. 이와 같은 코드 구조를 콜백 지옥이라고 합니다.
- 프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용합니다. 일반적으로 웹 애플리케이션을 구현할 때 서버에서 데이터를 요청하고 받아오기 위해 아래와 같은 API를 사용합니다.
- 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 마냥 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜹니다. 이와 같은 문제점을 해결하기 위한 방법 중 하나가 프로미스입니다.
- 프로미스의 3가지 상태(States)
-Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
-Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
-Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
프로미스 처리 흐름 - 출처:MDN
- 실제 서비스를 구현하다 보면 네트워크 연결, 서버 문제 등으로 인해 오류가 발생할 수 있습니다. 따라서, 프로미스의 에러 처리 방법에 대해서도 알고 있어야 합니다.
에러 처리 방법에는 다음과 같이 2가지 방법이 있습니다.
1.then()의 두 번째 인자로 에러를 처리하는 방법2.catch()를 이용하는 방법**개개인의 코딩 스타일에 따라서 then()의 두 번째 인자로 처리할 수도 있고 catch()로 처리할 수도 있겠지만 가급적 catch()로 에러를 처리하는 게 더 효율적입니다.
- async 와 await는 자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법입니다. 기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와줍니다.
async & await 기본 문법먼저 함수의 앞에 async 라는 예약어를 붙입니다. 그러고 나서 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await를 붙입니다. 여기서 주의하셔야 할 점은 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작합니다.일반적으로 await의 대상이 되는 비동기 처리 코드는 Axios 등 프로미스를 반환하는 API 호출 함수입니다.
비동기로 시작한 작업의 특징은, 그로부터 파생된 모든 작업 또한 비동기 작업으로 간주할 수 있습니다. 어느 항구 마을에서 커다란 고기잡이 배를 바다로 떠나보낸다고 가정합시다. 커다란 고기잡이 배는 비동기 작업의 시작입니다. 동이 틀 무렵 고기잡이 배는 떠났고, 그 고기잡이 배는 나름 열심히 일할 겁니다. 고기잡이 배에서 다른 소형 배를 다시 내보내든 그물을 준비하는 작업을 하든 큰 배를 떠나보낸 항구 입장에서는 신경쓸 일이 없습니다. 배 안에서 일어나는 게 비동기 작업이든 동기 작업이든, 항구 입장에서는 모두 비동기 작업입니다.
동기 환경에서 비동기 작업을 마냥 기다리는 게 의미가 없는 이유는, 그럴 바에야 그냥 동기 코드를 사용하면 되기 때문입니다! 항구는 배를 떠나보낸 뒤 어제 잡아왔던 물고기를 포장하거나 새로운 거래처를 뚫는 등 다른 작업들을 수행할 수 있습니다. 그냥 아무것도 안하고 떠나보낸 고기잡이 배를 기다리는 건 비동기 작업의 의의를 없애는 것입니다.
반면 비동기 환경에서 비동기 작업의 결과를 기다리겠다는 것은 다소 의미가 있습니다. 예를 들어 비밀 요원의 정보를 받아오는 비동기 작업인 fetchData 함수가 있는데, 이 함수의 결과로 나오는 생일을 알아야 생일 파티를 제때에 해줄 수 있는 것처럼, 마냥 기다리는 게 정답일 때도 있습니다. 생일을 알기 전까지는 생일 파티 준비를 해서는 안 됩니다. 그 때 await 을 사용하는 것입니다.하여튼 비동기는 동작 특성상 실제 작업과 그 작업의 후속조치를 따로 분리시킬 수 밖에 없는데, (그래서 then, catch 등을 썼는데) async 와 await을 쓰면 하나의 흐름 속에서 코딩할 수 있게 해줍니다! 실제 작업이 끝난 다음 그 후속조치를 수행한다. 가 아니라, 실제 작업이 끝나는 걸 기다린 다음 다음 코드를 수행한다의 느낌으로, 코딩할 수 있는 것이죠. 기다리는 게 뭐죠? 동기 코드를 쓸 때 마냥 기다렸죠? 그걸 할 수 있다는 것입니다. async와 await은 우리가 예전에 동기 코드를 작성했던 익숙한 경험 속에서 비동기 작업들을 코딩할 수 있게 해줍니다. 당장 then과 catch를 사용한 코드와 async, await 까지 활용한 코드를 비교해보면 체감이 확 될 것입니다.