-
[CS] Blocking / Non-Blocking, Sync / Async 구분하기CS 2025. 12. 6. 16:53

개발자로 일하다 보면 Blocking과 Synchronous, Non-Blocking과 Asynchronous라는 용어를 자주 접하게 됩니다. 많은 경우 이 용어들이 혼용되어 쓰이곤 하지만, 엄밀히 말해 이들은 서로 다른 차원의 개념입니다.
오늘은 백엔드 개발자가 I/O 모델을 설계하거나 고성능 애플리케이션을 이해하기 위해 반드시 구분해야 할 이 네 가지 개념을 '제어권'과 '결과 처리'라는 핵심 키워드로 명확히 정리해 보겠습니다.
1. 두 개념의 결정적 차이: "관심사(Focus)가 다르다"
이 용어들이 헷갈리는 이유는 결과적으로 비슷해 보이기 때문입니다. 하지만 이들을 구분하는 기준, 즉 관심사가 다릅니다.
1) Blocking / Non-Blocking (제어권의 관점)
- 핵심 질문: 호출된 함수가 제어권(Control)을 바로 돌려주는가?
- Blocking: 호출된 함수가 작업을 마칠 때까지 제어권을 가지고 놓아주지 않습니다. 호출한 함수는 그동안 아무것도 하지 못하고 대기합니다.
- Non-Blocking: 호출된 함수가 작업 완료 여부와 상관없이 제어권을 즉시 반환(Return)합니다. 호출한 함수는 멈추지 않고 다음 로직을 실행할 수 있습니다.
여기서 말하는 제어권은 무엇일까요?
단순히 추상적인 권한을 말하는 것이 아니라, "현재 스레드(Thread)가 CPU를 점유하여 자신의 코드(명령어)를 계속 실행할 수 있는 상태"를 의미합니다.
- 제어권을 가졌다: 내 함수(Caller)가 CPU를 쓰고 있으며, 내 코드의 다음 줄(Next Line)을 실행할 수 있다는 뜻입니다.
- 제어권을 뺏겼다: 호출된 함수(Callee)나 OS가 CPU를 가져가 버려, 내 함수는 실행 흐름이 멈추고(Blocked) 대기해야 한다는 뜻입니다.
2) Sync / Async (결과 처리의 관점)
- 핵심 질문: 작업의 완료 여부(Result)를 누가, 언제 신경 쓰는가?
- Synchronous (동기): 호출한 함수가 작업의 완료를 직접 확인하거나 기다린 후 처리합니다. 요청과 결과가 순차적으로 흐릅니다.
- Asynchronous (비동기): 호출한 함수는 작업 시작만 요청하고, 결과는 신경 쓰지 않습니다. 작업이 끝나면 콜백(Callback)이나 이벤트 등을 통해 결과가 전달됩니다. 요청된 작업은 별도의 쓰레드나 커널(OS)에게 위임되어 수행됩니다.
2. 4가지 조합 (2x2 Matrix)
이 두 개념을 조합하면 총 4가지 케이스가 나옵니다. 개발자가 실무에서 마주하는 상황들을 대입해 보겠습니다.
① Sync + Blocking (동기 + 블로킹)
- 상황: 가장 일반적인 함수 호출.
- 설명: 함수를 호출하면 결과가 나올 때까지 제어권을 뺏기고(Blocking), 결과가 나오면 받아서 다음 줄을 실행합니다(Sync).
- Java 예시: JDBC를 이용한 DB 조회, InputStream.read().
import java.util.Scanner; public class SyncBlockingExample { public static void main(String[] args) { System.out.println("[Start] 메인 스레드 시작"); // 1. Blocking: 사용자가 입력을 마칠 때까지 제어권이 여기서 멈춥니다. // 2. Sync: 입력값을 리턴받아야만 다음 라인(result 변수 할당)으로 넘어갑니다. Scanner scanner = new Scanner(System.in); System.out.print("입력하세요: "); String result = scanner.nextLine(); System.out.println("[Result] 입력값: " + result); System.out.println("[End] 메인 스레드 종료"); } }② Async + Non-Blocking (비동기 + 논블로킹)
- 상황: 고성능 서버 프레임워크나 자바스크립트 엔진.
- 설명: 함수를 호출하자마자 제어권을 돌려받고(Non-Blocking), 다른 일을 하다가 작업이 끝났다는 신호(Callback)가 오면 결과를 처리합니다(Async).
- Java 예시: Spring WebFlux, CompletableFuture (thenApply).
import java.util.concurrent.CompletableFuture; public class AsyncNonBlockingExample { public static void main(String[] args) { System.out.println("[Start] 메인 스레드: " + Thread.currentThread().getName()); // 1. Non-Blocking: supplyAsync 호출 즉시 제어권이 메인 스레드로 돌아옵니다. CompletableFuture.supplyAsync(() -> { // 별도의 쓰레드(ForkJoinPool)에서 실행됨 try { Thread.sleep(2000); } catch (InterruptedException e) {} return "Hello, Async!"; }) // 2. Async: 작업 완료 후 실행될 콜백을 미리 정의합니다. .thenAccept(result -> { System.out.println("[Callback] 결과 처리 스레드: " + Thread.currentThread().getName()); System.out.println("[Result] 결과값: " + result); }); System.out.println("[End] 메인 스레드는 멈추지 않고 종료됩니다."); // (테스트를 위해 메인 스레드가 바로 죽지 않게 대기) try { Thread.sleep(3000); } catch (InterruptedException e) {} } }③ Sync + Non-Blocking (동기 + 논블로킹)
- 상황: Polling(폴링) 방식.
- 설명: 함수는 즉시 리턴되지만(Non-Blocking), 호출자가 "끝났어?"라고 계속 물어보며 확인합니다(Sync).
- Java 예시: Future.isDone()을 루프 돌며 확인하는 경우.
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class SyncNonBlockingExample { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor(); System.out.println("[Start] 작업 요청"); // 1. Non-Blocking: submit하자마자 Future 객체를 받고 제어권이 돌아옵니다. Future<String> future = executor.submit(() -> { Thread.sleep(2000); return "Finished"; }); // 2. Sync: 결과가 나왔는지 호출자(Main)가 계속 확인합니다. (Polling) while (!future.isDone()) { System.out.println("[Polling] 아직 안 끝났어? 딴짓 하는 중..."); Thread.sleep(500); // 다른 작업 수행 시뮬레이션 } // 결과가 준비된 것을 확인하고 가져옴 String result = future.get(); System.out.println("[Result] 결과값: " + result); executor.shutdown(); } }④ Async + Blocking (비동기 + 블로킹)
- 상황: 안티 패턴(Anti-Pattern) 또는 실수.
- 설명: 비동기로 작업을 시켜놓고, 정작 결과는 멈춰서 기다리는 상황입니다. 비동기의 이점을 살리지 못합니다.
- Java 예시: CompletableFuture를 사용하고 바로 .get()을 호출하여 스레드를 멈추게 하는 경우.
import java.util.concurrent.CompletableFuture; public class AsyncBlockingExample { public static void main(String[] args) { System.out.println("[Start] 메인 스레드 시작"); // 1. Async: 작업을 별도 스레드에 맡겼습니다. (여기까진 좋음) CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) {} return "Database Result"; }); System.out.println("[Wait] 결과를 기다리는 중... (여기서 멈춤)"); // 2. Blocking: Async로 던졌는데, 결과가 필요해서 join()을 호출하는 순간 // 메인 스레드는 아무것도 못 하고 대기 상태가 됩니다. String result = future.join(); System.out.println("[Result] 결과값: " + result); System.out.println("[End] 메인 스레드 종료"); } }3. 스레드(Thread)와 제어권
많은 개발자가 "Async나 Non-Blocking을 쓰면 무조건 새로운 스레드가 생성되는가?"에 대해 혼동합니다. 결론부터 말하면 작업의 종류에 따라 다릅니다.
Case A: CPU 연산 작업 (Async)
- 계산 작업은 CPU가 수행해야 하므로, 메인 스레드가 아닌 별도의 스레드(Worker Thread)가 반드시 필요합니다.
- Java에서는 ThreadPool에서 스레드를 하나 빌려와 작업을 수행합니다.
Case B: I/O 작업 (Non-Blocking)
- 네트워크나 파일 입출력은 CPU가 아니라 커널(OS)과 하드웨어(랜카드 등)가 수행합니다.
- 따라서 Java 애플리케이션 레벨에서 별도의 스레드를 만들 필요가 없습니다.
- 스레드는 커널에 "데이터 오면 알려줘"라고 등록만 하고 다른 일을 합니다. 이것이 Node.js나 Spring WebFlux가 적은 수의 스레드로 대용량 트래픽을 처리하는 비결입니다.
4. Java 코드로 비교하기
Blocking (Java IO)
// 데이터를 읽을 때까지 스레드가 여기서 멈춤 (Blocking) // 결과가 나와야 다음 라인 실행 (Sync) InputStream in = socket.getInputStream(); int data = in.read(); System.out.println("데이터 도착: " + data);Async + Non-Blocking (Java CompletableFuture)
// 비동기 작업 요청 (즉시 리턴 - Non-Blocking) CompletableFuture.supplyAsync(() -> { // 별도 스레드 혹은 I/O 처리 return "Result"; }).thenAccept(result -> { // 작업이 끝나면 실행될 콜백 (Async) System.out.println("결과 처리: " + result); }); // 메인 스레드는 멈추지 않고 계속 실행됨 System.out.println("다른 작업 수행 중...");5. 결론: 무엇을 써야 할까?
Spring 생태계로 본다면, 전통적인 Spring MVC는 Blocking + Sync 모델을 기반으로 합니다. 요청당 하나의 스레드를 할당하므로 코드를 작성하기 쉽고 디버깅이 직관적입니다.
반면, Spring WebFlux는 Non-Blocking + Async 모델을 기반으로 합니다. 적은 수의 스레드로 엄청난 양의 동시 접속을 처리할 수 있지만, 학습 곡선이 높고 디버깅이 어렵습니다.
단순히 "Non-Blocking이 빠르다"라고 이해하기보다, "내 애플리케이션이 I/O 대기 시간이 긴가, CPU 연산이 많은가?"를 파악하여 적절한 모델을 선택하는 것이 백엔드 개발자의 핵심 역량일 것입니다.
'CS' 카테고리의 다른 글
[CS] Http란 무엇이고 어떤 특징이 있을까? (1) 2025.12.05 [CS] URI, URL, URN 완벽정리 (1) 2025.12.04 [CS] 인터넷 네트워크와 핵심 개념 정리: IP, TCP/UDP, PORT, DNS (0) 2025.12.03 [CS] OSI 7계층에 대해서 알아보기 (0) 2025.12.01