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

[PYTHON] 비동기 프로그래밍의 핵심, Future와 Task의 2가지 근본적 차이와 협력 방법

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

Future와 Task
Future와 Task

파이썬의 asyncio 라이브러리를 깊게 파고들다 보면 반드시 마주치게 되는 두 가지 존재가 있습니다. 바로 FutureTask입니다. 많은 개발자가 이 둘을 혼용하거나 단순히 '비동기 작업의 결과물' 정도로만 이해하고 넘어가곤 합니다. 하지만 고성능 비동기 애플리케이션을 설계하고 디버깅하기 위해서는 이들의 계층 구조와 실행 메커니즘을 명확히 구분할 줄 알아야 합니다. 본 포스팅에서는 파이썬 비동기 생태계의 기초가 되는 asyncio.Future와 이를 확장한 asyncio.Task의 내부 동작 원리를 분석하고, 실제 코드에서 발생할 수 있는 문제 해결 방안을 심도 있게 다룹니다.


1. Future 객체: "기다림의 약속"

Future는 아직 완료되지 않은 작업의 최종 결과를 담는 저수준(low-level) 객체입니다. 비유하자면, 식당에서 주문 후 받은 '진동벨'과 같습니다. 진동벨 자체는 음식이 아니지만, 음식이 준비되면 소리가 울리고(상태 변화), 결국 음식을 받을 수 있는 권리를 나타냅니다.

  • 상태 관리: PENDING, CANCELLED, FINISHED 중 하나의 상태를 가집니다.
  • 콜백 메커니즘: 작업이 완료되었을 때 실행될 함수를 등록할 수 있습니다.
  • 수동적 특성: Future는 스스로 실행되지 않습니다. 외부에서 set_result()set_exception()을 호출해줘야 비로소 완료됩니다.

2. Task 객체: "살아 움직이는 Future"

TaskFuture를 상속받은 고수준(high-level) 객체입니다. 코루틴(coroutine)을 실행하기 위한 전용 객체로, 이벤트 루프에 의해 스케줄링되어 실제로 "일을 하는" 주체입니다.

  • 자동 실행: 생성과 동시에 이벤트 루프에 등록되어 실행될 준비를 마칩니다.
  • 코루틴 래퍼: 코루틴의 실행 상태를 추적하며, 코루틴이 yieldawait를 만날 때마다 제어권을 관리합니다.
  • 능동적 특성: 루프가 작업을 진행시키므로 개발자가 직접 결과를 주입할 필요가 없습니다.

3. Future와 Task의 핵심 차이 및 관계 비교

이 두 객체의 기술적 접점과 차이점을 한눈에 파악할 수 있도록 정리했습니다.

비교 항목 Future (asyncio.Future) Task (asyncio.Task)
계층 구조 부모 클래스 (기본형) 자식 클래스 (Future 상속)
주요 목적 결과값 저장을 위한 '틀' 제공 코루틴의 실행 및 관리
실행 주체 외부 작업(DB, Network 드라이버 등) 이벤트 루프(Event Loop)
생성 방법 loop.create_future() asyncio.create_task()
사용 빈도 라이브러리 제작 시 주로 사용 일반 비동기 로직 구현 시 필수

4. 실전 예제: Task를 이용한 문제 해결 방법

아래 코드는 여러 개의 비동기 작업을 Task로 만들어 병렬로 처리하고, 그 관계를 확인하는 예시입니다.


import asyncio

async def fetch_data(id, delay):
    print(f"작업 {id} 시작 (지연: {delay}초)")
    await asyncio.sleep(delay)
    print(f"작업 {id} 완료")
    return f"결과 {id}"

async def main():
    # 1. 코루틴을 Task로 감싸서 즉시 스케줄링 (Task 생성)
    task1 = asyncio.create_task(fetch_data(1, 3))
    task2 = asyncio.create_task(fetch_data(2, 2))

    # 2. Task가 Future의 인스턴스인지 확인
    print(f"task1은 Future인가요? {isinstance(task1, asyncio.Future)}")

    # 3. Task 완료 기다리기 (방법: await)
    # Task는 Future를 상속받았으므로 await 가능
    results = await asyncio.gather(task1, task2)
    print(f"최종 결과: {results}")

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

5. 결론 및 요약

파이썬 비동기 프로그래밍에서 Future는 작업의 '상태'와 '결과'를 나타내는 추상적인 약속이고, Task는 그 약속을 이행하기 위해 실제로 코루틴을 돌리는 실행 단위입니다. 우리가 await를 사용할 때 내부적으로는 Future 객체가 완료되기를 기다리는 것이며, create_task를 호출하는 것은 파이썬에게 "이 코루틴을 지금 당장 실행 대기열에 올려줘"라고 명령하는 것과 같습니다. 이 메커니즘을 이해하면 복잡한 비동기 흐름에서도 데이터가 어떻게 전달되는지 명확히 파악할 수 있습니다.


참고 문헌 (Sources)

  • Python 공식 문서: asyncio — Low-level APIs (Futures)
  • Python 공식 문서: asyncio — High-level APIs (Tasks)
  • "Fluent Python" by Luciano Ramalho - Chapter 18: Concurrency with asyncio
728x90