[Python] @asynccontextmanager - 비동기 컨텍스트 관리자

류지수·2025년 6월 2일

@asynccontextmanager는 Python의 contextlib 모듈에서 제공하는 데코레이터로, 비동기 컨텍스트 관리자를 간단하게 만들 수 있게 해준다.
이를 이용하면 async with문과 함께 사용할 수 있는 컨텍스트 관리자를 쉽게 구현할 수 있다.


컨텍스트 매니저란?

자원의 획득과 해제를 책임지는 객체

예시

  • 파일 열고 닫기
  • DB 연결 및 정리
  • 락(Lock) 획득 및 해제
  • 리소스 관리
  • 네트워크 연결 및 해제
  • Redis 커넥션 풀
  • 세션 임시 설정
  • 타임아웃 관리
  • ...
# 동기
with open("file.txt") as f:
    data = f.read()

# 비동기
async with aiofiles.open("file.txt", "r") as f:
    data = await f.read()

사용하면 좋을 경우

자원 획득과 정리를 단순하고 간결하게 처리하고 싶을 때
복잡한 상태 관리가 필요하지 않은 짧은 생명주기의 비동기 자원을 다룰 때



비동기 함수 (async def)와 차이점

항목async def비동기 컨텍스트 매니저
목적비동기 함수 실행자원 열고 닫기
사용 문법await func()async with manager:
핵심 메서드없음__aenter__(), __aexit__()
상태 관리없음있음 (초기화/정리 구조)

여기서 aenter()와 aexit()란?

비동기 컨텍스트 매니저는 내부적으로 다음 메서드를 사용

  • __aenter__() : async with 블록 진입 시 실행
  • __aexit__() : 블록 종료 시 실행 (예외 발생 여부와 관계없이)

직접 클래스로 구현할 경우, 이 메서드를 정의해야한다.

from asyncio import run
from contextlib import AsyncContextDecorator

class mycontext(AsyncContextDecorator):
    async def __aenter__(self):
        print('Starting')
        return self

    async def __aexit__(self, *exc):
        print('Finishing')
        return False

사용 방법

@context()
async def function():
	print('The bit in the middle')
    
>>> run(function(())
The bit in the middle
Finishing
@context()
async def function():
   async with mycontext():
        print('The bit in the middle')

>>> run(function())
Starting
The bit in the middle
Finishing

@asynccontextmager 란?

python의 contextlib 모듈에서 제공하는 데코레이터이다.
비동기 컨텍스트 매니저를 함수 하나로 쉽게 작성할 수 있게 도와준다.

1. DB 연결 관리 예시

@asynccontextmanager
async def get_db():
    db = await connect()
    try:
        yield db
    finally:
        await db.close()

2. python 공식 문서 예시

# Added in version 3.7.
from contextlib import asynccontextmanager

@asynccontextmanager
async def get_connection():
    conn = await acquire_db_connection()
    try:
        yield conn
    finally:
        await release_db_connection(conn)

async def get_all_users():
    async with get_connection() as conn:
        return conn.query('SELECT ...')
  • get_connection()함수는 async with 문에서 사용할 수 있는 컨텍스트 매니저처럼 작동
  • yield는 자원을 블록으로 넘겨주는 지점이고, finally는 자원 해제 시 실행

Python 3.10부터 데코레이터로 사용 가능

import time
from contextlib import asynccontextmanager

@asynccontextmanager
async def timeit():
    now = time.monotonic()
    try:
        yield
    finally:
        print(f'it took {time.monotonic() - now}s to run')

@timeit()
async def main():
    # ... async code ...
  • @timeit()main() 함수 실행 전후로 실행 시간을 측정
  • 내부적으로 매번 새로운 제너레이터 인스턴스를 생성해주기 때문에 여러 번 재사용 가능

방법구현 필요장점
클래스 직접 구현__aenter__() / __aexit__()더 많은 제어
@asynccontextmanager구현 필요 없음 (자동 처리)간결하고 직관적


주의할 점

  • yield는 한 번만 사용할 수 있음
  • yield가 함수 내부에 반드시 1회 존재해야 함
  • yield 전 → 초기화, 이후 → 정리 작업
  • 반드시 try...finally로 정리 코드 작성할 것


정리

  • async def: 단순 비동기 함수
  • async with: 자원 관리
  • @asynccontextmanager: 복잡한 컨텍스트 매니저를 간단하게 구현


참고

profile
끄적끄적

0개의 댓글