비동기 프레임워크 vs 동기 프레임워크, 스프링 부트는 왜 비동기를 안 쓸까? (FastAPI·가상 스레드까지)

"FastAPI는 비동기라던데, 그럼 스프링은 동기인가요?"
"비동기가 더 빠른 거 아니에요? 근데 회사에서는 왜 다들 스프링 부트만 쓰죠?"
"WebFlux 한번 써봤는데 코드가 너무 어려워서 다시 갈아엎었어요."
개발 커뮤니티에서 심심하면 한 번씩 올라오는 질문들입니다.
비동기 프레임워크가 동기 프레임워크보다 무조건 빠르고 좋은 거라면,
왜 현업 자바 진영은 스프링 부트(동기 모델)를 그렇게 고집할까요?
반대로 파이썬 쪽에서는 왜 FastAPI 같은 비동기 프레임워크가 빠르게 자리를 잡았을까요?
이 글에서는 동기와 비동기 프레임워크가 내부적으로 뭐가 다른지를 어려운 용어 대신 식당 비유로 먼저 풀고,
FastAPI가 왜 "비동기 프레임워크"로 불리는지,
스프링은 비동기 카드가 있는데도 왜 잘 안 쓰는지를 정리합니다.
그리고 2026년 현재 이 이야기를 한 번 더 뒤집어 놓은 자바 21 가상 스레드까지 이어가겠습니다.
웹 백엔드를 막 시작한 입문자나,
"비동기"라는 단어는 들어봤지만 손에 안 잡히는 주니어 개발자라면 끝까지 읽고 나서 프레임워크 선택의 감을 잡을 수 있을 겁니다.
📋 목차
- 동기 vs 비동기, 식당 비유로 한 번에 이해하기
- 동기 프레임워크는 어떻게 동작하나 (스프링 MVC·Flask·Django)
- 비동기 프레임워크는 어떻게 동작하나 (FastAPI·Node.js)
- FastAPI는 왜 '비동기 프레임워크'라고 부를까
- 스프링도 비동기 카드는 있다 - MVC vs WebFlux
- 그런데 왜 스프링 진영은 비동기를 잘 안 쓸까
- 2026년의 반전 - 자바 21 가상 스레드
- 그래서 언제 비동기, 언제 동기를 쓰나
- 정리 - 비동기는 '더 좋은 것'이 아니라 '다른 도구'
1. 동기 vs 비동기, 식당 비유로 한 번에 이해하기
용어부터 정리하면 더 헷갈립니다. 그냥 식당으로 생각해 보겠습니다.
동기 식당: 손님 한 명당 전담 종업원 한 명을 붙입니다. 종업원은 손님이 주문한 음식이 주방에서 나올 때까지 그 앞에 가만히 서서 기다립니다. 손님이 100명이면 종업원도 100명이 필요하죠.
비동기 식당: 종업원 한 명이 여러 테이블을 돌아다닙니다. A 테이블 주문을 주방에 넘긴 뒤, 음식이 나오길 멍하니 기다리지 않고 곧장 B 테이블 주문을 받으러 갑니다. 음식이 준비되면 그때 가져다 주고요.
여기서 종업원이 바로 스레드(thread), 즉 일하는 일꾼입니다. 음식을 기다리는 시간은 데이터베이스 조회나 외부 API 호출처럼 "결과가 올 때까지 기다려야 하는 시간"이고, 개발 용어로는 I/O 대기라고 부릅니다.
동기 방식은 종업원이 기다리는 동안 아무 일도 못 합니다(이걸 '블로킹'이라고 합니다). 비동기 방식은 기다리는 시간에 다른 손님을 받으니 종업원이 놀지 않죠(이걸 '논블로킹'이라고 합니다). 핵심은 이겁니다. 비동기는 기다리는 시간을 알뜰하게 재활용하는 구조라는 것.
2. 동기 프레임워크는 어떻게 동작하나
전통적인 동기 프레임워크는 '요청 하나당 스레드 하나(thread-per-request)' 모델로 돌아갑니다. 손님 한 명에 종업원 한 명을 붙이는 그 방식이죠. 자바의 스프링 MVC, 파이썬의 Flask와 전통적인 Django가 여기에 속합니다.
요청이 들어오면 스레드 풀에서 스레드 하나를 꺼내 그 요청을 처리합니다. 처리 도중 DB 조회 같은 I/O가 생기면 그 스레드는 결과가 올 때까지 멈춰서(블로킹) 기다립니다. 코드를 위에서 아래로 순서대로 읽으면 그대로 실행되기 때문에 흐름이 직관적이고 디버깅도 쉽습니다.
대신 약점이 있습니다. 동시 접속자가 폭발적으로 늘면 스레드도 그만큼 필요해지는데, 스레드는 메모리를 적잖이 잡아먹습니다. 어느 순간 스레드 풀이 바닥나면(고갈) 새 요청이 대기열에서 밀리기 시작합니다. 전통적인 스프링 MVC는 각 요청을 전담 스레드가 처리하는데, 그 스레드가 I/O를 기다리며 블로킹될 수 있고, 동시성이 높아지면 스레드 고갈과 컨텍스트 스위칭 부담에 부딪힐 수 있습니다.
3. 비동기 프레임워크는 어떻게 동작하나
비동기 프레임워크는 '이벤트 루프(event loop)' 모델을 씁니다. 종업원 몇 명이 여러 테이블을 돌아다니는 그 방식입니다. 적은 수의 스레드가 수많은 요청을 번갈아 가며 처리하죠. 파이썬의 FastAPI, 그리고 자바스크립트의 Node.js가 대표적입니다.
요청이 I/O 대기에 들어가면, 스레드는 그걸 붙잡고 기다리는 대신 다음 작업으로 넘어갑니다. 기다리던 결과가 도착하면 그때 이어서 처리하고요. 이벤트 루프가 핵심으로, 작업이 I/O를 기다리는 동안 그냥 멈춰 있지 않고 다른 작업으로 전환할 수 있게 해 줍니다. 그래서 DB·네트워크 호출이 많은 'I/O 바운드' 작업, 즉 기다리는 시간이 긴 작업에서 진가를 발휘합니다.
📌 비동기가 항상 빠른 건 아닙니다
비동기는 '기다리는 시간'을 재활용하는 구조라,
계산이 무거운 작업(CPU 바운드)에는 오히려 불리할 수 있습니다.
이미지 변환이나 복잡한 연산처럼 종업원이 직접 땀 흘려야 하는 일은 여러 테이블을 도는 게 의미가 없으니까요.
4. FastAPI는 왜 '비동기 프레임워크'라고 부를까
FastAPI가 비동기 프레임워크로 불리는 건 처음부터 비동기를 전제로 설계됐기 때문입니다. FastAPI는 본질적으로 ASGI 위에서 동작하도록 만들어졌고, 이 ASGI가 비동기 요청을 기본으로 지원합니다. 여기서 ASGI는 비동기를 위한 통로, 그 이전 세대인 WSGI는 동기를 위한 통로라고 보면 됩니다.
조금 더 들어가면 이렇습니다. FastAPI는 ASGI 프레임워크라 async/await를 지원하고, Uvicorn 같은 ASGI 서버 위에서 이벤트 루프를 통해 여러 요청을 동시에 처리합니다. 반대로 Flask는 전통적으로 WSGI 기반이라, 기본 배포 방식에서는 워커 하나가 한 번에 요청 하나만 처리합니다.
그 차이가 처리량으로 드러납니다. 정확한 수치는 작업 종류와 설정에 따라 다르지만, 벤치마크들은 대체로 I/O 바운드 상황에서 FastAPI가 Flask보다 초당 처리량이 몇 배가량 높게 나온다고 보고합니다. 다만 주의할 점이 있습니다. async 함수 안에서 비동기를 지원하지 않는 옛날 라이브러리를 그냥 쓰면, 그 한 줄이 이벤트 루프 전체를 멈춰 세워 버립니다. 종업원이 한 테이블에서 발이 묶이는 셈이죠.
5. 스프링도 비동기 카드는 있다 - MVC vs WebFlux
여기서 오해를 하나 풀어야 합니다. "스프링 = 동기"는 절반만 맞습니다. 스프링에는 두 갈래가 있습니다.
| 항목 | 스프링 MVC (동기) | 스프링 WebFlux (비동기) |
|---|---|---|
| 처리 모델 | 요청당 스레드 하나 (블로킹) | 이벤트 루프 (논블로킹) |
| 코드 스타일 | 위에서 아래로 순차적, 직관적 | Mono·Flux 체이닝, 함수형 |
| 바탕 기술 | 서블릿 (보통 톰캣) | Project Reactor (보통 Netty) |
| 난이도 | 낮음 | 높음 (학습 곡선 가파름) |
즉 스프링 부트로도 비동기 애플리케이션을 만들 수 있습니다. 스프링 WebFlux는 스프링 5에서 도입된 리액티브·논블로킹 프레임워크로, Reactive Streams 명세와 Project Reactor를 활용해 높은 동시성 작업을 처리하도록 설계됐습니다. 카드는 분명히 있는데, 현업에서 잘 안 꺼낸다는 게 문제입니다. 왜 그럴까요?
6. 그런데 왜 스프링 진영은 비동기를 잘 안 쓸까
이유는 크게 세 가지로 모입니다.
첫째, 리액티브 코드가 어렵습니다. WebFlux는 결과값을 그냥 반환하는 게 아니라 Mono(값 하나)나 Flux(값 여러 개)라는 상자에 담아 흘려보내고, flatMap·zip 같은 연산자로 그 흐름을 엮어야 합니다. 개발자는 리액티브 스트림, 백프레셔, 비동기 합성이라는 완전히 다른 패러다임을 받아들여야 하는데, 강력한 만큼 인지적·운영적 복잡성도 함께 따라옵니다. 에러가 나도 스택 트레이스가 평소처럼 친절하게 안 나와서 디버깅이 고통스럽습니다.
둘째, 전체 스택이 전부 논블로킹이어야 효과가 납니다. 종업원이 아무리 부지런해도 주방 입구가 막혀 있으면 소용없는 것과 같습니다. 가장 흔한 걸림돌이 데이터베이스입니다. 자바의 표준 DB 연결 방식인 JDBC가 블로킹이라, WebFlux 이점을 살리려면 R2DBC 같은 리액티브 드라이버로 갈아타야 합니다. 중간에 블로킹 호출이 단 하나라도 끼면 이벤트 루프가 막혀 비동기의 이점이 통째로 사라집니다.
셋째, 대부분의 업무 시스템은 그 정도의 극단적인 동시성이 필요하지 않습니다. 사내 관리 시스템이나 일반적인 쇼핑몰 백엔드가 초당 수만 건을 동시에 받아낼 일은 흔치 않죠. 얻는 이점은 작은데 코드는 어려워지고 유지보수 인력은 구하기 힘들어지니, 팀 입장에서 쉽게 손이 안 가는 겁니다.
📌 한 줄 요약
WebFlux는 성능을 위해 개발 편의성을 희생하는 선택이었습니다.
리액티브 학습 비용, 전체 스택 논블로킹이라는 까다로운 전제,
그리고 "대부분의 앱엔 과한 성능" 이 세 가지가 겹치면서
스프링 진영의 기본값은 여전히 동기 모델인 MVC로 남아 있었습니다.
7. 2026년의 반전 - 자바 21 가상 스레드
그런데 이 이야기가 최근 한 번 더 뒤집혔습니다. 범인은 자바 21에 들어온 가상 스레드(Virtual Threads, Project Loom)입니다.
가상 스레드는 자바 21에서 도입된 기능으로, 익숙한 블로킹 코드를 그대로 쓰면서도 JVM 차원에서 논블로킹 동작을 얻을 수 있게 해 줍니다. 동작 원리는 이렇습니다. 가상 스레드가 블로킹 I/O를 만나면 자바 런타임이 그 가상 스레드를 잠시 내려놓고, 거기에 묶여 있던 OS 스레드를 풀어 줘서 다른 가상 스레드를 처리하게 합니다. 종업원을 묶어 두던 '기다리는 시간' 문제를, 코드 스타일은 그대로 둔 채 밑바닥에서 해결해 버린 거죠.
스프링 부트도 이미 발을 맞췄습니다. 스프링 부트 3.2 이상부터 가상 스레드를 기본 기능으로 지원합니다. 결과적으로 WebFlux를 떠받치던 가장 큰 명분이었던 '고동시성'이, 이제는 익숙한 동기 스타일 코드(MVC)에 가상 스레드를 켜는 것만으로도 상당 부분 해결됩니다. 어려운 리액티브를 배우지 않고도 비슷한 확장성을 얻을 수 있게 된 셈입니다.
💡 그렇다고 WebFlux가 사라지는 건 아닙니다
벤치마크 결과는 작업 종류에 따라 갈립니다.
어떤 비교에서는 고동시성 상황에서 가상 스레드가,
또 다른 DB 위주 테스트에서는 WebFlux가 더 빠르게 나오기도 합니다.
WebFlux는 스트리밍, 백프레셔 제어, WebSocket, 카프카 같은 이벤트 기반 시스템에서 여전히 강점을 보입니다.
가상 스레드가 '리액티브가 유일한 답이던 시대'를 끝냈다고 보는 게 더 정확합니다.
8. 그래서 언제 비동기, 언제 동기를 쓰나
정답은 상황에 따라 다르지만, 대략의 기준은 이렇게 잡을 수 있습니다.
| 상황 | 추천 방향 |
|---|---|
| 일반적인 CRUD·관리 시스템 | 동기로 충분 (스프링 MVC, Flask, Django) |
| 외부 API·DB 호출이 많은 I/O 바운드 API | 비동기 유리 (FastAPI 등) |
| 실시간 스트리밍·WebSocket·이벤트 기반 | 리액티브 강점 (WebFlux 등) |
| 자바인데 동시성도 챙기고 싶을 때 | MVC + 가상 스레드 (자바 21+) |
| 무거운 계산 위주 (CPU 바운드) | 비동기 이점 적음, 동기·병렬 처리 고려 |
9. 정리 - 비동기는 '더 좋은 것'이 아니라 '다른 도구'
비동기 프레임워크는 '기다리는 시간'이 많은 작업에서 적은 자원으로 많은 요청을 받아내는 데 강합니다. FastAPI가 파이썬 진영에서 빠르게 자리 잡은 것도 그런 이유고요. 반대로 스프링 진영이 비동기(WebFlux)를 굳이 안 쓴 데는 리액티브의 높은 난이도, 전체 스택을 논블로킹으로 맞춰야 하는 부담, 그리고 대부분의 앱엔 그 성능이 과하다는 현실적인 이유가 있었습니다.
그리고 2026년 현재, 자바 21의 가상 스레드가 등장하면서 "동시성을 챙기려면 어려운 비동기를 배워야 한다"는 오래된 전제 자체가 흔들리고 있습니다. 개인적으로는 앞으로 자바 백엔드의 기본값이 'MVC + 가상 스레드' 쪽으로 옮겨갈 가능성이 크다고 보지만, 이건 좀 더 지켜봐야 할 부분입니다.
결국 비동기는 동기보다 우월한 상위 호환이 아니라, 쓰임새가 다른 도구입니다. 프레임워크를 고를 때 "요즘 뜨는 게 뭐지"가 아니라 "내 서비스는 기다리는 시간이 많은가, 계산이 많은가"를 먼저 따져 보는 게 훨씬 도움이 됩니다.
자주 묻는 질문 (FAQ)
FastAPI가 Flask보다 항상 빠른가요?
항상은 아닙니다. DB나 외부 API 호출처럼 기다리는 시간이 많은 I/O 바운드 작업에서는 FastAPI가 대체로 더 높은 처리량을 보이지만, 계산이 무거운 작업이나 단순한 소규모 서비스에서는 체감 차이가 거의 없거나 오히려 Flask의 단순함이 나을 수도 있습니다.
스프링 부트는 비동기를 지원하지 않나요?
지원합니다. 스프링 WebFlux라는 리액티브·비동기 프레임워크가 따로 있습니다. 다만 코드 난이도가 높고 전체 스택을 논블로킹으로 맞춰야 해서, 현업에서는 동기 모델인 스프링 MVC를 훨씬 많이 씁니다.
가상 스레드가 WebFlux를 완전히 대체하나요?
완전히 대체한다고 보긴 어렵습니다. 일반적인 웹 API의 동시성 문제는 가상 스레드로 상당 부분 풀리지만, 스트리밍이나 백프레셔 제어, 이벤트 기반 시스템에서는 WebFlux의 리액티브 모델이 여전히 유효합니다.
비동기 프레임워크를 쓰면 무조건 성능이 좋아지나요?
아닙니다. 코드 어딘가에 블로킹 호출이 끼어 있으면 비동기의 이점이 사라집니다. 비동기를 제대로 쓰려면 사용하는 라이브러리와 DB 드라이버까지 전부 논블로킹이어야 합니다.
동기 프레임워크는 이제 구식인가요?
그렇지 않습니다. 코드가 직관적이고 디버깅이 쉬워서 대부분의 업무 시스템에는 동기 모델이 여전히 합리적인 선택입니다. 가상 스레드 같은 기술 덕분에 동기 스타일로도 높은 동시성을 얻을 수 있게 되면서, 오히려 다시 주목받는 면도 있습니다.
마무리
비동기냐 동기냐는 유행을 따라가는 문제가 아니라, 내 서비스가 어떤 일을 주로 하느냐의 문제입니다. 기다리는 시간이 많은 서비스라면 비동기가, 평범한 업무 시스템이라면 동기가 보통은 정답에 가깝습니다. 스프링 진영이 오랫동안 동기 모델을 고집한 데는 분명한 이유가 있었고, 가상 스레드의 등장으로 그 선택은 한층 더 설득력을 얻었습니다. 새 프레임워크를 고민하기 전에, 종업원이 손님을 기다리느라 발이 묶이는 구간이 내 서비스 어디에 있는지부터 짚어 보시길 권합니다.
📚 함께 보면 좋은 글
자바(Java)는 왜 커피잔일까? 오크 나무에서 시작된 전설
한국의 개발자라면 누구나 한 번쯤은 배우게 되는 언어, 바로 자바(Java)입니다.공공기관 웹사이트부터 안드로이드 앱까지 우리 생활 깊숙이 들어와 있는 이 언어의 로고는 왜 김이 모락모락 나
devwar.tistory.com
자바 개발의 영원한 고전, 이클립스(Eclipse) 완벽 가이드
개발자 커뮤니티에서 자주 보이는 논쟁이 있습니다."요즘 누가 이클립스 쓰나요? 다 인텔리제이 쓰지 않나요?"하지만 통계를 보면 놀랍게도 여전히 수많은 회사가 이클립스를 사용하고 있습니
devwar.tistory.com
스파게티 코드가 낳은 구원자? 객체지향 프로그래밍(OOP) 탄생의 비화
개발 과정에서 마주하는 복잡한 코드, 즉 '스파게티 코드'는 생산성을 저해하고 유지보수를 어렵게 만듭니다. 이러한 문제 해결을 위해 등장한 개념이 바로 객체지향 프로그래밍(OOP)입니다. OOP
devwar.tistory.com
스파게티 코드의 구원자, '프레임워크'는 어떻게 개발자를 해방시켰나?
소프트웨어 개발 초기, 개발자들은 모든 기능을 백지상태에서 구현해야 하는 비효율적인 환경에 놓여 있었습니다. 이러한 혼돈 속에서 탄생한 프레임워크(Framework)는 단순한 도구를 넘어 개발의
devwar.tistory.com
프론트엔드 3대장 비교 - 리액트 뷰 앵귤러 탄생 이유와 장단점 (2026)
"프론트 시작하려는데 앵귤러, 리액트, 뷰 중에 뭘 배워야 하죠?" "셋이 뭐가 그렇게 다른가요?" "회사는 리액트 쓰는데 저는 뷰가 편해요, 괜찮을까요?" "신입인데 그냥 제일 인기 많은 거 하면 되
devwar.tistory.com
'언어의역사' 카테고리의 다른 글
| 자바와 자바스크립트 차이는? 10일 만에 만들어진 자바스크립트 역사 이야기 (0) | 2026.07.05 |
|---|---|
| 타입스크립트는 왜 만들어졌을까 - 자바스크립트가 커지자 마이크로소프트가 내놓은 답 (0) | 2026.07.03 |
| C언어 이름의 유래 - B 다음이라서 C? 창시자도 확답하지 않은 이유 (0) | 2026.07.03 |
| C++는 왜 C+1이 아닐까? 이름에 담긴 1983년의 농담과 탄생 배경 (0) | 2026.07.03 |
| 자바(Java)는 왜 커피잔일까? 오크(Oak)에서 자바가 된 이름의 유래 (0) | 2026.07.02 |