Intro.
지난 게시글에서 멀티 스레딩과 성능, 그리고 그 효율 뒤에 숨겨진 컨텍스트 스위칭 비용에 대해 알아보았다. 결국 우리가 멀티 스레드를 쓰는 근본적인 이유는 "여러 일을 동시에 처리해서 응답성을 높이기 위함"이었다.
이 '응답성'을 이야기할 때 절대 빠질 수 없는 개념이 바로 동기(Synchronous)와 비동기(Asynchronous)다. 멀티 스레드와 혼동하기도 하지만, 사실 이건 '작업을 어떻게 수행하느냐'에 대한 이야기다.
동기(Synchronous)
동기 방식은 말 그대로 '동기화'를 맞춘다는 뜻이다. 어떤 작업이 시작되면 그 작업이 끝날 때까지 다음 작업은 시작하지 않고 기다린다. 카페에서 커피를 주문했는데, 직원이 커피를 다 만들어서 내 손에 쥐어 줄 때까지 내가 카운터 앞에서 꼼짝도 않고 서 있는 상황이다. 내 뒤에 줄을 선 사람들은 내가 커피를 받을 때까지 아무도 주문을 못 한다.
효율이 당연히 좋지는 않겠지만, 설계가 매우 간단하다 못해 직관적이고 쉽게 결과가 예측 가능하다는 장점이 있다.
비동기(Asynchronous)
비동기 방식은 작업을 요청해 두고, 그 작업이 끝나기를 기다리지 않은 채 바로 다음 일을 하러 가는 방식이다. 카페에서 주문을 하고 진동벨을 받은 뒤 자리에 앉아 내 할 일을 하는 상황이다. 직원이 커피를 다 만들면 진동벨로 '알림'을 주고, 그때 비로소 나는 커피를 받으러 간다. 내가 기다리는 동안에도 다른 사람들은 계속 주문을 할 수 있다.
무엇보다 효율적이지만, 설계가 복잡하다.
멀티 스레드와 비동기는 같은 거 아니야?
이런 의문이 들 수도 있다. 멀티 스레드는 누가 일을 하느냐이고, 비동기는 어떻게 일을 하느냐의 방식에 따른 것이다. 놀랍게도, 단일 스레드로도 비동기를 구현할 수 있다. (Node.js가 대표적인 예시)
싱글 스레드인 자바스크립트가 비동기 처리를 기가 막히게 잘하는 것을 보면 알 수 있듯이, 일꾼이 한 명이라도 비동기 방식으로 일하면 여러 일을 동시에 처리하는 것처럼 보일 수 있다.
왜 다 비동기 방식으로 하지 않을까?
얼핏 들으면 비동기 방식이 무조건적으로 더 좋아 보인다. 하지만 왜 모두가 이 방식을 사용하지 않을까?
우선 코드의 구현이 직관적이지 않아서 개발이 어렵다는 단점이 크다. 특히 작업을 넘기고 다음 작업할 때 오류가 발생하면 어디서 오류가 발생했는지 추적하기도 힘들다.
비동기 처리가 공짜는 아니다. 비동기를 구현하기 위해서는 이벤트 루프를 돌리고, 작업 큐에 넣고 빼는 등의 추가적인 관리 비용이 발생하게 되는데 만약 1+1을 한다고 치자. 그러면 계산 자체는 짧은 시간이 걸리는데 이걸 비동기 처리하는데 시간을 더 많이 쓰게 된다.
우리 프로그램의 로직 중에는 반드시 의존성이 강한 일들이 아주 많다. 즉, 무조건 순차적으로 실행되어야 하는 경우가 있다는 것이다. 결제 시스템이 대표적인 예시이다..
- 잔액이 있는지 확인한다.
- 잔액을 차감한다.
- 결제 완료 메시지를 보낸다.
이 과정에서 2번(차감)이 끝나기도 전에 3번(메시지)이 비동기로 실행되어 버리면? 돈은 안 빠져나갔는데 결제 완료라고 뜨는 대참사가 날 수 있다. 이렇게 순서가 생명인 로직에서는 비동기보다 동기적인 흐름이 훨씬 안전하고 명확하다.
블로킹(Blocking)
블로킹은 말 그대로 '막아버린다'는 뜻이다. 호출된 함수가 자신의 작업을 모두 마칠 때까지 제어권을 계속 갖고 있어서, 호출한 쪽은 아무것도 못 하고 그 자리에서 대기하게 만든다. 직원에게 서류 확인을 요청했는데, 직원이 "잠시만요" 하더니 서류를 다 읽을 때까지 내 손을 꽉 잡고 안 놓아주는 상황이다. 나는 직원이 서류를 다 볼 때까지 다른 업무는커녕 화장실도 못 가고 서 있어야 한다.
동기와 비슷한 느낌이라고 생각하면 된다.
논블로킹(Non-blocking)
논블로킹은 호출된 함수가 일을 시작하자마자 "일단 알겠어!" 하고 제어권을 바로 돌려주는 방식이다. 호출한 쪽은 제어권을 바로 돌려받았으니, 작업이 끝나기를 기다리는 동안 다른 일을 할 수 있다. 직원에게 서류 확인을 요청했더니, 직원이 "알겠습니다, 확인하는 동안 다른 업무 보고 계세요!"라고 말하며 바로 내 손을 놓아준다. 나는 자리에 돌아가서 다른 메일을 보내거나 전화를 받을 수 있다. 중간중간 "다 됐나요?"라고 물어볼 수도 있다.
비동기와 비슷한 느낌이라고 생각하면 된다.
동기/비동기 vs 블로킹/논블로킹
이게 제일 중요하다. "비동기면 논블로킹 아니야?"라고 생각할 수 있는데, 둘은 관심사가 다르다. 블로킹이냐 논블로킹이냐는 제어권이 핵심이다. 즉, 한 놈이 일하는 동안 다른 애들을 차단시키는 방식으로 제어권을 독점하냐 안 하냐의 차이가 핵심이다.
- 동기/비동기: 결과가 나왔을 때 누가 챙기느냐?
- 블로킹/논블로킹: 기다리는 동안 호출한 놈의 일이 중단되느냐?
이 두 가지를 조합하면 총 4가지 케이스가 나오는데, 대표적인 두 가지만 살펴보도록 하겠다.
- Sync-Blocking (가장 흔한 방식): "커피 나올 때까지 카운터 앞에서 멍하니 기다리기." 대부분의 일반적인 코드 실행 방식이다.
- Async-NonBlocking (가장 효율적인 방식): "주문하고 자리에 앉아 내 공부하기. 커피가 다 되면 진동벨이 울림." Node.js나 고성능 서버 엔진이 추구하는 방식이다.
'CS' 카테고리의 다른 글
| 🤗멀티 스레딩 수듄 ㅋㅋ (0) | 2026.03.14 |
|---|---|
| 🤯그게 그거 아닌가? 멀티 프로세스 vs 멀티 스레드 (0) | 2026.03.14 |
| 💻프로세스? 스레드? 어디서 들어는 봤는데.. (0) | 2026.03.10 |
| 😗TCP? HTTP랑 비슷한 거 아님? (0) | 2026.03.03 |
| 🤔 REST API랑 RESTful API, 뭐가 달라? (진짜 모름) (0) | 2026.02.25 |
