📝 1. 미리보는 3줄 요약 (TL;DR)
- 비동기 처리의 본질: 한 작업을 더 빨리 끝내는 기술이 아니라, 결과값을 기다리는 동안 서버가 놀지 않게 하여 전체 처리 능력을 높이는 기술입니다.
- async/await 문법의 핵심: async def 선언과 내부의 await 호출이 한 세트로 움직여야 하며, await은 대기하는 동안 이벤트 루프에 제어권을 돌려준다는 의미입니다.
- 무조건 비동기가 정답은 아니다: CPU를 오래 쓰는 작업에서는 효과가 제한적이며, 서비스 규모와 병목 지점에 따라 동기와 비동기 방식을 올바르게 선택해야 합니다.
🔍 2. 주제에 대한 깊은 이해
💡 주요 개념 정리
- FastAPI: Python으로 API 서버를 만들 수 있는 모던 웹 프레임워크입니다.
- 비동기 처리와 async/await의 차이: * 비동기 처리: 서버 자원을 효율적으로 쓰게 해주는 큰 개념 및 처리 방식입니다.
- async/await: Python에서 이 비동기 처리를 직관적으로 표현하는 문법적 수단입니다.
- 이벤트 루프 (Event Loop): 비동기 작업 관리자입니다. 서버에 들어오는 요청들을 계속 왔다 갔다 순회하면서, 지금 바로 실행 가능한 작업이 있는지 판단하고 처리합니다.
- 모델 추론 API: AI 모델을 학습시키는 API가 아니라, 이미 학습된 모델에게 입력을 주고 예측 결과를 받는 API입니다.
- 전체 흐름: 요청 ➡️ 입력 검증(Pydantic) ➡️ 전처리 ➡️ 모델 추론 ➡️ 후처리 ➡️ 응답 반환
💥 3. 비동기 처리가 필요한 이유와 시나리오
호출, DB 조회, 큐 대기, 파일 입출력(I/O)처럼 결과값 반환까지 대기 시간이 긴 요청을 처리할 때, 서버가 그 시간을 낭비하지 않고 실행 가능한 다른 요청을 함께 처리하기 위함입니다.
⚠️ 주의: 단일 요청의 계산 시간 자체가 줄어드는 것은 아닙니다. (스트리밍도 걸리는 시간은 똑같으나 결과를 받는 대로 조금씩 보여주는 것뿐입니다.) 하지만, 동시에 많은 요청이 들어오는 상황(트래픽이 몰릴 때)에서 서버 처리량을 높이고 대기열을 줄여 전체 응답성을 크게 개선합니다.
📊 동기 vs 비동기 트래픽 시나리오 비교
❌ 동기적 처리 (Sync)
한 작업이 끝날 때까지 다음 흐름이 그대로 멈춰서 기다리는 방식입니다.
- A 요청 시작 ➡️ 모델 응답 대기 (3초간 서버 멈춤) ➡️ A 완료
- B 요청 시작 ➡️ 모델 응답 대기 (3초간 서버 멈춤) ➡️ B 완료
- C 요청 시작 ➡️ 모델 응답 대기 (3초간 서버 멈춤) ➡️ C 완료
⭕ 비동기적 처리 (Async)
대기 시간이 발생하면 시간을 낭비하지 않고 다음 요청을 바로 받습니다.
- A 요청 시작 ➡️ 모델 응답 대기 (기다리는 동안 제어권 반납)
- B 요청 시작 ➡️ 모델 응답 대기 (기다리는 동안 제어권 반납)
- C 요청 시작 ➡️ 모델 응답 대기 (기다리는 동안 제어권 반납)
- ➡️ A / B / C 응답이 도착하는 순서대로 즉시 반환
🛠️ 4. 검증을 위한 FastAPI PoC
import asyncio
import time
from fastapi import FastAPI
app = FastAPI()
# 실제 프로덕션 환경이라면 대형 AI 모델(가중치)이 로드되어 있다고 가정합니다.
# ------------------------------------------------------------------
# ❌ [시나리오 1] 동기(Sync) 방식의 모델 추론 API
# ------------------------------------------------------------------
@app.post("/predict/sync")
async def predict_sync(text: str):
start_time = time.time()
# 🚨 Blocking 작업 시뮬레이션
# 예: 동기 방식 외부 API 호출, 동기 DB 조회, 동기 파일 I/O, 잘못 작성된 모델 호출
# time.sleep은 현재 실행 흐름을 완전히 멈추는 동기 함수입니다.
#
# 즉, "async를 붙였는데도 서버가 막히는" 상황을 보여주는 예시입니다.
time.sleep(5.0)
end_time = time.time()
process_time = end_time - start_time
return {
"mode": "Sync (Blocking)",
"input": text,
"result": f"요약 완료 (소요시간: {process_time:.2f}초)"
}
# ------------------------------------------------------------------
# ⭕ [시나리오 2] 외부 API를 활용한 비동기(Async) 방식의 모델 추론 API
# ------------------------------------------------------------------
@app.post("/predict/async")
async def predict_async(text: str):
start_time = time.time()
# ✨ I/O-Bound 대기 시뮬레이션 (예: OpenAI나 외부 Ollama 서버에 API 요청 후 대기)
# asyncio.sleep은 대기하는 동안 '이벤트 루프'에 제어권을 반납하여 다른 요청을 받게 합니다.
# (외부 AI API, DB, 큐 응답을 기다리는 I/O 대기 상황을 흉내냅니다.)
#
# 🤔 Q. 'await time.sleep(5.0)'은 안 되나요?
# 💡 A. time.sleep은 동기 함수이므로 await과 함께 사용할 수 없습니다. (= 자격 미달)
# 마지막에 '10 + "안녕"'을 하면 데이터 타입이 맞지 않아 TypeError가 나는 것처럼
# await 뒤에 일반 함수를 넣으면 파이썬이 아래와 같은 자격 미달 에러를 던집니다.
# 👉 TypeError: object NoneType can't be used in 'await' expression
await asyncio.sleep(5.0)
end_time = time.time()
process_time = end_time - start_time
return {
"mode": "Async (Non-Blocking)",
"input": text,
"result": f"요약 완료 (소요시간: {process_time:.2f}초)"
}
# ------------------------------------------------------------------
# 🩺 [상태 점검 API] 서버가 살아있는지 확인하는 가벼운 헬스체크
# ------------------------------------------------------------------
@app.get("/health")
def health_check():
return {"status": "healthy", "message": "서버가 정상적으로 응답하고 있습니다!"}
⚖️ 5. 그렇다면 비동기가 무조건 정답일까? (한계와 선택)
① CPU를 오래 쓰는 작업에서의 제한
- AI 모델 추론 과정에서 외부 API가 아닌, 서버 내부적으로 CPU를 극도로 오래 쓰는 연산 작업이 포함되어 있다면 비동기 처리의 효과가 제한적입니다.
② 일반 CRUD 및 게시글 조회(Read) API에서의 고민
- 비동기가 좋은 이유: 여러 사람이 하나의 게시글을 동시에 읽을 때, DB에서 내용을 가져오는 대기 시간이 존재하므로 비동기 드라이버나 ORM을 활용하면 서버가 대기하는 동안 다른 요청을 처리할 수 있어 효율이 좋아집니다.
- 고려해야 할 트레이드오프: 하지만 서버가 비동기로 요청을 너무 빠르게 다 받아내면, 역으로 데이터베이스(DB) 쪽에 부하가 몰릴 수 있습니다. 이런 경우 DB 단에서 캐싱이나 인덱스 최적화 같은 설계가 추가로 필요할 수 있습니다.
📌 최종 요약
트래픽이 크지 않다면 동기 방식도 충분히 안정적이며 구현을 단순화시켜 줍니다. 무조건 비동기가 정답이라기보다는, 서비스의 규모와 시스템의 어느 지점에 병목이 있는지에 따라 개발자가 알맞게 선택해야 합니다.
'IT' 카테고리의 다른 글
| [Deep Learning] 경사 하강법과 Adam 옵티마이저 차이: 수식과 코드로 이해하는 장단점 비교 (0) | 2026.06.19 |
|---|---|
| [Python/Pandas] 데이터 리모델링 완벽 이해: 와이드 포맷을 롱 포맷으로 (melt와 pivot) (0) | 2026.06.06 |