비동기(Asynchronous)와 논블로킹(Non-blocking)은 현대 소프트웨어 개발에서 매누 중요한 개념이다.
블로킹과 논블로킹
이 두 가지는 작업이 완료될 때까지 호출자(Caller)의 프로그램 흐름이 멈추는지(블로킹) 멈추지 않는지(논블로킹)에 초점을 둔다.
주로 I/O 작업(파일 읽기, 네트워크 요청, 데이터베이스 쿼리 등)에서 이 개념이 중요하게 사용된다.
블로킹 (Blocking)
어떤 함수나 메소드를 호출했을 때 그 함수나 메소드의 작업이 완전히 완료될 때까지 호출한 곳(호출자)의 다음 코드가 실행되지 않고 기다리는 방식이다.
- 예를 들어 파일을 읽는 함수를 호출하면, 파일 읽기가 끝날 때까지 CPU는 다른 일을 하지 않고 기다리게 된다.
- 단점: CPU가 I/O 작업의 완료를 기다리느라 유휴 상태가 되어 자원 활용 효율이 떨어질 수 있다.
- 특히 동시성(Concurrency)이 중요한 애플리케이션에서는 성능 저하의 주범이 된다.
- 예시: 전통적인 서블릿 기반 웹 서버에서 하나의 요청이 데이터베이스 쿼리를 날리면 해당 요청을 처리하는 스레드는 쿼리 결과가 올 때까지 블로킹된다.
논블로킹(Non-blocking)
어떤 함수나 메소드를 호출했을 때 그 함수나 메소드가 작업을 바로 시작하고 작업이 완료되기를 기다리지 않고 즉시 호출자에세 제어권을 반환하는 방식이다.
- 호출자는 함수가 작업을 진행하는 동안 다른 작업을 계속 수행할 수 있다.
- 작업이 완료되면 보통 콜백(Callback)이나 이벤트(Event), 퓨처(Future)/프로미스(Promise) 같은 메커니즘을 통해 결과가 통지된다.
- 장점: CPU가 I/O 작업 완료를 기다리지 않고 다른 유용한 작업을 수행할 수 있으므로 자원 활용 효율을 높이고 더 많은 동시 요청을 처리할 수 있다.
- 예시: 스프링 WebFlux에서
WebClient를 통해 외부 API를 호출하면 요청을 보낸 후 즉시 제어권을 반환하고 응답이 오면 미리 등록된 콜백 함수가 실행된다.
동기와 비동기
이 두 가지는 작업의 수행 순서와 결과 처리 방식에 초점을 맞춘다.
동기 (Synchronous)
호출한 함수나 메소드의 작업이 순서대로 실행되며 이전 작업이 완료된 후에 다음 작업이 시작되는 방식이다.
- 작업의 시작과 끝이 일치한다.
- 호출자가 작업을 시작하고 그 작업의 결과를 받을 때까지 기다린다.
- 단점: 한 작업이 오래 걸리면 전체 프로그램의 흐름이 지연될 수 있다.
- 예시: 일반적인 함수 호출이나 순차적인 코드 실행.
비동기 (Asynchronous)
호출한 함수나 메소드의 작업이 백그라운드에서 실행되고 호출자는 그 작업이 완료되기를 기다리지 않고 다음 코드를 계속 실행하는 방식이다.
- 작업의 시작과 끝이 일치하지 않는다.
- 호출자는 작업을 시작만 하고 나중에 작업이 완료되면 그 결과를 통보받는다.
- 결과는 콜백 함수, 이벤트 리스터, Future/Promise, 리액티브 스프림 등 다양한 메커니즘을 통해 처리된다.
- 장점: 오래 걸리는 작업이 메인 스레드를 블로킹하지 않아 애플리케이션의 응답성을 유치하고 여러 작업을 동시에 효율적으로 처리할 수 있다.
- 예시: 웹 브라우저에서 서버에 이미지를 요청하고 이미지가 로딩되는 동안 사용자는 웹 페이지의 다른 부분을 스크롤하거나 클릭할 수 있다. 이미지가 로딩되면 그때 화면에 나타난다.
요약 및 관계
| 블로킹 (Blocking) | 논블로킹 (Non-blocking) |
|---|
| 동기 (Synchronous) | 작업을 시작하면 완료될 때까지 기다린다(순서 유지, 제어권 반환 X) | 거의 사용되지 않는다(제어권을 바로 반환하지만 결과는 즉시 제공) |
| 비동기 (Asynchronous) | 작업을 시작하면 백그라운드에서 실행(순서 무관, 제어권 바로 반환) | 작업을 시작하면 백그라운드에서 실행(순서 무관, 제어권 바로 반환) |
- 블로킹과 논블로킹은 어떻게 제어권을 반환하는가에 초점을 둔다.
- 동기와 비동기는 작업의 완료를 어떻게 기다리는가에 초점을 둔다.
가장 흔하고 효율적인 조합은 비동기 논블로킹이다.
- 논블로킹: 작업을 시작하면 제어권을 즉시 반환하여 호출자가 다른 일을 할 수 있게 한다.
- 비동기: 작업이 백그라운드에서 실행되며 완료되면 나중에 콜백 등으로 결과를 알려준다.
- 스프링 WebFlux와 같은 리액티브 프레임워크가 바로 이 비동기 논블로킹 방식을 사용한다. 이를 통해 적은 수의 스레드로도 높은 동시성을 달성하여 효율적인 자원 사용과 높은 처리량을 제공한다.