동기와 비동기를 공부하다 보면 거의 반드시 blocking, non-blocking이라는 말도 따라옵니다. 문제는 이 두 쌍이 비슷해 보여서 자꾸 같은 뜻처럼 느껴진다는 점입니다.
하지만 실제로는 다른 축의 개념입니다. 동기/비동기는 “결과를 언제 이어서 처리하느냐”에 가깝고, blocking/non-blocking은 “호출한 쪽의 제어가 언제 돌아오느냐”에 더 가깝습니다.
이 글에서는 아래 내용을 정리합니다.
- blocking 과 non-blocking 이 무엇인지
- 동기/비동기와 왜 다른 개념인지
- 어디서 많이 헷갈리는지
핵심은 blocking 은 호출한 쪽이 멈춰 서는지의 문제이고, 동기/비동기는 결과 처리 흐름의 문제라는 점입니다.
Blocking 이란 무엇인가
blocking 호출은 작업이 끝날 때까지 호출한 쪽이 제어를 돌려받지 못하는 방식입니다. 즉, 호출하고 나면 끝날 때까지 기다려야 합니다.
예를 들어 파일 읽기 함수가 blocking 이라면:
- 파일 읽기 호출
- 읽기 완료까지 멈춤
- 완료 후 다음 코드 실행
이 구조에서는 호출한 스레드나 흐름이 그대로 붙잡힙니다.
Non-Blocking 이란 무엇인가
non-blocking 호출은 작업이 아직 끝나지 않았더라도 호출한 쪽에 제어를 빨리 돌려주는 방식입니다. 즉, 호출 후 바로 다른 일을 이어서 할 수 있습니다.
예를 들어 네트워크 요청을 보낸 뒤 곧바로 제어가 돌아오면, 호출한 쪽은 그동안 다른 작업을 진행할 수 있습니다.
즉, non-blocking 의 핵심은 “호출한 쪽이 덜 붙잡힌다”는 점입니다.
왜 동기/비동기와 헷갈릴까
둘 다 “기다리느냐 안 기다리느냐” 이야기처럼 보이기 때문입니다. 하지만 보는 지점이 다릅니다.
- 동기/비동기: 결과를 어떤 흐름으로 처리하는가
- blocking/non-blocking: 호출 후 제어가 언제 돌아오는가
그래서 조합도 여러 가지가 가능합니다.
네 가지 조합을 간단히 보면
1. 동기 + blocking
가장 직관적인 형태입니다. 호출하고, 끝날 때까지 기다리고, 그 결과를 바로 이어서 처리합니다.
2. 비동기 + non-blocking
웹 개발에서 자주 떠올리는 전형적인 비동기 형태입니다. 호출 후 제어가 바로 돌아오고, 결과는 나중에 callback, Promise, event 같은 방식으로 처리합니다.
3. 동기 + non-blocking
입문자에게는 덜 익숙하지만 개념적으로 가능합니다. 호출 후 제어는 빨리 돌아오지만, 결과를 얻는 쪽은 여전히 순차적으로 맞추는 방식이 있을 수 있습니다.
4. 비동기 + blocking
겉으로는 비동기 구조처럼 보여도, 중간에 join, get, await 결과를 바로 강제로 기다림 같은 패턴이 들어가면 실제 호출 흐름은 다시 blocking 처럼 느껴질 수 있습니다.
이 마지막 경우가 실무에서 특히 많이 헷갈립니다.
가장 쉬운 질문
헷갈릴 때는 질문을 둘로 나누면 좋습니다.
- 호출한 뒤 제어가 바로 돌아오는가? -> blocking / non-blocking
- 결과를 지금 바로 순차 처리하는가, 나중에 이어 처리하는가? -> 동기 / 비동기
이렇게 나누면 개념이 훨씬 덜 섞입니다.
왜 이 차이가 중요할까
이 차이를 이해하면 “비동기로 짰는데 왜 느리지?” 같은 질문도 풀기 쉬워집니다.
예를 들어:
- 구조는 async 인데 중간에서 blocking wait 를 해 버릴 수 있다
- non-blocking I/O 를 썼는데 결과 처리 흐름은 여전히 동기적으로 묶일 수 있다
즉, 코드가 비동기 모양이라고 해서 항상 non-blocking 이고 효율적인 것은 아닙니다.
자주 하는 오해
1. 비동기면 무조건 non-blocking 이다
항상 그렇지는 않습니다. 중간에 결과를 강제로 기다리는 패턴이 들어가면 blocking 성격이 다시 강해질 수 있습니다.
2. blocking 은 나쁜 것이고 non-blocking 은 좋은 것이다
문제에 따라 다릅니다. 단순한 스크립트나 순차 흐름에서는 blocking 이 더 이해하기 쉬울 수 있습니다.
3. 개념만 알면 실제 코드도 저절로 이해된다
실제로는 Promise, async/await, event loop, executor, callback 구조까지 같이 봐야 감이 더 잘 잡힙니다.
공부할 때 추천하는 연결
이 개념을 이해했다면 다음으로는 JavaScript 쪽 비동기 문법을 보는 것이 좋습니다.
- callback
Promiseasync/await
이 흐름은 Promise 와 async/await 가이드에서 이어서 보면 좋습니다.
FAQ
Q. blocking 이면 항상 동기인가요?
많이 겹쳐 보이지만, 개념적으로 완전히 같은 말은 아닙니다.
Q. non-blocking 이면 병렬 처리인가요?
아닙니다. 제어를 빨리 돌려준다는 뜻이지, 실제 동시 실행과는 다른 이야기입니다.
Q. await 는 blocking 인가요?
문맥에 따라 다르게 느껴질 수 있습니다. 언어와 런타임이 어떤 식으로 제어를 돌리는지 같이 봐야 합니다.
Read Next
- 실제 JavaScript 비동기 문법이 궁금하다면 Promise 와 async/await 가이드를 이어서 읽어보세요.
- 브라우저와 Node.js 에서 이런 흐름이 실제로 어떻게 굴러가는지는 Event Loop 가이드에서 이어집니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.