
파이썬의 비동기 프로그래밍이 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가지 핵심 팁
코루틴을 실무에서 다룰 때 흔히 저지르는 실수를 방지하기 위한 전문적인 조언입니다.
- 자동 프라이밍(Automatic Priming):
send()를 호출하기 전에 항상next()를 호출하여 첫 번째yield까지 코루틴을 진행시켜야 합니다. 이를 자동화하기 위해 데코레이터(Decorator) 패턴을 활용하는 것이 효율적입니다. - 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".
'Artificial Intelligence > 60. Python' 카테고리의 다른 글
| [PYTHON] 입문자가 마주치는 3가지 필수 예외(IndexError, KeyError, ValueError)의 의미와 해결 방법 (0) | 2026.03.08 |
|---|---|
| [PYTHON] 패키지 구조화의 핵심 __init__.py 파일의 3가지 역할과 버전 별 차이 해결 방법 (0) | 2026.03.08 |
| [PYTHON] 비동기 스트리밍 데이터 처리 시 백프레셔(Backpressure) 해결을 위한 3가지 관리 방법 (0) | 2026.03.08 |
| [PYTHON] Pytest 픽스처 Scope 관리를 위한 4가지 핵심 전략과 성능 차이 해결 방법 (0) | 2026.03.07 |
| [PYTHON] Django ORM vs SQLAlchemy 성능 및 5가지 기능적 차이 해결 방법 심화 분석 (0) | 2026.03.07 |