본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Async Generator와 Async Context Manager의 3가지 실제 활용 사례와 해결 방법

by Papa Martino V 2026. 2. 26.
728x90

Async Generator와 Async Context Manager
Async Generator와 Async Context Manager

 

파이썬의 비동기 프로그래밍(Asynchronous Programming)은 단순히 async/await 키워드를 사용하는 수준을 넘어, 리소스의 효율적인 관리와 대규모 데이터 스트리밍 처리에서 그 진가를 발휘합니다. 특히 Async GeneratorAsync Context Manager는 복잡한 비동기 워크플로우를 간결하고 안전하게 유지하는 핵심 도구입니다. 본 글에서는 이 두 기술의 본질적인 차이를 분석하고, 실무에서 마주하는 병목 현상을 해결하기 위한 구체적인 활용 방법을 제시합니다.


1. 개념의 본질: 왜 비동기 전용 도구가 필요한가?

전통적인 제너레이터와 컨텍스트 매니저는 동기적인 루프 내에서 작동하므로, 네트워크 I/O나 데이터베이스 쿼리 대기 시간 동안 전체 프로세스를 블로킹(Blocking)합니다. 하지만 비동기 버전은 대기 시간 동안 이벤트 루프가 다른 작업을 처리할 수 있도록 제어권을 양도(Yield)합니다.

① Async Generator (yield + async)

데이터의 양이 너무 많아 메모리에 한꺼번에 올릴 수 없거나, 소켓을 통해 지속적으로 데이터가 유입되는 스트리밍 상황에서 유용합니다. __anext__ 매직 메서드를 통해 비동기적으로 다음 아이템을 생성합니다.

② Async Context Manager (async with)

네트워크 연결, 파일 핸들러, DB 세션 등 비동기 환경에서 자원의 '생성-사용-해제' 생명 주기를 보장합니다. __aenter____aexit__를 사용하여 예외 상황에서도 안전한 리소스 회수를 해결합니다.


2. 기술적 비교: Async Generator vs Async Context Manager

두 도구는 사용 목적과 구조에서 명확한 차이를 보입니다. 아래 표는 이를 체계적으로 비교한 내용입니다.

항목 Async Generator Async Context Manager
주요 목적 연속적인 데이터 스트림 생성 및 소비 안전한 리소스 획득 및 해제 (Lifecycle)
핵심 키워드 async for, yield async with
데이터 처리 데이터를 한 번에 하나씩 비동기로 전달 블록 내부의 작업이 완료된 후 정리 작업 수행
해결 과제 메모리 부족 문제(OOM) 방지 리소스 누수(Memory/Connection Leak) 방지

3. 실무 중심의 3가지 실제 활용 사례

사례 1: 대용량 API 페이지네이션 스트리밍

수천 개의 레코드를 가진 외부 API를 호출할 때, 모든 데이터를 리스트에 담는 방식은 메모리 부하를 초래합니다. Async Generator를 사용하면 네트워크 응답을 기다리면서도 필요한 만큼만 데이터를 가져와 처리할 수 있습니다.

사례 2: 비동기 데이터베이스 연결 풀 관리

트랜잭션이 시작될 때 연결을 확보하고, 작업이 끝난 후(혹은 에러 발생 시) 반드시 연결을 반환해야 합니다. Async Context Manager는 비동기 작업 중 발생하는 예외를 캡처하여 안전하게 세션을 종료하는 해결 방법을 제공합니다.

사례 3: 비동기 웹 크롤러의 속도 제한(Throttling)

비동기 제너레이터를 사용하여 타겟 URL을 일정 간격으로 생성하고, 컨텍스트 매니저를 통해 각 세션의 동시 접속 수를 제한함으로써 서버 차단을 방지하는 전략을 구현할 수 있습니다.


4. Sample Example: 결합형 아키텍처 구현

다음 예제는 실무에서 가장 흔히 쓰이는 비동기 DB 세션 관리 및 데이터 스트리밍을 구현한 코드입니다.


import asyncio
import random

# 1. Async Context Manager 활용: 가상의 DB 연결 세션
class AsyncDatabaseSession:
    async def __aenter__(self):
        print("--- DB 세션 시작: 연결 확보 중 ---")
        await asyncio.sleep(0.5)  # 연결 대기 시뮬레이션
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await asyncio.sleep(0.2)  # 정리 작업 시뮬레이션
        print("--- DB 세션 종료: 자원 해제 완료 ---")

# 2. Async Generator 활용: 비동기 데이터 스트리밍
async def get_sensor_data_stream(limit=5):
    """센서 데이터를 실시간으로 읽어오는 비동기 제너레이터"""
    for i in range(1, limit + 1):
        await asyncio.sleep(1)  # I/O 대기 (예: 네트워크 패킷 대기)
        value = random.uniform(20.0, 30.0)
        yield f"Batch {i}: {value:.2f}°C"

# 3. 실무 응용 로직
async def process_data():
    async with AsyncDatabaseSession() as db:
        print("데이터 처리를 시작합니다...")
        async for data in get_sensor_data_stream(3):
            print(f"로그 기록: {data}")
            # 여기서 비동기 DB 저장 로직 수행 가능
        print("모든 스트림 처리가 완료되었습니다.")

if __name__ == "__main__":
    asyncio.run(process_data())

5. 결론: 고성능 파이썬 코드를 위한 설계 방향

현대적인 파이썬 백엔드 개발에서 Async Generator와 Async Context Manager는 옵션이 아닌 필수입니다. 단순히 await를 남발하는 것보다, 데이터의 흐름은 Generator로 제어하고 리소스의 생명 주기는 Context Manager로 보호하는 설계 패턴이 훨씬 유지보수가 용이하며 성능 또한 우수합니다. 본 가이드에서 제시한 방법들을 프로젝트에 적용하여 더 견고한 비동기 시스템을 구축해 보시기 바랍니다.


6. 내용의 출처 및 참고 자료

  • Python Software Foundation. "PEP 525 -- Asynchronous Generators."
  • Python Software Foundation. "PEP 492 -- Coroutines with async and await syntax."
  • EdgeDB Blog. "How to use Async Context Managers in Python."
  • Real Python. "Asynchronous Iterators and Generators in Python."
728x90