TL;DR: Python에서 OpenAI API를 비동기로 호출하려면 `AsyncOpenAI` 클라이언트와 `asyncio` 라이브러리를 함께 사용하면 됩니다.
왜 OpenAI API를 비동기로 호출해야 할까요?
AI 애플리케이션을 개발하다 보면 동시에 여러 요청을 처리해야 하는 상황이 자주 발생합니다. 예를 들어, 여러 사용자가 동시에 챗봇을 사용하거나, 대량의 텍스트를 한꺼번에 분석해야 할 때가 있습니다. 이런 상황에서 동기 방식으로 API를 호출하면 각 요청이 완료될 때까지 프로그램이 멈추게 되어 성능이 크게 저하됩니다.
비동기 프로그래밍을 활용하면 하나의 요청이 응답을 기다리는 동안 다른 작업을 동시에 처리할 수 있습니다. 이를 통해 애플리케이션의 처리량을 극적으로 향상시킬 수 있으며, 사용자 경험도 훨씬 매끄러워집니다. OpenAI의 공식 Python 라이브러리는 버전 1.0부터 비동기 클라이언트를 공식 지원하므로, 이를 적극 활용하는 것이 현명합니다.
사전 준비: 필요한 패키지 설치
비동기 OpenAI API 호출을 시작하기 전에 필요한 패키지를 설치해야 합니다. Python 3.7 이상 버전이 필요하며, `asyncio`는 Python 표준 라이브러리에 포함되어 있으므로 별도 설치가 필요 없습니다.
터미널에서 다음 명령어를 실행하여 OpenAI 라이브러리를 설치하세요:
pip install openai설치가 완료되면 OpenAI API 키를 환경 변수로 설정합니다. 보안을 위해 코드에 직접 API 키를 입력하지 않는 것이 중요합니다:
export OPENAI_API_KEY="your-api-key-here"Windows 사용자라면 PowerShell에서 `$env:OPENAI_API_KEY="your-api-key-here"`를 사용하거나, `.env` 파일과 `python-dotenv` 패키지를 활용하는 것을 권장합니다.
AsyncOpenAI 클라이언트 기본 사용법
OpenAI Python 라이브러리는 `AsyncOpenAI`라는 전용 비동기 클라이언트를 제공합니다. 이 클라이언트는 동기 버전인 `OpenAI`와 거의 동일한 인터페이스를 가지지만, 모든 메서드가 코루틴(coroutine)으로 동작합니다.
아래는 단일 비동기 요청을 보내는 기본 예제입니다:
import asyncio
from openai import AsyncOpenAI
# AsyncOpenAI 클라이언트 초기화
client = AsyncOpenAI()
async def get_completion(prompt: str) -> str:
"""단일 프롬프트에 대한 완성 텍스트를 비동기로 가져옵니다."""
response = await client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "당신은 도움이 되는 AI 어시스턴트입니다."},
{"role": "user", "content": prompt}
],
max_tokens=500
)
return response.choices[0].message.content
async def main():
result = await get_completion("Python 비동기 프로그래밍의 장점을 설명해주세요.")
print(result)
# 이벤트 루프 실행
if __name__ == "__main__":
asyncio.run(main())
이 코드에서 핵심은 `async def`로 함수를 정의하고, `await` 키워드를 사용하여 비동기 작업이 완료될 때까지 기다리는 것입니다. `asyncio.run()`은 이벤트 루프를 생성하고 비동기 함수를 실행하는 진입점 역할을 합니다.
여러 요청을 동시에 처리하기: asyncio.gather() 활용
비동기 프로그래밍의 진정한 강점은 여러 작업을 동시에 처리할 때 발휘됩니다. `asyncio.gather()`를 사용하면 여러 API 요청을 병렬로 실행할 수 있습니다.
asyncio.gather()로 배치 처리하기
예를 들어, 10개의 다른 주제에 대한 요약을 동시에 생성해야 한다고 가정해 보겠습니다. 동기 방식이라면 순서대로 10번 요청해야 하지만, 비동기 방식에서는 거의 동시에 모든 요청을 보낼 수 있습니다:
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def summarize_text(text: str, index: int) -> dict:
"""주어진 텍스트를 요약합니다."""
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "텍스트를 2-3문장으로 간결하게 요약하세요."},
{"role": "user", "content": text}
],
max_tokens=200
)
return {
"index": index,
"summary": response.choices[0].message.content
}
async def process_multiple_texts(texts: list) -> list:
"""여러 텍스트를 동시에 요약합니다."""
tasks = [
summarize_text(text, i)
for i, text in enumerate(texts)
]
results = await asyncio.gather(*tasks)
return results
async def main():
sample_texts = [
"인공지능은 현대 사회에서 점점 더 중요한 역할을 하고 있습니다...",
"Python은 데이터 과학과 머신러닝 분야에서 가장 인기 있는 언어입니다...",
"클라우드 컴퓨팅은 기업의 IT 인프라를 혁신적으로 변화시켰습니다..."
]
results = await process_multiple_texts(sample_texts)
for result in results:
print(f"텍스트 {result['index'] + 1} 요약: {result['summary']}\n")
if __name__ == "__main__":
asyncio.run(main())
속도 제한(Rate Limit) 처리하기
여러 요청을 동시에 보낼 때는 OpenAI API의 속도 제한(Rate Limit)을 고려해야 합니다. 너무 많은 요청을 한꺼번에 보내면 `RateLimitError`가 발생할 수 있습니다. 이를 방지하기 위해 `asyncio.Semaphore`를 사용하여 동시 요청 수를 제한하는 것이 좋습니다:
`semaphore = asyncio.Semaphore(5)`와 같이 설정하면 동시에 최대 5개의 요청만 처리되도록 제한할 수 있습니다. 또한 `tenacity` 라이브러리를 활용한 재시도 로직을 구현하면 일시적인 오류를 우아하게 처리할 수 있습니다.
스트리밍과 비동기 처리 결합하기
OpenAI API는 스트리밍 응답도 지원합니다. 비동기 클라이언트와 스트리밍을 함께 사용하면 사용자에게 실시간으로 텍스트를 표시할 수 있어 더욱 자연스러운 챗봇 경험을 제공할 수 있습니다.
비동기 스트리밍을 구현하려면 `async for` 루프를 사용합니다. `stream=True` 옵션을 활성화하고, 반환되는 청크(chunk)를 비동기적으로 처리하면 됩니다. 각 청크에는 부분적인 텍스트가 포함되어 있으며, 이를 실시간으로 화면에 출력하거나 WebSocket을 통해 클라이언트에 전송할 수 있습니다.
FastAPI와 같은 비동기 웹 프레임워크와 결합하면 더욱 강력한 실시간 AI 애플리케이션을 구축할 수 있습니다. FastAPI의 `StreamingResponse`와 AsyncOpenAI의 스트리밍을 함께 사용하면 서버에서 클라이언트로 실시간 데이터를 효율적으로 전송할 수 있습니다.
실전 팁과 모범 사례
비동기 OpenAI API 호출을 프로덕션 환경에서 사용할 때 고려해야 할 중요한 사항들이 있습니다.
클라이언트 재사용하기
`AsyncOpenAI` 클라이언트는 애플리케이션 전체에서 하나의 인스턴스를 재사용하는 것이 좋습니다. 매 요청마다 새로운 클라이언트를 생성하면 불필요한 오버헤드가 발생합니다. 싱글턴 패턴이나 의존성 주입을 통해 클라이언트를 관리하세요.
에러 처리 구현하기
비동기 코드에서도 적절한 예외 처리는 필수입니다. `try/except` 블록을 사용하여 `openai.APIError`, `openai.RateLimitError`, `openai.APITimeoutError` 등의 예외를 처리하세요. 특히 타임아웃 설정을 통해 응답이 너무 오래 걸리는 경우를 대비하는 것이 중요합니다.
Anakin.ai로 더 쉽게 시작하기
비동기 API 호출 코드를 직접 작성하고 관리하는 것이 부담스럽다면, Anakin.ai를 활용해 보세요. Anakin.ai는 복잡한 인프라 설정 없이도 OpenAI를 포함한 다양한 AI 모델을 손쉽게 활용할 수 있는 플랫폼입니다. 개발자와 비기술 사용자 모두가 AI 애플리케이션을 빠르게 구축하고 배포할 수 있도록 도와줍니다.
자주 묻는 질문 (FAQ)
Q: 동기 OpenAI 클라이언트와 비동기 클라이언트는 어떻게 다른가요?
동기 클라이언트(`OpenAI`)는 각 API 호출이 완료될 때까지 프로그램 실행을 차단합니다. 반면 비동기 클라이언트(`AsyncOpenAI`)는 `await` 키워드를 사용하여 응답을 기다리는 동안 다른 코루틴이 실행될 수 있도록 합니다. 단순한 스크립트에는 동기 방식이 적합하지만, 웹 서버나 대량 처리 작업에는 비동기 방식이 훨씬 효율적입니다.
Q: Jupyter Notebook에서 비동기 OpenAI 코드를 실행할 수 있나요?
네, 가능합니다. Jupyter Notebook은 자체 이벤트 루프를 실행하므로 `asyncio.run()` 대신 `await`를 직접 사용할 수 있습니다. 예를 들어, 노트북 셀에서 `result = await get_completion("안녕하세요")`와 같이 직접 실행하면 됩니다. 만약 충돌이 발생한다면 `nest_asyncio` 패키지를 설치하여 해결할 수 있습니다.
Q: 비동기 처리 시 OpenAI API 비용이 더 많이 발생하나요?
비동기 처리 자체가 API 비용에 영향을 주지는 않습니다. OpenAI API 비용은 사용하는 토큰 수에 따라 결정되며, 요청 방식(동기/비동기)과는 무관합니다. 다만, 비동기 처리로 인해 더 많은 요청을 빠르게 보낼 수 있으므로 전체 토큰 사용량이 늘어날 수 있습니다. 속도 제한과 예산 관리를 위해 `asyncio.Semaphore`로 동시 요청 수를 조절하는 것을 권장합니다.