개념정리 - 비동기 프로그래밍 (1)

Seungmin Shin·2021년 5월 18일
1

코딩 개념정리

목록 보기
1/33

동기? 비동기?

- 동기

비동기 자바스크립트를 이해하려면, 먼저 동기 자바스크립트를 이해해야 한다.

여기 하나의 코드가 있다.

const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  alert('You clicked me!');

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

이 코드로 생성된 버튼을 클릭하면, 경고창에 'You clicked me!'라는

문구가 나오고, 확인버튼을 누르면 그 뒤에 'This is a newly-added paragraph.'가

출력된다, 저 'This is a newly-added paragraph.'는

저 위의 'You clicked me!'이 닫히기 전까지는 출력되지 않는다.

상단 작업이 처리되는 동안 하위 작업은 발생하지 않고 렌더링이 일시중지 되는것이다.

이것이 동기식 프로그래밍이다.

- 비동기 프로그래밍이란?

나는 코딩의 개념을 실생활에 비유하는것을 좋아한다.

왜냐하면 그래야 더 이해가 잘 되기때문이다. 이번에도 비유를 해볼것이다.

우리가 청소를 할 때, 세탁기에 빨래를 넣고 세탁기를 돌리고 나면

우리는 대략 4~50분 후 세탁이 완료된 빨래를 받는다.

그런데, 빨래가 되기까지의 4~50분을 어떻게 사용하는가?

빨래가 끝날때 까지 세탁기 앞에서 기다리고 있는가? 아니다.

세탁기의 작동이 정지될때까지 우리는 다른 일을 하고 있는다.

그 시간동안 앞에서 기다리고 있으면, 나는 그 시간동안 아무것도 하지 못하기 때문이다.

만약 그 후로 설거지나, 청소를 해야 할 일이 있다면 세탁기가 돌아가는동안 하면 되고

세탁기가 빨래를 끝내면 나는 그때 빨래를 가지고 나오면 되는것이다.

코드도 마찬가지다.

일반적으로 코드는 순차적으로 진행되는것이 일반적이긴 하다.

한 코드에 한가지의 사건이 발생하면서 진행되어 내려간다.

그런데 만약 어떤 함수의 결과가 다른함수에 영향을 주게 된다면?

그 함수는 다른 함수가 끝나고 값을 산출할때 까지 기다려야 한다.

그 값을 받아 함수를 실행해야되기 때문이다.

예전에는 이런것이 당연했지만, 요즘은 컴퓨터 자체에도 여러 프로세서를 돌리는데,

그런 컴퓨터의 성능을 효율적으로 사용하지 못하는 것이다.

우리가 어떠한 작업을 실행하고, 그 작업이 완료되면 알림을 받을 수 있을 때,

우리는 그동안 다른 작업을 할 수 있게된다. 이것이 비동기 프로그래밍의 기본이다.

- Blocking code?

웹앱이 브라우저에서 특정 코드를 실행하느라 브라우저에게 제어권을 넘겨주지 않는 경우가 있다.

그럴때는 마치 브라우저가 정지된 것 처럼 보이는데, 이런 현상을 blocking 이라고 한다.

자세히 정의하기로는, 사용자의 입력을 처리하느라 웹앱이 프로세서에 대한 제어권을 브라우저에게

반환하지 않는 현상이라고 한다.

Blocking 의 예를 들어보겠다. 조금은 극단적인 예시이다.

const btn = document.querySelector('button');
btn.addEventListener('click', () => {
  let myDate;
  for(let i = 0; i < 10000000; i++) {
    let date = new Date();
    myDate = date
  }

  console.log(myDate);

  let pElem = document.createElement('p');
  pElem.textContent = 'This is a newly-added paragraph.';
  document.body.appendChild(pElem);
});

이 코드는, 버튼에 이벤트리스너를 지정해서 천만번의 날짜를 계산하게 끔 만든 코드이다.

그리고 처리가 끝난다면 간단한 한 문장 정도를 출력하게끔 해줬다.

그 후 콘솔을 열고 버튼을 클릭하면, 콘솔에 메세지가 출력되기 전까지 페이지에는

아무런 문장도 나타나지 않는다, 코드는 위에서 아래로 실행되기때문에

위의 날짜를 천만번 계산하는 코드가 끝나기 전까지 아래쪽 코드는 실행되지 않는것이다.

