저번 글에서 동기와 비동기, 블로킹과 논블로킹을 정리했는데, 쓰고 나니 오히려 더 헷갈렸다. 두 쌍의 개념을 자꾸 같은 말처럼 섞어 쓰고 있었기 때문이다. 그래서 이번 글에서는 명확한 기준을 먼저 세우고 시작한다.
핵심은 두 개념이 서로 다른 질문에 답한다는 점이다. 참고로 이 글은 국내 개발 커뮤니티에서 널리 쓰는 해석, 즉 완료 통보와 제어권 양도를 각각의 기준으로 삼는 관점을 따른다. POSIX나 스티븐스(Stevens)의 네트워크 프로그래밍 책에서는 동기를 "작업이 끝날 때까지 요청한 프로세스가 블록되는 것"으로 정의하므로, 그 관점에서는 분류가 달라질 수 있다.
1. 두 개념의 기준
동기와 비동기는 완료 통보가 기준이다
동기(synchronous)는 호출한 쪽이 작업의 완료를 직접 확인하거나 결과를 기다려서 받는 방식이다. 작업의 시작과 끝이 호출한 쪽의 흐름과 맞물려 있다.
비동기(asynchronous)는 호출된 쪽이 작업을 끝낸 뒤 콜백이나 이벤트 같은 수단으로 완료를 알려 주는 방식이다. 호출한 쪽은 완료 시점을 신경 쓰지 않고 자기 일을 한다.
블로킹과 논블로킹은 제어권 양도가 기준이다
블로킹(blocking)은 호출된 쪽이 작업을 끝낼 때까지 제어권을 돌려주지 않는 방식이다. 호출한 쪽은 그동안 아무것도 하지 못하고 멈춰 있다.
논블로킹(non-blocking)은 호출되자마자 제어권을 바로 돌려주는 방식이다. 작업이 끝났는지와 상관없이 호출한 쪽은 곧바로 다음 코드를 실행할 수 있다.
2. 네 가지 조합
두 기준은 서로 독립적이므로 총 네 가지 조합이 나온다.
| 구분 | 블로킹 | 논블로킹 |
| 동기 | 호출 후 끝날 때까지 멈춰서 결과를 받는다 | 제어권은 바로 돌려받고, 완료 여부를 계속 직접 확인한다(폴링) |
| 비동기 | 완료는 통보받지만, 제어권은 돌려받지 못하고 멈춰 있다 | 제어권을 바로 돌려받고, 완료는 나중에 통보받는다 |
흔히 비동기와 논블로킹을 같은 말로 쓰는데, 이 표에서 보듯 둘은 같은 축이 아니다. 비동기이면서 블로킹인 경우도 존재한다.
3. 자바스크립트에 대입하기
자바스크립트에서는 async가 비동기를 결정하고, await가 논블로킹을 결정한다고 정리하면 기준이 선명해진다. 다만 이것은 문법 수준에서 두 기준을 대응시킨 모델이다. 실제 I/O가 논블로킹으로 처리되는 것은 Node.js의 libuv 같은 런타임 계층 덕분이고, await는 그 결과를 기다리는 문법이라는 점은 구분해서 알아 두어야 한다.
async 함수는 호출하면 항상 프로미스(Promise)를 반환한다. 결과는 프로미스가 이행(resolve)되는 시점에 통보되므로 완료 통보 기준으로 비동기가 된다. 또한 async 키워드가 별도 스레드에서 실행하라는 뜻은 아니다. async 함수의 본문은 첫 await를 만나기 전까지 호출한 쪽과 같은 스레드에서 동기적으로 실행된다.
await는 프로미스가 끝나기를 기다리는 동안 해당 함수의 실행을 일시 중단하고, 제어권을 호출한 쪽과 이벤트 루프(event loop)에 넘긴다. 함수 내부 코드는 결과가 나올 때까지 순서대로 멈춰 있는 것처럼 보이지만, 스레드 자체는 멈추지 않고 다른 일을 처리한다. 제어권을 양도하므로 논블로킹이 된다.
const url = "https://example.com/data";
async function foo() {
console.log("A");
const res = await fetch(url); // 여기서 제어권 양도
console.log("C");
return res.json();
}
console.log("1");
foo();
console.log("2");
// 출력 순서: 1, A, 2, C
foo()가 await를 만나는 순간 제어권이 바깥으로 돌아와서 "2"가 먼저 출력된다. 이 흐름이 비동기이면서 논블로킹인 경우다.
반대로 async만 붙이고 await가 없으면 어떻게 될까. 완료 통보는 프로미스로 받으니 비동기이지만, 제어권을 넘기는 지점이 없어서 함수 본문이 끝까지 실행된 뒤에야 호출한 쪽으로 돌아온다. 즉 비동기이면서 블로킹인 조합이 된다.
async function bar() {
for (let i = 0; i < 1e9; i++) {} // 제어권을 넘기는 지점이 없다
return "done";
}
console.log("1");
bar();
console.log("2"); // bar()의 반복문이 끝난 뒤에야 실행된다
정리하면 자바스크립트에서는 다음과 같이 대응된다.
- async도 없고 await도 없는 일반 함수는 동기이면서 블로킹이다.
- async만 있고 await가 없으면 비동기이면서 블로킹이다.
- async와 await가 함께 있으면 비동기이면서 논블로킹이다.
동기이면서 논블로킹인 조합은 이 두 키워드로 직접 대응되는 문법이 없다. 완료 여부를 반복해서 직접 확인하는 폴링 방식이 여기에 해당하므로, 억지로 키워드에 끼워 맞추지 않는 편이 정확하다.
4. 마무리
앞으로 이 두 개념이 헷갈리면 질문을 둘로 나누면 된다. 완료를 어떻게 알게 되는지를 물으면 동기와 비동기가 갈리고, 호출하고 나서 제어권을 바로 돌려받는지를 물으면 블로킹과 논블로킹이 갈린다. 자바스크립트에서는 async가 앞의 질문에, await가 뒤의 질문에 답한다고 기억하면 된다.
'🎓 Computer Science' 카테고리의 다른 글
| 화면 모듈 구현 (0) | 2026.08.31 |
|---|---|
| 단일 모듈 구현 (0) | 2026.08.31 |
| 테스트 관리 지원 도구의 종류 (0) | 2026.08.31 |
| 소프트웨어 개발 보안, 시큐어 코딩, Secure OS (1) | 2026.08.28 |
| 소프트웨어 패키징 - 빌드 도구, 패키징 도구, 배포본, 릴리스 노트, 매뉴얼 (0) | 2026.08.28 |