💻 프로그래밍

제네레이터

Generator

yield로 값을 하나씩 생성하는 함수. 메모리 효율적인 이터레이션. Python, JavaScript에서 지원.

📖 상세 설명

제네레이터(Generator)는 yield 키워드를 사용하여 값을 하나씩 생성하는 특별한 함수입니다. 일반 함수처럼 한 번에 모든 결과를 반환하지 않고, 호출될 때마다 다음 값을 생성합니다. 이를 통해 메모리 효율적으로 시퀀스를 처리하고 무한 시퀀스도 표현할 수 있습니다.

제네레이터의 핵심은 실행 상태 유지입니다. yield에서 함수가 일시 정지되고, next()가 호출되면 그 지점부터 다시 실행됩니다. 함수의 지역 변수, 실행 위치 등 모든 상태가 보존됩니다. 이는 코루틴(coroutine)의 기반이 되는 메커니즘입니다.

Python에서 제네레이터는 두 가지 방식으로 만들 수 있습니다. yield를 포함한 함수를 정의하거나, 제네레이터 표현식(x*2 for x in range(10))을 사용합니다. 두 방식 모두 이터레이터 프로토콜을 자동으로 구현하므로 for 루프에서 바로 사용할 수 있습니다.

JavaScript에서는 function* 문법으로 제네레이터를 정의합니다. async/await는 실제로 제네레이터의 문법적 설탕으로 시작되었으며, Redux-Saga 같은 라이브러리가 제네레이터를 활용해 비동기 흐름 제어를 구현합니다. Rust에서는 Iterator 트레이트와 async/await가 유사한 개념을 제공합니다.

💻 코드 예제

# Python 제네레이터 예제

# 기본 제네레이터 함수
def countdown(n):
    while n > 0:
        yield n
        n -= 1
    print("발사!")

for num in countdown(5):
    print(num)  # 5, 4, 3, 2, 1, "발사!"

# 무한 시퀀스 제네레이터
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
print([next(fib) for _ in range(10)])  # [0,1,1,2,3,5,8,13,21,34]

# 제네레이터 표현식
squares = (x**2 for x in range(10))
print(list(squares))  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 파일 처리 (메모리 효율적)
def read_large_file(file_path):
    with open(file_path) as f:
        for line in f:
            yield line.strip()

# send()로 양방향 통신
def accumulator():
    total = 0
    while True:
        value = yield total
        if value is not None:
            total += value

acc = accumulator()
next(acc)       # 시작
print(acc.send(10))  # 10
print(acc.send(20))  # 30

🗣️ 실무 대화 예시

데이터 엔지니어: "DB에서 100만 건을 가져와서 처리하는데 메모리가 부족합니다."

시니어 개발자: "한 번에 다 가져오지 말고 제네레이터로 청크 단위로 처리하면 어떨까요?"

데이터 엔지니어: "커서를 제네레이터로 감싸서 yield 하면 되겠네요."

시니어 개발자: "맞아요. 배치 사이즈를 1000 정도로 설정하고, 각 배치를 yield하면 메모리 사용량이 일정하게 유지됩니다."

면접관: "제네레이터와 리스트 컴프리헨션의 차이점은 무엇인가요?"

지원자: "리스트 컴프리헨션은 모든 요소를 즉시 생성해서 메모리에 저장하고, 제네레이터 표현식은 요청 시에만 값을 생성합니다. 대용량 데이터에서는 제네레이터가 메모리 효율이 훨씬 좋습니다."

면접관: "yield from은 어떤 용도인가요?"

지원자: "다른 제네레이터의 값을 위임할 때 사용합니다. 중첩된 반복문 대신 yield from sub_generator()로 간결하게 표현할 수 있습니다."

리뷰어: "이 함수에서 리스트에 append하고 마지막에 return하고 있는데, 제네레이터로 바꾸면 좋겠어요."

작성자: "return 대신 yield를 쓰면 되나요?"

리뷰어: "네, append 대신 yield하고, return 문은 제거하세요. 그러면 중간 리스트 없이 스트리밍 방식으로 처리됩니다."

작성자: "단점은 없나요?"

리뷰어: "한 번만 순회할 수 있다는 점이요. 여러 번 순회해야 하면 list()로 변환하거나 함수를 다시 호출해야 합니다."

⚠️ 주의사항

🔗 관련 용어

📚 더 배우기