동기/비동기와 블로킹/논블로킹 (Sync/Async and Blocking/Non-blocking)

한눈에 보기

동기와 비동기는 완료를 누가 챙기는가, 블로킹과 논블로킹은 제어권을 돌려받는가의 구분이다. 두 축의 2x2 조합 네 가지를 식당 비유와 이벤트 루프 동작으로 정리한다.

최종 업데이트 2026.09.17

동기(synchronous)와 비동기(asynchronous)는 작업의 완료를 누가 챙기는가에 대한 관심사의 구분이고, 블로킹(blocking)과 논블로킹(non-blocking)은 호출한 쪽이 제어권을 돌려받는가에 대한 구분이다. 두 축은 서로 독립적이므로 조합하면 동기 블로킹, 동기 논블로킹, 비동기 블로킹, 비동기 논블로킹의 네 가지 방식이 나온다. 이 두 축을 분리해서 이해하는 것이 비동기 프로그래밍과 I/O 모델을 정확히 설명하는 출발점이다.

두 축은 서로 다른 질문이다

블로킹과 논블로킹은 "호출 직후 제어권이 어디에 있는가"를 묻는다. 호출한 함수가 결과가 나올 때까지 반환하지 않으면 블로킹이고, 결과와 무관하게 즉시 반환해 호출자가 계속 실행되면 논블로킹이다. 즉 호출자 입장에서 그 순간 다른 일을 할 수 있느냐의 문제이며, 스레드가 대기 상태로 잠기는지 아닌지가 관찰 가능한 기준이 된다.

동기와 비동기는 관점이 다르다. 이 축은 "작업의 완료를 누가 확인하고 후속 처리를 하는가"를 묻는다. 호출자가 작업 완료를 직접 기다리거나 반복 확인해서 결과를 챙기면 동기이고, 완료 시점에 콜백(callback)이나 이벤트, 프로미스(promise) 해결 같은 통지를 받아 피호출자 쪽이 완료를 알려 주면 비동기이다. 즉 흐름의 주도권이 호출자에게 있으면 동기, 통지 메커니즘에 있으면 비동기라고 정리할 수 있다. 두 축을 뭉뚱그려 "비동기는 안 기다리는 것"이라고만 답하면 정확성이 떨어지는 이유가 여기에 있다.

2x2 조합으로 보는 네 가지 방식

조합동작 방식대표 예
동기 + 블로킹호출자가 멈춰서 결과를 기다리고, 결과를 직접 받아 다음으로 진행일반적인 read 시스템 콜, JDBC 쿼리 호출
동기 + 논블로킹호출이 즉시 반환되지만, 호출자가 완료 여부를 반복 확인(폴링)논블로킹 소켓에 대한 재시도 루프, 작업 상태 API를 주기적으로 조회
비동기 + 블로킹완료 통지를 쓰기로 해 놓고도 호출자가 결과를 기다리며 멈춤비동기 API 호출 직후 결과를 동기적으로 기다리는 get 또는 await성 대기, select 계열 호출에서의 대기
비동기 + 논블로킹호출이 즉시 반환되고, 완료되면 콜백이나 이벤트로 통지받아 처리자바스크립트 fetch와 프로미스, 자바 CompletableFuture, io_uring

동기 블로킹은 코드가 위에서 아래로 읽히는 가장 직관적인 방식이고, 비동기 논블로킹은 자원 효율이 가장 좋은 방식이다. 동기 논블로킹은 제어권은 유지하지만 폴링으로 CPU를 소모하는 어정쩡한 형태라 단독으로는 잘 쓰이지 않는다. 비동기 블로킹은 비동기의 이점을 스스로 버리는 조합이라 대개 잘못된 사용의 결과로 나타나는데, 비동기 프레임워크 안에서 무심코 결과를 동기 대기하면 이 형태가 되어 스레드가 잠기고 전체 처리량이 급락한다.

