동기(Synchronous) vs 비동기(Asynchronous) 원리

방충림·2023년 3월 12일
4

CS

목록 보기
7/26
post-thumbnail

동기와 비동기 개념적으로는 알고 있지만, 그 원리를 아는 것은 생각보다 추상적이게 느껴져 쉽지 않은 일이다. 마침 유튜브에 이 개념을 아주 쉽고 재미있게 설명해 준 영상이 있어, 그 설명에 모던자바스크립트의 설명을 보충해서 여기에 남긴다.


동기(Synchronous) vs 비동기(Asynchronous)

동기 코드는 순차적으로 실행
비동기 코드는 꼭 한 줄 한 줄 순서대로 실행되는 코드는 아닌 것.

비유를 하자면 짜장명 배달부가 짜장면을 배달하고, 주문자가 짜장면을 먹고있는 동안 다른 배달을 하다가 와서 그릇을 회수하는 것이다.

이것은 마치 프로세스, 스레드와 같다. 주문자가 짜장면을 먹는 건 배달부의 배달과는 다른 쓰레드에서 이루어 지는 것이다. 이처럼 프로그램이 비동기로 일을 한다는 건 쓰레드나 프로세스가 여럿이 돌고 있다는 말도 되는 것이다. 즉 멀티태스킹이 구현대고 있는 것이다.


동기는 [동]일한 [기]찻길에 놓인 열차들이다.
때문에 앞의 열차까 꾸물거리거나 멈춰있으면 뒤의 열차가 나아갈 수 없다.


비동기 방식은 필요에 따라서 이동이 느리거나 자주 서는 열차를 다른 선로에 배치하는 것이다.
이렇게 하면 뒤의 열차가 막히지 않을 것이다.

자바스크립트 코드로 살펴 보자

function asyncBlackBeanTimer (seconds) {
 console.log("짜장면 배달됨")
  
setTimeout(
	function () {
      console.log("식사 완료");
  },
  seconds * 1000
);
  console.log("배달부 떠남");
}

asyncBlackBeanTimer(1);

해당 코드를 콘솔로 찍어보면 위와 같은 결과나 나타남을 알 수 있다.
setTimeout이라는 함수를 살펴보면 이름없는 함수가 들어가는 것을 볼 수 있다.
이만큼의 시간이 경과한다음. 즉 비동기로 주어진 일을 다 마친 다음에는 이 함수를 실행하도록, 추후 업무를 맡겨 놓는것이다. 그 함수를 콜백함수(Callback function)라고하다.

"다 드신 뒤에는'식사완료'라고 call back 해주세요"라고 주문자에게 알려주는 것이다.

Q. 자바스크립트는 싱글쓰레드인데 어떻게 그런 비동기적인 작업이 가능한가요?

자바 스크립트는 웹 브라우저나 Node.js의 자바스크립트 엔진에서 실행된다.
이 엔진에는 자바스크립트를 돌리는 하나의 쓰레드인 자바스크림트 함수 전용 특급 선로가 있다.


호출 순서같은 레벨의 함수들의 나열이 있다고 가정해보겠다.

자바스크립트가 도는 환영에는 Javascript엔진 뿐 아니라 WebAPI라는 것이 함께 동작한다.
여기에서는 타이머를 사용하는 작업을 하거나, AJAX로 http요청을 보내거나 파일에서 데이터를 읽어오는 등 시간을 소요하는 작업을을 수행한다.

비유로 이해하는 비동기의 원리

이해를 돕기 위해 태스크를 열차에 비유한 다음과 같은 구조를 떠올려보면 좋다
JS전용 특급 선로가 있고, 비동기를 처리해주는 Wep API선로가 있다.

