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

[PYTHON] 코루틴의 핵심 3가지 제어 메서드 send, throw, close 완벽 활용 방법과 차이 분석

by Papa Martino V 2026. 3. 8.
728x90

send, throw, close 완벽 활용
send, throw, close 완벽 활용

 

 

파이썬의 비동기 프로그래밍이 async/await로 대중화되기 전, 그 뿌리에는 제너레이터(Generator)를 활용한 코루틴(Coroutine)이 있었습니다. 단순히 값을 생산하는 것을 넘어 외부로부터 데이터를 주입받고, 예외를 던지며, 실행을 강제로 종료하는 등의 상호작용적 제어는 파이썬 코루틴의 정수라 할 수 있습니다. 오늘날 많은 개발자가 라이브러리 수준에서 제공하는 비동기 기능을 사용하지만, 그 내부 엔진이 어떻게 데이터를 주고받는지 이해하지 못하면 복잡한 동시성 문제를 해결하기 어렵습니다. 본 포스팅에서는 코루틴의 제어권을 쥐는 send(), throw(), close() 메서드의 구체적인 사용법과 내부 메커니즘을 심층적으로 다룹니다.


1. 코루틴 제어의 중추: 3가지 메서드의 정의와 목적

일반적인 함수는 호출되면 끝날 때까지 제어권을 독점하지만, 코루틴은 yield를 통해 실행을 일시 중단하고 외부와 소통합니다. 이때 외부(Caller)에서 코루틴 내부로 영향을 주는 3가지 통로가 존재합니다.

  • send(value): 코루틴 내부의 yield 식에 값을 전달하고 다음 yield까지 실행을 재개합니다.
  • throw(typ, val, tb): 코루틴이 일시 정지된 지점에서 특정 예외를 발생시켜 내부 로직을 중단하거나 예외 처리를 유도합니다.
  • close(): 코루틴의 실행을 영구적으로 중단하며, 내부적으로 GeneratorExit 예외를 던져 자원을 정리하게 합니다.

2. 각 메서드별 동작 메커니즘 및 결정적 차이

코루틴을 단순한 반복자로 쓰는 것과 제어 가능한 객체로 쓰는 것의 차이를 아래 표를 통해 한눈에 비교해 보십시오.

메서드 전달 대상 내부 반응 주요 용도 반환 값
send() 임의의 데이터 (Object) yield 식이 해당 값을 반환 상태 업데이트 및 데이터 주입 다음 yield가 내놓는 값
throw() 예외 객체 (Exception) yield 지점에서 예외 발생 오류 전파 및 비정상 흐름 제어 다음 yield 값 혹은 StopIteration
close() 없음 GeneratorExit 예외 발생 자원 해제 및 가비지 컬렉션 유도 None

3. [Sample Example] 실전 코루틴 파이프라인 제어

입력된 값의 평균을 계산하는 코루틴을 구현하면서, 외부에서 데이터를 주입(send), 오류를 발생(throw), 그리고 작업을 종료(close)하는 3단계 제어 과정을 확인해 보겠습니다.


def averager():
    total = 0.0
    count = 0
    average = None
    print("---> 코루틴 시작: 데이터 대기 중")
    try:
        while True:
            # yield는 값을 내보내고(None), 동시에 외부에서 들어올 값을 기다림
            term = yield average
            if term is None:
                break
            total += term
            count += 1
            average = total / count
            print(f"현재 평균: {average}")
    except ValueError:
        print("---> 경고: 부적절한 값이 입력되었습니다.")
    finally:
        print("---> 자원 정리: 코루틴을 종료합니다.")

# 코루틴 객체 생성
coro = averager()

# 1. 코루틴 프라이밍 (실행 준비)
next(coro) 

# 2. send()로 데이터 주입
coro.send(10)
coro.send(20)

# 3. throw()로 예외 강제 발생 (로직 테스트)
try:
    coro.throw(ValueError)
except StopIteration:
    pass

# 4. close()로 안전한 종료
coro = averager() # 재할당 후 예시
next(coro)
coro.close()

실행 분석: 위 예제에서 send()는 평균 계산을 위한 숫자를 넣고, throw()는 논리적인 오류 상황을 시뮬레이션하며, close()finally 블록을 실행시켜 안전하게 메모리를 해제하는 해결책을 보여줍니다.


4. 고성능 코루틴 설계를 위한 2가지 핵심 팁

코루틴을 실무에서 다룰 때 흔히 저지르는 실수를 방지하기 위한 전문적인 조언입니다.

  1. 자동 프라이밍(Automatic Priming): send()를 호출하기 전에 항상 next()를 호출하여 첫 번째 yield까지 코루틴을 진행시켜야 합니다. 이를 자동화하기 위해 데코레이터(Decorator) 패턴을 활용하는 것이 효율적입니다.
  2. GeneratorExit 처리: close()가 호출되었을 때 발생하는 GeneratorExit는 다시 yield를 호출할 수 없습니다. 만약 finally 절 안에서 값을 생성하려 하면 RuntimeError가 발생하므로 주의해야 합니다.

5. 결론: 제어권의 분산이 만드는 유연성

파이썬의 send, throw, close는 비동기 프레임워크의 저수준(low-level) 동작을 이해하는 핵심 열쇠입니다. 이 메서드들을 적절히 활용하면 데이터의 흐름을 단순히 소비하는 것이 아니라 적극적으로 '조종'할 수 있게 됩니다. 객체 지향적인 캡슐화와 함수형 프로그래밍의 유연성을 동시에 확보하여, 더욱 견고한 파이썬 애플리케이션을 구축해 보시기 바랍니다.


내용 출처 및 기술 참조

  • Python Language Reference. "Generators - send/throw/close methods".
  • Luciano Ramalho. "Fluent Python" - Control Flow and Coroutines.
  • PEP 342. "Coroutines via Enhanced Generators".
  • Real Python Tutorials. "Advanced Python Generators".
728x90