https://yozm.wishket.com/magazine/detail/1982/

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7eb218de-2e4e-448f-a263-c7fea7beffea/EC9DBDEAB8B0_ECB18CEBA6B0ECA780_EAB5ACEAB880ED8FBC2x__1_.png

“비동기 통신에 대해 설명해 보세요.”

프론트엔드 개발자 면접에서 자주 받는 질문이다. 개발자라면 비동기 통신과 동기 통신에 대한 개념을 정확하게 알아야 하고, 이를 바탕으로 효율적인 프로그램을 만들어야 하기 때문이다. 특히 자바스크립트는 싱글 스레드로 작동하기 때문에, 효율적인 프로그램을 만들기 위해서는 비동기 처리를 적절하게 사용하는 것이 중요하다. 이번 글에서는 필자가 면접에서 받은 질문을 토대로 비동기 통신, 동기 통신에 대한 개념을 소개한다. 그리고 비슷한 개념을 가진 블로킹과 논블로킹에 대해서도 살펴보고자 한다.

동기, 비동기 통신이란?

비동기 처리는 프로세스의 완료를 기다리지 않고 동시에 다른 작업을 처리하는 방식이다. 현실 세계의 복잡한 일들은 대부분 비동기 방식으로 처리되기 때문에, 프로그래밍 세계에서도 여러 기능들이 비동기 방식으로 동작한다고 생각할 수 있다. 하지만 프로그래밍 세계에서는 각 개별 기능들이 동기적으로(순차적으로) 수행되는 것이 더 자연스럽다. 프로세스 또는 함수는 컴퓨터의 메모리나 CPU를 점유하면서 동작하는데, 기본적으로 하나의 컴퓨터는 각각의 자원을 하나씩만 가지기 때문이다.

프로그래밍 세계에서 비동기적으로(동시에) 여러 일을 수행하기 위해 점차 컴퓨터의 메모리 공간이 커졌고, 각 프로세스에 할당되는 메모리 공간이 분리되었다. 또한 CPU가 여러 프로세스와 스레드를 동시에 실행하는 멀티스레딩 방식이 생겨났다. 하지만 자바스크립트는 싱글스레드로 작동되는 프로그래밍 언어이기 때문에 동시에 두 개의 함수가 실행될 수 없다. 따라서 작업의 효율을 높이기 위해 비동기 처리가 필요하다.

2022년 AWS re:Invent 키노트 영상에서 비동기 방식에 대해 친절히 설명하고 있으니, 영상을 보고 이번 글을 읽으면 이해하기 더 쉬울 것이다.

그동안 면접에서 받은 질문들

1) 모던 브라우저는 어떻게 비동기 로직을 처리하나요?

자바스크립트는 싱글스레드 언어이기 때문에 멀티스레딩을 지원하지 않는다. 하지만 자바스크립트 함수로 호출한 브라우저의 Web API는 서로 다른 스레드에서 동시에 실행된다. 물론 서버에 요청한 API 역시 서버에서 실행되므로 자바스크립트 스레드를 방해하지 않고 수행된다. 브라우저의 콜 스택에는 호출된 함수들이 스택 구조로 쌓이는데, 비동기 함수는 처리되는 동안 콜 스택에서 기다리는 것이 아니라 곧바로 사라진다.

비동기 함수가 완료된 이후의 동작은 콜백 함수를 통해서 이루어진다. 비동기 함수를 호출할 때 인자로 콜백 함수를 넘겨주면, 콜백 함수가 별도의 메시지큐에 쌓여 다시 콜 스택에 쌓이기를 기다린다. 브라우저의 이벤트 루프는 주기적으로 콜 스택을 확인하고 콜 스택이 비면 메시지큐의 태스크를 콜 스택으로 이동시킨다. 이러한 원리로 콜 스택은 비동기 함수가 처리되는 동안 정체되지 않고 다음 태스크를 수행할 수 있다.

2) 자바스크립트는 어떻게 비동기 로직을 지원해왔나요?

전통적으로 비동기 처리는 콜백 함수를 통해 이루어졌다. 그러나 이 방식은 호출하는 비동기 함수의 깊이가 깊어질수록 에러 핸들링이 까다롭다. 콜 스택을 기준으로 안쪽 스택에서 발생한 에러는 바깥쪽의 호출자 방향으로 전파되는데, 함수의 깊이가 깊어지면 안쪽 콜 스택이 먼저 제거되어 에러가 바깥까지 전파되지 않을 수 있기 때문이다.

또한 콜백 함수를 중첩해서 호출하다 보면 소위 말하는 ‘콜백 지옥 현상’이 발생한다. 콜백 지옥이란 비동기 함수를 호출하는 과정에서 콜백 함수의 깊이가 감당하기 힘들 정도로 깊어지는 현상이다.

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/05aaa9bb-0b10-4105-a980-9af1cee7b099/image1.png

콜백 지옥 현상 <출처:Callback Hell>

자바스크립트 ES6에서는 비동기 함수의 성공과 실패 여부를 then과 catch라는 별도의 블록으로 구분해서 받을 수 있는 Promise 패턴을 지원한다. Promise 객체는 Promise 생성자 함수를 사용하여 만들며, 이 생성자 함수는 콜백 함수를 인자로 받는다.

이 함수는 resolve, reject라는 인자를 순서대로 받고, 각각의 함수를 통해 성공과 실패 케이스에 대해 처리할 수 있다. resolve 시킨 결과는 호출자 입장에서 then 블록으로 받을 수 있고, reject 시킨 결과는 호출자 입장에서 catch 블록으로 받을 수 있다. Promise 객체는 프로퍼티로 여러 개의 비동기 함수를 병렬로 수행하도록 하는 all, 모든 비동기 함수가 모두 완료된 후에 수행하도록 하는 allSettled 등의 정적 메서드도 가지고 있어서 활용도가 높다.