헷갈릴 수 있는 Blocking/NonBlocking, Sync/Async 의 차이를 알아보자.
각 개념을 간단하게 그림으로 보면 다음과 같다.

Blocking과 NonBlocking의 차이는 호출하는 쪽과 호출받은 쪽 중 '제어권이 어디에 있느냐' 이다.
Blocking 방식은 호출받은 쪽이 제어권을 가진다. 따라서 호출한 쪽은 제어권을 넘겨주고, 다른 일을 하지 못한 채 대기하게 된다
NonBlocking 방식은 호출한 쪽이 제어권을 가진다. 따라서 호출한 쪽은 일을 시킨 후에도 기다리는 동안 다른 일을 수행하는 것이 가능하고, 자원을 효율적으로 사용할 수 있다.
Sync/Async의 차이는 '호출한 쪽이 호출받은 쪽의 작업을 신경 쓰느냐 신경쓰지 않느냐' 이다.
Synchronous (동기) 방식은 호출해 놓고 그 결과를 기다리거나, 다른 일을 수행하면서도 끝났는지 안끝났는지 계속해서 물어본다.
Asynchronous(비동기) 방식은 Callback 과 함께 호출하여, 그 결과를 신경쓰지 않는다. 결과를 반환받지 않고 호출받은 쪽에서 Callback을 호출하여 끝남을 알림으로서 작업을 종료한다.
Blocking/NonBlocking IO
그렇다면 Blocking IO/NonBlocking IO 컨셉의 차이가 어떻게 성능의 차이를 만들어 내는지 알아보자.
그 전에 다음 사실을 기억하자. IO 작업은 커널을 통해서만 수행 가능하다. 사용자 스레드는 IO 처리를 위해 커널에게 IO 작업을 요청한다. DB 연산, 조회 등도 IO 작업에 해당한다.
Blocking IO 방식에서 요청은 커널에 IO 작업을 요청함과 동시에 대기 상태로 들어간다. 여기서 CPU 는 스레드가 IO 작업을 하는 중에 놀지 않기 위해 컨텍스트 스위칭으로 실행 스레드를 교체한다. Blocking IO 방식에서 하나의 요청은 하나의 스레드를 의미하는데, 요청이 많아질수록 스레드도 많아져 컨텍스트 스위칭이 매우 많아지게 된다. 이 과정에서
1. 컨텍스트 스위칭에 발생하는 오버헤드
2. 각각의 스레드가 가지는 독립 메모리 공간(스택)이 메모리 과다 점유
로 인해 시스템 리소스가 매우 빠르게 고갈되게 된다.
NonBlocking IO 는 이 문제를 해결한다. 위에서 언급한 NonBlocking IO는 하나의 스레드, 또는 CPU 코어 개수(정도)의 매우 적은 수의 스레드가 수만개의 요청을 처리한다. 이것이 가능한 이유는 무엇일까?
스레드 하나로 요청을 처리하는 상황을 생각해 보자. NonBlocking 기반 구조에서 스레드는 어플리케이션 코드의 비즈니스 로직을 수행한 후, 이후의 IO 작업을 커널에게 위임한다. 같은 상황에서 Blocking IO 의 경우 커널/하드웨어의 IO 작업이 끝날때까지 대기 하며 CPU는 다른 스레드로 컨텍스트 스위칭한다. NonBlocking IO 의 경우 커널에게 IO 작업을 위임하고, 다시 다음 요청의 비즈니스 로직을 빠르게 처리하며 또다시 커널에게 IO 작업을 요청한다. 이 과정을 빠르게 반복한다. 요청에서 대부분의 시간을 잡아먹는 것은 IO 처리 시간이므로, CPU 는 IO 작업을 커널에게 던져만 두고 대기 없이 다음 요청의 처리를 수행한다.
여기서 다음 의문이 들었다.
커널에게 던져진 IO 작업들은 어떻게 병렬로 처리되는가?
우선, 대부분의 IO 작업은 CPU가 직접 데이터를 복사하거나 기다리는 방식으로 수행되지 않는다. 디스크, 네트워크 카드 같은 IO 장치는 DMA(Direct Memory Access) 를 통해 메모리와 직접 데이터를 주고받는다.
DMA 방식이란 하드웨어 장치가 주 메모리와 CPU 개입 없이 직접 데이터를 읽거나 쓰는 시스템 기능이다. 커널이 어떤 메모리에 어떤 데이터를 적재하거나 읽어올 지에 대한 정보를 패킷 형태로 전송하면, DMA 컨트롤러라는 하드웨어가 전용 시스템 버스로 대용량의 데이터를 직접 이동시킨다. 이렇게 대량의 IO 작업이 물리적으로 병렬처리되기 때문에, NonBlocking IO 모델에서는 스레드 하나로 수많은 요청을 처리할 수 있는 것이다.
즉, 애플리케이션 스레드가 커널에 IO를 요청하면,
1. 커널은 해당 IO 작업을 디바이스에 등록하고
2. 실제 데이터 전송은 하드웨어(DMA)가 수행하며
3. 작업이 완료되면 인터럽트(interrupt) 를 통해 커널에 완료 사실을 알린다.
4. 커널은 인터럽트를 받아 이벤트 형태로 어플리케이션에 전달한다.
의 흐름으로 동작한다. 이 과정에서 CPU는 전혀 대기하지 않는다. 여기에서 NonBlocking IO 모델은 이벤트 루프로 들어가 완료된 이벤트를 수신하여 처리한다. 결국 NonBlocking IO 기반 모델이 대규모 요청이 몰리는 상황에서 성능 이점을 가져갈 수 있는 이유는 요청 수 만큼의 스레드로부터 발생하는 컨텍스트 스위칭과 메모리 점유 문제를 해결하기 때문이다.
'Study' 카테고리의 다른 글
| Ollama+Spring 피드백 프로젝트 개발 2 (0) | 2026.01.17 |
|---|---|
| 커리어 나침반 문서 (1) | 2026.01.05 |
| 로드밸런싱 기본 (0) | 2025.12.25 |
| TCP/UDP (0) | 2025.12.18 |
| Cause: org/gradle/internal/enterprise/impl/GradleEnterprisePluginServices has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0 에러?(SpringB (0) | 2025.12.18 |