kimyenac
techblog
cs
Blocking vs Non-Blocking vs Sync vs Async 완전 정리
2026-03-05

백엔드나 네트워크 프로그래밍 관련 레퍼런스를 보다보면, Blocking / Non-Blocking 이라는 용어를 자주 접할 수 있습니다. 특히 Node.js, 이벤트 루프, 비동기 처리 등을 이해하려면 반드시 알아야 하는 개념이기도 합니다.

많은 사람들이 Blocking == Sync, Non-Blocking == Async 라고 생각하는 경우가 많지만, 실제로 네 가지 개념은 서로 다른 기준을 가진 개념입니다.

이번 글에서는 Blocking 과 Non-Blocking 의 개념과 두 방식의 차이, 실제 동작 흐름, 4가지 조합 등에 대해 정리해보고자 합니다. 동기와 비동기는 이전 포스팅 에서 다뤘으니 참고 부탁드립니다.




Blocking vs Non-Blocking

먼저 Blocking 과 Non-Blocking 의 기준은 "제어권이 언제 돌아오는가" 입니다.
Blocking 은 작업이 끝날 때까지 제어권을 반환하지 않지만, Non-Blocking 은 작업을 요청하면 바로 제어권을 반환하는 데 차이가 있습니다.


Blocking 동작 흐름

작업이 끝날 때까지 스레드가 기다립니다.

Thread
  │
  │ request()
  │──────────── 기다림 ────────────│
  │                                │
  │                            결과 반환
  │
  │ 다음 코드 실행
// 파일 읽기 요청 예시 코드
const data = fs.readFileSync("file.txt")
console.log(data)
console.log("next task")

위 예시 코드의 동작 순서는 파일 읽기 요청이 이뤄지고 -> 파일 읽기 완료까지 대기 -> 결과 반환 -> 다음 코드 실행으로 이뤄집니다.


Non-Blocking 동작 흐름

작업 요청 후 기다리지 않고 바로 다음 코드를 실행합니다.

Thread
  │
  │ request()
  │
  │ 다음 작업 실행
  │
  │ 다른 작업 수행
  │
  │      (작업 완료)
  │            │
  │        callback 실행
// 파일 읽기 요청 예시 코드
fs.readFile("file.txt", (err, data) => {
  console.log(data)
})

console.log("next task")

동일하게 파일 읽기 요청 예시 코드를 살펴보았을 때, 논블로킹으로 작성된 위 코드의 흐름은 파일 읽기 요청 -> 바로 다음 코드 실행 -> 파일 읽기 완료 -> callback 실행 순서로 이뤄집니다.





Sync vs Async

Sync 와 Async 의 기준을 정리해보면, "결과를 어떻게 받는가" 입니다.
Sync 는 결과를 요청한 함수가 직접 받고, Async 는 결과를 callback / promise / event 로 나중에 받습니다.


Sync 흐름

function 호출
↓
작업 수행
↓
결과 반환
↓
다음 코드 실행
// 동기 함수 예시 코드
function add(a, b) {
  return a + b
}

const result = add(1,2)
console.log(result)

Async 흐름

function 호출
↓
바로 return
↓
작업 수행
↓
callback / promise 실행
// 비동기 함수 예시 코드
fetch("/data")
  .then(res => res.json())
  .then(data => console.log(data))




왜 헷갈리는가? + 4가지 조합

많은 상황에서 다음의 조합처럼 사용되기 때문입니다.

// 대표적으로 Java I/O
Blocking + Sync

// Node.js
Non-Blocking + Async

그림으로 정리하면 다음과 같습니다.


Blocking

Thread
  │
  │ request()
  │──────── 기다림 ────────│
  │
  │ result

Non-Blocking

Thread
  │
  │ request()
  │
  │ 바로 다음 작업
  │
  │ 작업 완료 이벤트

Sync

function()
  ↓
result 반환
  ↓
다음 코드 실행

Async

function()
  ↓
바로 return
  ↓
나중에 callback 실행

Blocking / Non-Blocking 과 Sync / Async 는 독립적인 개념으로써 다음 4가지 조합이 가능합니다.


1️⃣ Blocking + Sync

가장 전통적인 방식입니다. (대표적으로 readFileSync() 함수)

request
↓
작업 수행
↓
완료까지 대기
↓
결과 반환

2️⃣ Non-Blocking + Async

대표적인 예시가 Node.js 입니다.

request
↓
바로 return
↓
다른 작업 수행
↓
작업 완료
↓
callback 실행

// 예시
fs.readFile("file.txt", callback)

3️⃣ Non-Blocking + Sync

이 방식은 polling 구조입니다.

request
↓
결과 확인
↓
아직 안됨
↓
다시 확인

// 예시
while(!done) {
  check()
}

CPU 를 많이 사용하기 때문에 비효율적일 수 있습니다.


4️⃣ Blocking + Async

조금 특이한 케이스입니다.

request
↓
작업 완료까지 기다림
↓
callback 실행

// 예시
Future.get()




Node.js 구조

Node.js 는 대표적인 Non-Blocking + Async 구조입니다.

요청
↓
I/O 작업 등록
↓
Event Loop 계속 실행
↓
I/O 완료
↓
Callback 실행

그래서 Node.js는 싱글 스레드지만 많은 요청을 동시에 처리할 수 있습니다.





마무리

가장 중요한 포인트는 Blocking / Non-Blocking 는 제어권 반환 시점이 기준이고, Sync / Async 는 결과 처리 방식이라는 것. 그리고 이 두 개념은 서로 다른 기준이기 때문에 조합이 가능하다는 점입니다.






Reference