본문 바로가기

카테고리 없음

Python : Async With - 비동기 컨텍스트 관리자 와 aiohttp 사용가이드

 async with는 비동기 컨텍스트 관리자로, 비동기 작업 내에서 자원을 안전하게 관리하고 정리할 수 있도록 돕는 구문입니다. 일반적인 with 문과 유사하지만, 비동기 I/O 작업과 호환되도록 설계되었습니다. 이를 통해 파일이나 네트워크 연결 같은 리소스를 효율적으로 다루면서, 자동으로 리소스를 열고 닫아주는 관리 기능을 제공합니다.

 

아래 예제는 async with 없이 aiohttp 를 사용하는 예제입니다.  aiohttp 는 Python에서 비동기 HTTP 클라이언트와 서버를 쉽게 구현할 수 있도록 지원하는 패키지입니다.

Async With 없이 구현된 예제

import aiohttp
import asyncio

async def fetch_data(url):
    session = aiohttp.ClientSession()  # 수동으로 세션 생성
    try:
        response = await session.get(url)
        data = await response.text()
        return data
    finally:
        await session.close()  # 수동으로 세션 종료

async def main():
    url = "https://www.tistory.com/"
    data = await fetch_data(url)
    print("Data length:", len(data))

asyncio.run(main())

 

  위 예제에서는 aiohttp.ClientSession을 사용하여 수동으로 세션을 생성하고 있습니다. 하지만, 이 방식은 자원을 정리하는 await session.close() 호출이 빠지지 않도록 주의해야 합니다. 만약 이 호출을 잊으면 프로그램이 종료되지 않거나 메모리 및 네트워크 자원이 낭비될 수 있습니다. 

  async with 를 사용하면 이러한 귀찮은 작업들을 편하고 간단하게 처리할 수 있습니다.

 

Async With 를 사용하여 구현된 예제

import aiohttp
import asyncio

async def fetch_data(url):
    async with aiohttp.ClientSession()  as session : # 자동으로 세션을 관리
        response = await session.get(url)
        data = await response.text()
        return data
    

async def main():
    url = "https://www.tistory.com/"
    data = await fetch_data(url)
    print("Data length:", len(data))

asyncio.run(main())

 

AsyncContextManager  

 async with 문은 AsyncContextManager 객체와 함께 사용되어야 하는데 예제에서는 session 객체가 이 객체입니다.

 AsyncContextManager는 비동기적으로 __aenter____aexit__ 메서드가 구현되어 있어야 합니다. 예를 들어 아래와 같이  직접 AsyncContextManager 클래스를 구현할 수 있습니다.

class AsyncContextManager:
    async def __aenter__(self):
        print("Entering context")
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        print("Exiting context")

async def use_context():
    async with AsyncContextManager() as manager:
        print("Inside context")

asyncio.run(use_context())

#output
#Entering context
#Inside context
#Exiting context

 

   메인 예제에서 aiohttp.ClientSession()이 반환하는 session 객체가 AsyncContextManager 객체가 맞는지는 아래와 같이 dir 함수를 확인하여 확인해 볼 수 있습니다. 

import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession() as session:
        print(dir(session)) 
        
# 이벤트 루프 실행
asyncio.run(main())

#output
#['ATTRS', '__aenter__', '__aexit__', '__annotations__', ... 'ws_connect']

 

  결과값에 '__aenter__' 와 '__aexit__'  메서드가 보입니다. 

 

  추가적으로  메인 예제에서 사용된 session.get() 에 의하여 반환된 response 객체도 AsyncContextManager 객체입니다. 

  네트워크 요청을 수행하는 session.get()은 HTTP 커넥션을 열어 서버와 통신하며, 이를 닫지 않으면 메모리나 커넥션이 누적되어 자원 누수가 발생할 수 있습니다. async with 구문을 사용하면 요청이 끝난 후 자동으로 자원을 정리해 이러한 문제를 방지합니다. 

 

  따라서, 최종적으로 아래와 같이 예제 코드를 최적화할 수 있습니다. 

 Async With 를 사용하여 최적화된 예제

import aiohttp
import asyncio

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:  # 비동기 세션 생성
        async with session.get(url) as response:  # 비동기 요청
            data = await response.text()  # 비동기적으로 응답 데이터 읽기
            print(f"Data fetched from {url}")
            return data

async def main():
    url = "https://www.tstory.com"
    data = await fetch_data(url)
    print("Data length:", len(data))

# 이벤트 루프 실행
asyncio.run(main())

 

1. async with 구문 사용: async with aiohttp.ClientSession() as session은 비동기 세션을 생성하고, session 객체가 자동으로 열리고 닫히도록 합니다.

 

2. 비동기 요청 및 응답 관리: session.get(url)response.text()는 비동기적으로 처리되며, 요청 완료 후 데이터를 반환합니다.

 

3. 리소스 관리: 요청이 끝나면 sessionresponse 객체가 자동으로 정리됩니다. 이를 통해 세션이 누출되지 않고 적절하게 관리됩니다.

 

async with 는 주로 아래와 같은 경우에 사용됩니다:

 

1. 파일 관리: 비동기적으로 파일을 열고 닫을 때 사용합니다.

 

2. 네트워크 연결 관리: 네트워크 세션이나 소켓 연결을 안전하게 관리합니다.

 

3. 데이터베이스 연결: 비동기 데이터베이스 클라이언트를 사용할 때 연결을 열고 닫는 데 활용합니다.

정리

async with는 비동기 프로그래밍에서 자원을 안전하게 관리하는 필수적인 구문으로, 리소스 누수를 방지하며 코드의 가독성과 안정성을 높이는 데 큰 도움을 줍니다. 이를 통해 비동기 환경에서의 리소스 관리를 더욱 직관적이고 간결하게 구현할 수 있습니다.