[JS] 동기와 비동기, 블로킹과 논블로킹이 뭐길래 나를..

Synchronous and Asynchronous, Blocking and Non-Blocking

또잉?

에러 해결을 위해 열심히 구글링하던 인피덕

그러다가 마주친 블로그에 동기와 비동기, 블로킹과 논블로킹에 대해 설명한 글이 있어서 홀린 듯이 읽어버렸는데?

비동기? 블로킹?? 논블로킹?? 게슈탈트 붕괴 상태가 된(?) 인피덕이었다… 시무룩

게슈탈트 붕괴 파괴 레츠고ㅣ

동기와 비동기, 블로킹과 논블로킹

개념부터 짚어보자

동기와 비동기가 무엇일까

여러 글을 읽어봤는데, 많은 분께서 카페를 예시로 들어 설명을 해주시더라.

나도 섭섭하지 않게 카페의 상황으로 동기와 비동기를 설명해 보겠다!

손님: ㅎㅇ

사장: ㅎㅇ

손님: 따뜻한 아아 하나 부탁.

사장: ? ㅇㅋ 조금만 기다려라

다음 손님: (언제 내 차례 와)

사장이 커피를 만들 동안 손님은 커피가 나올 때까지 기다리고 있어야 한다.

그리고 그다음 손님은 그 이후에 사장에게 주문할 수 있다.

순서가 중요한 이 카페는 동기적으로 하나씩 주문을 처리하고 있다고 볼 수 있다.

두 번째 카페를 살펴보자.

손님: ㅎㅇ

사장: ㅇㅇ ㅎㅇ

손님: 아바라 백 잔. 기깔나게 부탁.

사장: ...ㅇㅋ 진동벨 울리면 가지러 와라

손님: ㅇㅋ

사장: 다음 손님 주문 ㄱㄱ

다음 손님: ㅇㅇ 나는 차가운 뜨아 열 잔

사장: ...

두 번째 카페는 첫 번째 카페와 어떤 부분이 다른가?

사장이 첫 번째 손님의 주문을 받고, 두 번째 손님의 주문도 미리 받아서 두 가지 주문을 동시에 처리하려는 모습이 보인다.

그리고 두 번째 손님의 뜨아가 첫 번째 손님의 아바라 보다 더 빨리 나오는 경우도 생길 수도 있다.

순서에 상관없이 처리한다. 이러한 경우는 비동기로 주문을 처리한다고 볼 수 있다.

여기서 잠깐! 정리

단순하게 생각해 보자.

  • 동기(Synchronous): 작업이 순차적으로 진행되며, 요청의 순서가 보장

  • 비동기(Asynchronous): 하나의 작업이 완료되기 전에 다른 작업이 시작될 수 있고, 요청의 순서가 보장되지 않음

으로 이해할 수 있겠다.

블로킹과 논블로킹은 무엇일까

위의 카페 예시로 블로킹과 논블로킹을 설명할 수도 있다.

첫 번째 카페의 경우, 첫 번째 손님의 주문이 모두 완료되기 전까지 다음 손님의 주문이 불가능하게 막혀있는(블로킹) 상태로도 볼 수 있으므로

해당 카페는 동기적이며 블로킹 방식을 가지고 있는 카페라고 볼 수 있다.

두 번째 카페의 경우는 두 번째 손님이 첫 번째 손님과 상관없이 사장에게 바로 주문할 수 있으므로 중간에 막힌 게 하나도 없는 논블로킹 상태라고 볼 수 있다.

즉, 두 번째 카페는 비동기적이며 논블로킹 방식을 가지고 있다고 볼 수 있다.

블로킹하며 정리

  • 블로킹(Blocking): 전체적인 작업의 흐름 자체를 막아, 하나의 작업이 이미 실행되고 있는 경우 다른 작업 진행 불가

  • 논블로킹(Non-Blocking): 하나의 작업이 다른 작업에 영향을 끼치지 않아 전체적인 흐름이 막히지 않음

여러분: 흠... 동기가 블로킹이고 비동기가 논블로킹임? 그게 그거 아님?

인피덕: 그런 거 같지? 나도 그렇게 생각했지만.. 아니더라!