동기/비동기와 블로킹/논블로킹 차이: 두 축을 2×2로 조합한 네 가지 I/O 방식과 각각의 대표 예
표를 외우려 하지 말고 두 질문을 따로 던지면 된다. «호출이 바로 돌아오나?»(블로킹 축)와 «결과를 내가 확인하나, 알려주나?»(동기 축). 왼쪽 아래 칸이 어색해 보이는 이유는 비동기로 시켜 놓고 굳이 기다리기 때문이고, 그래서 실무에서는 거의 대각선 두 칸만 쓴다.

식당 비유로 정리하기

주문 후 카운터 앞에 서서 음식이 나올 때까지 기다리면 동기 블로킹이다. 주문하고 자리로 돌아가되 몇 분마다 카운터에 가서 "나왔나요"라고 묻는다면 동기 논블로킹이다. 진동벨을 받아 놓고도 카운터 앞을 떠나지 않고 서 있으면 비동기 블로킹이다. 진동벨을 받아 자리에서 다른 일을 하다가 벨이 울리면 찾으러 가는 것이 비동기 논블로킹이다. 이 비유에서 "내가 그 자리에 묶여 있는가"가 블로킹 축이고, "완료를 내가 확인하는가, 벨이 알려 주는가"가 동기 축이라는 점이 드러난다.

비동기 논블로킹이 동작하는 과정

자바스크립트의 이벤트 루프(event loop)를 예로 들면 흐름은 다음과 같다.

  1. 코드가 fetch 같은 비동기 API를 호출하면 요청이 런타임과 운영체제에 위임되고 함수는 프로미스를 즉시 반환한다.
  2. 호출자는 멈추지 않고 다음 코드를 실행하며, 스레드는 다른 태스크를 처리한다.
  3. 운영체제 수준에서 I/O가 완료되면 런타임이 해당 프로미스를 이행 상태로 만들고 후속 콜백을 태스크 큐에 넣는다.
  4. 이벤트 루프가 현재 실행 중인 코드를 마친 뒤 큐에서 콜백을 꺼내 실행해 결과를 처리한다.

async와 await 문법은 이 비동기 흐름을 동기 코드처럼 위에서 아래로 읽히게 만들어 주는 표기일 뿐이다. await 지점에서 함수의 실행이 일시 중단되고 제어권이 이벤트 루프로 넘어가므로, 스레드 자체는 블로킹되지 않는다는 점이 진짜 블로킹 대기와의 결정적 차이다.

선택 기준과 흔한 오해

지연이 긴 I/O가 많고 동시 요청이 많은 서버라면 비동기 논블로킹이 스레드 수를 줄이고 처리량을 높인다. 반면 로직이 단순하고 동시성 요구가 낮다면 동기 블로킹이 디버깅과 추적에 유리하다. 흔한 오해는 "논블로킹이면 곧 비동기"라는 등식인데, 논블로킹 호출 후 폴링으로 완료를 챙기면 여전히 동기이다. 반대로 비동기라고 항상 빠른 것도 아니며, CPU 연산이 병목인 작업은 비동기로 감싸도 어차피 코어가 일해야 하므로 이득이 없다. 두 축을 정확히 나눠 말할 수 있는지가 이 주제 면접의 핵심 평가 포인트다. 참고로 커널 I/O 모델의 관점에서 보면 블로킹 I/O와 논블로킹 I/O, I/O 멀티플렉싱은 모두 완료를 애플리케이션이 챙기므로 동기에 속하고, 완료까지 커널이 대신 수행해 통지해 주는 비동기 I/O(io_uring 등)만이 엄밀한 의미의 비동기라는 분류도 널리 쓰인다.

면접에서 이렇게 물어본다

  • 동기/비동기와 블로킹/논블로킹은 각각 무엇을 기준으로 나뉘는지 설명해 보라.
  • 동기이면서 논블로킹인 경우와 비동기이면서 블로킹인 경우의 실제 예를 들어 보라.
  • async/await를 쓰면 코드가 순차적으로 보이는데, 이것이 블로킹과 어떻게 다른가.
  • 비동기 논블로킹 서버가 동시 요청 처리에 유리한 이유를 스레드 자원 관점에서 설명해 보라.
  • 비동기 프레임워크에서 결과를 동기적으로 기다리면 어떤 문제가 생기는가.
네트워크 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기