python / / 2024. 8. 16. 06:57

[fastapi] 동시성, async/await

FastAPI의 동시성과 async/await 이해하기

FastAPI는 최근 인기를 끌고 있는 파이썬 웹 프레임워크로, 비동기 프로그래밍의 장점을 적극 활용할 수 있다. FastAPI는 특히 비동기 I/O 작업에서 높은 성능을 발휘하며, 이를 위해 asyncawait 구문을 사용한다.


1. 동시성(Concurrency)과 FastAPI

1.1 동시성이란?

동시성(Concurrency)은 여러 작업을 동시에 실행할 수 있는 능력을 의미한다. 하지만 이것이 곧 여러 작업이 실제로 동시에 수행된다는 것을 의미하지는 않는다. 일반적으로 동시성은 한 작업이 일시적으로 중단되었을 때, 다른 작업이 그 시간을 활용하여 실행되는 것을 뜻한다. 이는 특히 I/O-bound 작업에서 큰 이점을 제공한다.

1.2 동시성의 필요성

전통적인 웹 애플리케이션은 대부분 CPU-bound 작업이 많지 않고, I/O-bound 작업이 대부분이다. 예를 들어, 데이터베이스에 쿼리를 보내고 결과를 기다리거나, 외부 API와 통신하는 등의 작업이 여기에 해당합니다. 이런 상황에서 동기 방식으로 코드를 작성하면, 작업 하나가 완료될 때까지 다른 작업이 대기해야 하므로 비효율적이다.

2. async/await와 비동기 프로그래밍

2.1 async/await 기본 개념

파이썬 3.5부터 도입된 asyncawait 키워드는 비동기 프로그래밍을 보다 쉽게 구현할 수 있게 해준다. async는 함수가 비동기 함수임을 나타내고, await는 해당 작업이 완료될 때까지 기다리라는 뜻이다.

import asyncio

async def fetch_data():
    print("Fetching data...")
    await asyncio.sleep(2)
    print("Data fetched")
    return "data"

async def main():
    result = await fetch_data()
    print(result)

asyncio.run(main())

위의 코드는 fetch_data 함수에서 2초 동안 데이터를 가져오는 작업을 시뮬레이션한다. await asyncio.sleep(2)는 2초 동안 대기하면서 다른 작업이 실행될 수 있도록 한다.

2.2 async/await의 동작 방식

await는 실제로 비동기 작업이 완료될 때까지 해당 함수를 일시 중단하고, 이 중단된 시간 동안 다른 비동기 작업이 실행될 수 있게 한다. 이렇게 함으로써 여러 I/O-bound 작업을 동시에 처리할 수 있는 효율적인 프로그램을 작성할 수 있다.

3. FastAPI에서의 비동기 프로그래밍

3.1 FastAPI의 비동기 엔드포인트

FastAPI는 비동기 프로그래밍을 기본적으로 지원합니다. 엔드포인트 함수에 async 키워드를 붙이면, 비동기 함수로 정의할 수 있으며, FastAPI는 이를 효율적으로 처리합니다.

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items():
    items = await some_async_db_call()
    return items

위의 예시에서 read_items 함수는 비동기 함수로 정의되었고, 데이터베이스에서 데이터를 가져오는 작업을 await 키워드를 통해 비동기적으로 처리하고 있다.

3.2 비동기 데이터베이스와의 통합

FastAPI는 여러 비동기 데이터베이스 클라이언트와 잘 통합된다. 예를 들어, asyncpgTortoise ORM 등을 사용하여 비동기적으로 데이터베이스와 상호작용할 수 있다.

from databases import Database

database = Database("postgresql://user:password@localhost/dbname")

@app.on_event("startup")
async def startup():
    await database.connect()

@app.on_event("shutdown")
async def shutdown():
    await database.disconnect()

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    query = "SELECT * FROM users WHERE id = :user_id"
    return await database.fetch_one(query, values={"user_id": user_id})

이 코드에서 데이터베이스 연결 및 종료는 FastAPI의 이벤트 시스템을 통해 관리되며, 사용자 정보를 비동기적으로 쿼리하고 있다.

4. 성능 최적화와 주의사항

4.1 비동기 작업과 CPU-bound 작업

비동기 작업은 주로 I/O-bound 작업에서 효율적이다. 그러나 CPU-bound 작업에서는 비동기 프로그래밍이 크게 도움이 되지 않는다. CPU-bound 작업은 async 함수가 아닌 일반 함수로 처리하거나, 멀티스레딩이나 멀티프로세싱을 고려해야 한다.

4.2 비동기 함수의 호출

비동기 함수는 항상 await 키워드와 함께 호출되어야 한다. 그렇지 않으면 예상치 못한 동작이 발생할 수 있다. 또한, 비동기 함수를 호출할 때는 가능한 한 많은 비동기 작업을 동시에 실행하도록 설계해야 성능을 극대화할 수 있다.

5. 결론

FastAPI는 async/await을 적극적으로 활용하여 효율적인 웹 애플리케이션을 구축할 수 있는 강력한 도구를 제공한다. I/O-bound 작업을 많이 수행하는 애플리케이션에서는 동시성을 최대한 활용하여 성능을 최적화할 수 있으며, FastAPI의 비동기 엔드포인트와 다양한 비동기 라이브러리를 결합하여 더욱 효율적인 코드를 작성할 수 있다.

비동기 프로그래밍에 익숙해지면, FastAPI의 진정한 잠재력을 발휘할 수 있을 것이다. 동시성 모델에 대한 이해와 async/await의 올바른 사용은 고성능 웹 애플리케이션 개발의 필수 요소가 될 것이다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유