왜 이런 현상이 발생할까?

답은 자바스크립트는 기본적으로 single threaded (싱글스레드) 이기 때문이다.

이 시점에서 threads (스레드) 의 개념을 소개할 필요가 있을 것 같다.

- Threads?

Theads는 기본적으로 프로그램이 작업을 완료하는데 사용할 수 있는 단일 프로세스이다.

각 스레드는 한번에 하나의 작업만 수행 할 수 있다.

Task A --> Task B --> Task C

이처럼 각 작업은 순차적으로 실행되고, 다음작업을 시작하려면 앞의 작업이 완료되야 한다.

하지만 앞에서 말했듯이, 많은 컴퓨터들이 현재 여러개의 CPU코어를 가지고 있기 때문에

한번에 여러가지 일을 수행할 수 있다. Multiple thread 를 지원하는 프로그래밍 언어는

여러가지 작업을 동시에 수행 할 수 있다. 이렇게 말이다.

Thread 1: Task A --> Task B
Thread 2: Task C --> Task D


- JavaScript is single threaded.

자바스크립트는 전통적으로 싱글 thread 이다, 컴퓨터가 여러개의 cpu를 가지고 있다고 해도

main thread 라고 불리는 단일 thread 에서만 작업을 실행할 수 있다.

Main thread: Render circles to canvas --> Display alert()

자바스크립트는 이런 문제를 해결하기 위해서 몇가지 툴을 도입한다.

Web workers 라는게 있는데, 자바스크립트 청크를 동시에 실행할 수 있도록

worker라고 불리는 별도의 스레드로 보낼 수 있다. 따라서 시간이 오래걸리는 처리는

worker를 사용해서 처리하면 blocking 의 발생을 막을 수 있다.

Main thread: Task A --> Task C
Worker thread: Expensive task B


- Asynchronous code?

위에서 언급했던 Web worker 는 꽤 유용하지만, 이들도 한계가 있다.

몇가지를 말하자면, DOM에 접근할 수 없고, UI를 업데이트 하도록 worker에게 어떠한 지시도

내릴 수 없다. 또 다른 문제는, worker에서 실행되는 코드는 차단되지 않지만,

동기적으로 실행된다는 점이다. 이 문제는 함수를 사용할 때 발생한다.

어떤 함수가 일의 처리를 위해서 이전의 여러 프로세스의 결과를 return 받아야 하는 경우,

동기적으로 실행된다면 함수실행에 필요한 매개변수를 받아올 수 없는 경우의 수가 생기므로

함수는 사용자가 원하는 기능을 제대로 실행 할 수 없다.

Main thread: Task A --> Task B

아래 예시에서 Task A 는 서버로부터 이미지를 가져오고, Task B는 그 이미지에 필터를 적용하는것과 같은

작업을 수행한다고 가정해보자, Task A를 실행하고, 결과를 반환할 시간도 없이 Task B 를 실행시킨다면

에러가 발생해버린다, 왜냐하면 아직 Task A에서 이미지를 완전히 가져온 상태가 아니기 때문이다.

Main thread: Task A --> Task B --> |Task D|
Worker thread: Task C ----------> |............|

이번에는 Task D가 Task B 와 Task C의 결과를 모두 사용한다고 가정해보자.

Task B와 Task C가 동시에 아주 빠르게 결과를 반환해준다면 이상적이겠지만, 현실은 그렇지 않다.

Task D가 사용될때, Task B 와 Task C 둘 중 어느 것이라도 값이 입력되지 않았을 경우에는

에러가 발생한다.

이러한 문제를 해결하기 위해서 브라우저를 특정 작업을 통해 비동기적으로 실행할 수 있다.

바로 Promises를 사용하는 것이다.

아래 예시처럼 Task A 가 서버에서 이미지를 가져오는 동안 Task B를 기다리게 할 수 있다.

Main thread: Task A                   Task B
    Promise:      |__async operation__|

위의 작업은 다른곳에서 처리가 되므로 비동기 작업이 진행되는 동안 main thread가 차단되지 않는다.

일단은 비동기 프로그래밍의 기본에 대해서 정리했고,

다음에는 이 비동기 코드들을 어떻게 쓸 수 있는 지 정리해 보겠다.

profile
Frontend Developer

0개의 댓글