
FastAPI는 async/await 기반의 비동기 처리 덕분에 고성능 API 서버 구축에 최적화되어 있다. 그렇다면 반대로, 동일한 기능을 동기 방식으로 구현한다면 어떤 차이가 생길지 알아보자.
이 글에서는 FastAPI 비동기 코드를 동기 방식으로 전환하여 직접 비교해보고, 그 과정을 통해 비동기 방식의 이점이 단순한 속도 이상의 의미임을 검증해보고자 한다.
FastAPI의 비동기 구조를 동기 방식으로 전환하기 위해서는 몇 가지 명확한 수정이 필요하다. 아래는 개발하며 주로 변경되는 핵심 요소들이다.
# 동기
ASYNC_DB_URL = "mysql+aiomysql://user:pass@localhost:3306/dbname"
# 비동기
DB_URL = "mysql+pymysql://user:pass@localhost:3306/dbname"
aiomysql은 비동기 전용 드라이버이고, pymysql은 동기 전용이다.
ORM(SessionLocal, SQLAlchemy 등)이 에러 없이 작동하려면 DB 드라이버와 ORM 방식이 일치해야 한다.
# 동기
@router.post("/tasks")
def create_task(...):
...
# 비동기
@router.post("/tasks")
async def create_task(...):
...
async def 대신 일반적인 def로 선언
FastAPI는 자동으로 동기/비동기 방식을 구분하여 처리하므로, 내부 코드 실행 방식에 맞춰 명확하게 선언해야 한다.
# 동기
def get_db() -> Session:
db = SessionLocal()
try:
yield db
finally:
db.close()
# 비동기
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
비동기에서는 async with, 동기에서는 try-finally를 사용해 세션을 관리한다.
비동기 컨텍스트 매니저를 통해 처리한다.
# 동기 방식
return task_crud.create_task(db, task_body)
# 비동기 방식
return await task_crud.create_task(db, task_body)
await가 없으면 비동기 함수는 단순히 coroutine 객체만 반환하고, 실행되지 않는다.
동기 방식에서는 await 없이 바로 리턴한다.
python3 -m uvicorn api.main2:app --host 0.0.0.0 --port 8004 --reload
위 명령어로 main2.py를 실행하려 했지만 실제로는 main.py의 app 객체가 실행
--reload 옵션은 uvicorn이 파일 변경을 감지해 재시작하는 데몬을 띄웁니다. 이 때 최초 실행 지점의 모듈 경로 또는 import 로직이 꼬일 수 있다.
✅ main.py 와 main2.py에 app=FastAPI()가 동일하게 있게되므로 이전 main 삭제 조치 후 해결
router의 API에서 update_task() 혹은 delete_task()함수만 호출했더니
ORM(SQLAlchemy)오류 발생
original에서 실제 DB객체가 없으므로 NoneType 에러 유발
500 Internal Server Error
AttributeError: 'NoneType' object has no attribute 'title'
✅ 해결
@router.put("/tasks/{task_id}", response_model=TaskCreateResponse)
def update_task(task_id: int, task_body: TaskCreate, db: Session = Depends(get_db)):
db_task = task_crud.get_task(db, task_id)
if db_task is None:
raise HTTPException(status_code=404, detail="Task not found")
return task_crud.update_task(db=db, task_create=task_body, original=db_task)
먼저 get_task()로 DB에서 해당 task가 존재하는지 확인 후 전달
동기 방식은 구현이 단순하고, 구조가 직관적이라는 장점이 있다. 특히 대부분의 CRUD 중심 API 서버에서는 동기 처리만으로도 충분히 빠른 응답 속도를 제공할 수 있음을 확인했다. 또한, 요청 흐름이 순차적으로 처리되기 때문에 예외 상황에 대한 디버깅이 비교적 용이하다는 점도 큰 장점으로 느껴졌다.
반면, 비동기 방식은 수천 개 이상의 요청을 병렬로 처리해야 하는 환경에서 매우 유리하다. 외부 API 호출이나 데이터베이스 접근처럼 I/O 작업이 많은 경우에도 응답이 블로킹되지 않고, 전체 시스템의 처리 효율을 향상시킬 수 있었다. 특히 실시간성과 응답 속도가 중요한 서비스에서는 비동기 구조가 필수적이라는 점을 체감할 수 있었다.
ORM은 단순히 SQL을 대체하는 수준을 넘어, Python 객체를 통해 테이블과 레코드를 표현할 수 있는 강력한 도구로 활용하였다. 이를 통해 쿼리 작성이 훨씬 안전하고 명확해졌으며, 복잡한 조인이나 조건문도 Python 문법으로 처리할 수 있어 생산성과 유지보수성이 크게 향상되었다.