일반적인 함수 호출 방식을 서브 루틴(Subroutine) 이라고 합니다. 메인 루틴이 서브 루틴을 호출하면 서브 루틴의 코드를 실행한 뒤 메인 루틴으로 돌아오며, 서브 루틴이 끝나면 그 안의 내용은 모두 사라집니다. 즉, 메인 루틴에 종속된 관계입니다.
반면 코루틴(Coroutine) 은 Cooperative Routine, 즉 서로 협력하는 루틴을 의미합니다. 메인과 서브가 종속된 관계가 아니라 대등한 관계로, 특정 시점에 상대방의 코드를 실행하며 서로 값을 주고받습니다.
| 구분 | 서브 루틴 | 코루틴 |
|---|---|---|
| 관계 | 메인에 종속 | 대등한 관계 |
| 진입점 | 1개 | 여러 개 |
| 실행 횟수 | 한 번 | 여러 번 |
| 상태 유지 | ❌ 종료 시 소멸 | ✅ 중단 상태 유지 |
| 값 전달 | 반환값만 | 양방향 주고받기 |
코루틴은 함수 안에서 (yield) 형식으로 yield를 괄호로 묶어 변수에 저장합니다. 이렇게 하면 send()로 보낸 값을 받아올 수 있습니다.
def my_coroutine():
while True:
value = (yield) # send()로 보낸 값을 받음
print(f"받은 값: {value}")
co = my_coroutine()
next(co) # 코루틴 첫 실행 — yield 지점까지 진행 (초기화)
co.send(10) # 받은 값: 10
co.send(20) # 받은 값: 20
co.send(30) # 받은 값: 30
코루틴을 처음 사용할 때는 반드시 next()를 한 번 호출해서 첫 번째 yield 지점까지 진행시켜야 합니다. 이 과정을 코루틴 초기화(프라이밍, Priming) 라고 합니다.
둘 다 yield를 사용하지만 값을 다루는 방향이 다릅니다.
# 제너레이터: next()를 반복 호출해서 값을 꺼냄
def number_gen():
yield 1
yield 2
yield 3
gen = number_gen()
print(next(gen)) # 1
print(next(gen)) # 2
# 코루틴: next()로 초기화 후 send()로 값을 주고받음
def accumulator():
total = 0
while True:
value = (yield total) # total을 내보내고 value를 받음
total += value
co = accumulator()
print(next(co)) # 0 (초기화)
print(co.send(10)) # 10
print(co.send(20)) # 30
print(co.send(5)) # 35
| 구분 | 제너레이터 | 코루틴 |
|---|---|---|
| 값 흐름 | 바깥으로 전달 (단방향) | 양방향 주고받기 |
| 주요 메서드 | next() 반복 호출 | next() 1회 후 send() 사용 |
| 주요 용도 | 데이터 생성, 순회 | 비동기 작업, 데이터 처리 파이프라인 |
close()를 호출하면 코루틴이 종료되면서 GeneratorExit 예외가 발생합니다. 필요하다면 try/finally로 종료 시 정리 작업을 수행할 수 있습니다.
def my_coroutine():
try:
while True:
value = (yield)
print(f"받은 값: {value}")
except GeneratorExit:
print("코루틴이 종료됩니다.") # 종료 시 정리 작업
co = my_coroutine()
next(co)
co.send(1) # 받은 값: 1
co.close() # 코루틴이 종료됩니다.
throw()는 코루틴 안에 예외를 던집니다. 말 그대로 "예외를 던진다"는 의미입니다.
# 코루틴객체.throw(예외이름, 에러메시지)
def my_coroutine():
while True:
try:
value = (yield)
print(f"받은 값: {value}")
except ValueError as e:
print(f"예외 처리: {e}")
co = my_coroutine()
next(co)
co.send(10) # 받은 값: 10
co.throw(ValueError, "잘못된 값!") # 예외 처리: 잘못된 값!
co.send(20) # 받은 값: 20
yield from에 코루틴을 지정하면 해당 코루틴이 return으로 반환한 값을 가져올 수 있습니다. 코루틴도 제너레이터이므로 return을 사용하면 StopIteration이 발생하며, yield from은 이 값을 자동으로 받아옵니다.
def sub_coroutine():
x = (yield)
y = (yield)
return x + y # StopIteration과 함께 반환값 전달
def main_coroutine():
result = yield from sub_coroutine() # 반환값을 자동으로 받아옴
print(f"최종 결과: {result}")
co = main_coroutine()
next(co)
co.send(10)
co.send(20) # 최종 결과: 30