갑자기 예시 하나가 더 생각났는데 한번 생각해 보시라.

  • 여러분이 이 글을 위에서 아래로, 순차적으로 읽고 있다면 여러분은 동기적으로 글을 읽고 있다고 볼 수 있다.

  • 하지만 중간중간 챕터를 넘어가면서 읽는다면? 필요한 부분만 본다면? 순서대로 읽지 않는 당신은 비동기적 방식으로 글을 읽고 있는 것으로 생각할 수 있겠다.

  • 그리고 이 글이 한 챕터씩 읽어야 다음 챕터가 공개되는 구조로 되어있었다면 블로킹 방식으로 작성된 글이었겠지만,

  • 필요한 부분을 조건 없이 찾아볼 수 있기 때문에 논블로킹 방식으로 작성된 글이라고 생각할 수 있다.

만약 예시가 적절하지 않았다면 말씀 부탁드립니다. 쭈굴

다시 한번, 동기와 비동기

동기(Synchronous) 란 그리스어 Syn(함께)와 Chrono(시간)이 합쳐진 단어로,

작업 시간을 맞춰서 실행한다. 즉, 요청한 작업에 대한 완료 여부를 따져 순차적으로 처리하는 녀석으로 볼 수 있다.

요청한 작업의 순서를 매우 중요시하는 방식이라, 앞선 작업이 끝나지 않았을 경우 다음 작업을 실행하지 않는다.

  • 작업 요청: A -> B -> C, 작업 응답 결과: A -> B -> C
function syncTask() {
  console.log("1");
  // 약간 딜레이 시킴
  for (let i = 0; i < 100000000; i++);
  console.log("2");
  console.log("3");
  console.log("4");
}

syncTask();

// 출력 결과
1;
// 아주 약~간의 딜레이가 있지만
// 다음 작업을 먼저 실행시키지 않고 순차적으로 실행
2;
3;
4;

비동기(Asynchronous) 란 동기를 뜻하는 단어에 A라는 접두어(부정을 뜻함)를 붙여 작업에 대한 완료 여부를 따지지 않는 것을 의미한다.

즉, 작업의 순서를 보장하지 않으므로 작업이 순차적으로 진행되지 않을 수 있는 방식이다.

이전 작업이 끝나지 않더라도 다음 작업을 실행한다.

  • 작업 요청: A -> B -> C, 작업 응답 결과: A -> C -> B

  • 작업 요청: A -> B -> C, 작업 응답 결과: B -> A -> C 등

function asyncTask() {
  console.log("1");
  setTimeout(() => {
    // 비동기 작업
    console.log("2");
  }, 1000);
  console.log("3");
  console.log("4");
}

asyncTask();

// 출력 결과
1;
// 2가 완료되지 않았음에도 바로 다음 작업을 실행
3;
4;
2;

이처럼 동기와 비동기는 작업의 흐름과 관련 있는 녀석들로 이해할 수 있다.

비동기를 선호하는 이유

비동기로 처리했을 경우 어떠한 것을 실행하고 있을 때 해당 작업의 응답이 오지 않더라도 다른 작업을 동시에 진행할 수 있기 때문에 성능상의 이점이 있다.

동기적 관점으로 봤을 때 A라는 작업이 10분 정도 걸리는 작업이라면, 그다음 작업인 B는 무조건 10분 뒤에 처리되는데

비동기라면 A가 진행되는 동안 B를 처리할 수 있으므로 시간의 효율적인 처리가 가능하다.

따라서 개발 시에는 동기식 작업보다 비동기식 작업이 더 선호되는 편이다.

너도 다시 한번, 블로킹과 논블로킹

동기와 비동기가 처리되는 흐름에 관련된 녀석들이라면, 블로킹과 논블로킹은 전체적인 작업의 흐름에 관련된 녀석이라고 볼 수 있다.

여기부터는 제어권이라는 개념이 등장하는데, 제어권이란 자신(함수)의 코드를 실행할 권리를 뜻하니 알아두자.

블로킹(Blocking) 이란 요청한 작업이 전체적인 작업에 영향을 끼치는 녀석이다.

잘 모르겠는데용

등장인물: A(메인), B(서브)

A: 나 지금 실행 중인데 너 호출함

(B를 호출하는 부분을 마주하고 B를 호출하며 제어권을 넘김)

B: 오케이 나 실행함 엣헴 기다리거라

A: ㅇㅋ

(여기서 A는 제어권을 B에게 넘겼으므로 실행이 중지됨)

B: 나 실행 끝남

(B는 작업이 완료됐으므로 제어권을 다시 A에게 돌려줌)

A: ㅇㅋ 나 마저 실행함

(제어권을 받은 A는 자신의 함수를 실행함)

이처럼 제어권을 가지고 자신의 함수를 실행한다면 다른 함수를 호출할 수 없고 다른 작업의 실행이 막히게 된다.