시간이 오래 걸리는 태스크(빨간색)가 선로의 진입부에 들어오면 컴퓨터는 이 열차를 자바스크립트용 특급 선로(콜 스택)가 아닌 브라우저나 Node.js에서 운영하는 비동기 작업용 선로에 놓는다.(이 선로는 한 번에 여럿이 만들어 질 수 있다.) 이 빨간 열차들은 보통 콜백 열차칸(초록색)을 뒤에 달고 있다. 시간이 걸리는 이동을 다 마친 열치들은 비동기 처리소 톨게이트에 도착한 순서대로 TaskQueue라는 하나의 선로에 콜백한을 올려보낸다. 이곳에는 물레방아 처럼 계속 돌아가는 한 장치가 비동기 작업인, 클릭 등의 사용자 입력으로부터 테스크 큐를 타고 들어오는 콜백 간들을 기다리고 있다. 콜함 칸들이 도착하는 대로 특급선로에 올려서 이 콜백 함수들이 자바스크렙트에서 실행되도록 하는 이 물레방가 같은 장치를 "이벤트 루프"라고 한다.
이 콜백칸들은 태스크 큐의 선로를 따라 자바 스크립트 특급 선로로 돌아오게 된다.


이처럼 시간이 걸리는 작업들(파란색)을 분리된 선로에서 처리하면, 시간이 얼마 걸리지 않는 작업(빯간색)을 특급 선로가 먼저 출력함에 있어서 얼거나 버벅거리는 일을 방지할 수 있다.


자바스크립트는 싱글 스레드 방식으로 동작한다.
이때 싱글 스레드 방식으로 동작하는 것은 브라우저가 아니라 브라우저에 내장된 자바스크립트 엔진이라는 것에 주의하기 바란다.
만약 모든 자바스크립트 코드가 자바스크립트 엔진에서 싱글 스레드 방식으로 동작한다면 자바스크립트는 비동기로 동작할 수 없다.
즉, 자바스크립트 엔진은 싱글 스레드로 동작하지만 브라우저는 멀티 스레드로 동작한다.
이러한 자바스크립트 엔진과 브라우저가 협력하며 비동기 함수를 실행하는 것이다.


Promise(ES6)의 등장

실무에서 비동기를 해결해야 하는 문제들은 훨씬 복잡할 때가 많다. 그렇게하다보면 콜백 지옥에 빠지게 된다. 가독성도 떨어지고 실수 위험도 커지며 디버깅 작업은 그야말로 지옥이 돼버린다.
이런 문제를 해결하기 위해 자바스크립티는 ES&버전부터 Promise라는 것을 도입했다.

비동기 작업을 수행하는 함수가 프로미스 객체를 반환하는데 그 생성자의 인자가 들어간 함수에 첫번째 인자로는 수행할 비동기 작업, 두번째 인자로는 그 결과물을 콜백함수를 전달하는 함수가 들어간다.

이런 식으로 작성된 프로미스 반환 함수들이 사용되는 모습을 보는 다음과 같다.

이렇게 then함수로 꼬리에 꼬리는 잇는 체이닝방식으로 비동기 작업들을 순차적으로 처리할 수 있는 것이다.


Async/Await(ES7)의 등장

그럼에도 복잡해보이는 감이 있기 때문에 ES7에서는 Async/Await 이라는 기능이 추가되었다.
프로미스로 작성한 코드들을 한층 간결하고 직관적이에 실행할 수 있게 되었다.

비동기 작업을 수행할 함수 앞에 async라는 키워드를 붙인다. 그러면 이 함수 안에다가 비동기 작업을 마치 동기 작업처럼 작성할 수 있게 된다.

이 안에 await이라는 코드를 달면 어어어 wait! 하고 코드의 진행을 멈춘다음 한생 정보를 변수에 넣어 준 다음에 다음 작업들을 수행하게 된다.
(await이 작성되지 학생정보 변수에는 학생의 정보가 아닌 프로미스 객체가 들어가버림)

이처럼 내부 동작은 비동기적 작업이지만 이렇게 동기 코드처럼 쉽고 직관적이게 작성할 수 있는 것이다.



참고문헌 : 얄팍한 코딩사전

profile
최선이 반복되면 최고가 된다.

0개의 댓글