즉, 현재 실행 중인 작업이 다른 함수를 호출했을 때 차단 및 대기 상태가 되는 경우 블로킹이라고 볼 수 있다.

전체적인 흐름으로 봤을 때 어떠한 함수가 다른 함수의 실행으로 인해 동작이 중지된다면 블로킹 상태라고 볼 수 있겠다.

논블로킹으로 넘어가 보자.

논블로킹(Non-Blocking) 이란 함수의 호출이 전체적인 흐름에 영향을 끼치지 않는 방식으로,

등장인물: A(메인), B(서브)

A: 나 지금 실행 중인데 너 호출함

(B를 호출하는 부분을 마주하고 B를 호출하지만, 제어권은 넘기지 않음)

B: ㅇㅇ 나 실행함

A: ㅇㅇ 나도 계속 실행하고 있을 것임

(A, B가 모두 실행 중인 상태)

B: 나 다 했음

A: ㅇㅋ 잘했다

제어권을 가진 작업이 다른 작업을 실행할 때 제어권을 넘기지 않고 실행만 해주는 녀석이다.

따라서 전체적인 흐름에는 영향을 끼치지 않고 하나의 작업이 다른 함수도 자유롭게 호출할 수 있는 상태로 이해할 수 있고,

이러한 경우를 논블로킹 방식이라고 볼 수 있다.

여러분은 이제 동기와 비동기, 블로킹과 논블로킹의 대한 개념의 학습을 완료했다! (짝짝짝)

헷갈릴 수 있는 부분!!

동기와 블로킹

동기 방식은 이전 작업이 완료될 때까지 다음 작업을 시작하지 않는 것

블로킹은 현재 작업 중인 게 있으면 다른 작업을 중단 시키는 것

비동기와 논블로킹

비동기 방식은 이전 작업이 완료되지 않아도 다음 작업을 시작하는 것

논블로킹은 현재 작업 중인 게 있더라도 다른 작업을 실행시킬 수 있는 것

근데 이제 뭐 함?

이 개념들을 종합해서 적용한다면 어떤 케이스들이 나오는지 확인해 보자.

동기 + 블로킹

작업이 차례대로 진행되며, 현재 실행 중인 작업이 완료될 때까지 다른 작업을 진행할 수 없다.

아래의 코드를 보면 파일 읽기가 끝나야 코드 마지막 줄의 콘솔이 찍히는 걸 볼 수 있다.

// 동기로 파일 읽기
try {
  const data = fs.readFileSync('/infiduk.txt', 'utf8');
  console.log(data);
} catch (e) {
  console.error(e);
}

console.log('인피덕짱');

// 출력 결과
infiduk.txt 내용
인피덕짱

동기 + 논블로킹

작업이 차례대로 진행되지만, 현재 실행 중인 작업과 관계없이 다른 작업도 진행할 수 있다.

하지만 javascript의 경우, 코드가 동기적으로 실행될 때 기본적으로 블로킹하기 때문에 실제로 활용하긴 어렵다고 한다.

비동기 + 블로킹

작업이 차례대로 진행되지 않는 상태지만, 블로킹되어 현재 실행 중인 작업이 있으면 다른 작업을 실행시킬 수 없다.

비동기 처리가 가능함에도 블로킹하여 다른 작업을 진행할 수 없도록 하는 것은 비동기의 이점을 활용하기 어려운 조합으로,

이 경우도 실제로 실제로 적용하긴 어렵다.

비동기 + 논블로킹

작업이 차례대로 진행되지 않고, 실행 중인 작업이 있더라도 다른 작업을 실행시킬 수 있다.

setTimeout, fetch 등의 javascript 비동기 함수는 대부분 이 케이스라고 볼 수 있다.

아래의 예시를 보면, 파일을 비동기로 읽고 있기에 코드 마지막 줄의 콘솔이 먼저 찍히고 파일 내용이 출력되는 걸 볼 수 있다.

// 비동기로 파일 읽기
fs.readFile("/infiduk.txt", "utf8", (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(data);
});

console.log("인피덕최고");

// 출력 결과
인피덕최고
infiduk.txt 내용

심화 학습

다음 시간에는 JS가 어떤 방식으로 처리하는지.. 알아볼까?

# 이벤트 루프

# 태스크 큐

# 애니메이션 프레임 어쩌고

살려줘

맺음말

지식++

자바스크립트 고수의 길은 험난하다 T____T

참고

이 글의 저작권은 Attribution-NonCommercial 4.0 International 라이센스를 따릅니다. Attribution-NonCommercial 4.0 International