이번 글에서는 자바스크립트의 제너레이터(Generator) 에 대해 마저 알아보겠습니다.
제너레이터 정의
제너레이터 객체는 제너레이터 함수에 의해 반환되며, 이터러블 프로토콜과 이터레이터 프로토콜을 모두 따릅니다.
쉽게 말해, 제너레이터는 반복문에서 사용할 수 있고 동시에 next()를 호출해 값을 하나씩 꺼낼 수도 있는 객체입니다.
제너레이터의 장점
먼저, 제너레이터를 사용하는 이유부터 살펴보겠습니다.
- 메모리 효율성: 필요한 시점에만 값을 생성하므로 메모리를 절약할 수 있습니다.
- Iterable 정의 간소화: Iterable을 간단히 작성할 수 있습니다.
https://goto-helloworld.tistory.com/146 와 비교하면, 왜 간단한지 이해가 가능합니다. - 상태 제어 용이: yield를 통해 실행을 멈췄다가 다시 이어갈 수 있어 흐름 제어가 유연합니다.
def simple_generator():
yield 1
yield 2
yield 3
# generator 객체 생성
gen = simple_generator()
print("Generator를 사용한 반복문:")
for value in gen:
print(value)
제너레이터의 생성과 기본 동작
자바스크립트에는 new Generator라는 생성자가 직접적으로 존재하지 않습니다.
대신 제너레이터 함수를 정의하고 호출하면 Generator 객체가 반환됩니다.
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
위 예시처럼 function*으로 선언한 제너레이터 함수는 호출 시 새로운 Generator 객체를 반환하고,
내부에서 yield 키워드를 통해 값을 하나씩 전달합니다.
제너레이터의 동작 방식
- 제너레이터 함수는 호출 시 Generator 객체를 반환합니다.
- 처음 실행 시 함수 본문 맨 앞에서 대기(suspended) 상태로 시작합니다.
- 여러 번 호출하면 각각 독립적인 실행 컨텍스트를 유지합니다.
호출자와 제어 흐름
- 호출자는 next(), throw(), return()을 통해 제너레이터를 제어합니다.
- 제너레이터 내부에서는 yield, yield*, return, throw 등을 통해 호출자에게 제어권을 넘깁니다.
next() 실행 시
- yield를 만나면 { value: 값, done: false }를 반환하고 멈춥니다.
- yield*를 만나면 다른 이터러블에 제어를 위임합니다.
- return이나 함수 종료 시 { value: 반환값, done: true }를 반환하고 종료합니다.
throw()와 return() 실행 시
- throw(): 제너레이터 내부에서 예외를 던진 것처럼 동작합니다.
- return(): 현재 위치에서 return이 실행된 것처럼 동작하며 종료합니다.
무한 이터레이터 예시
제너레이터는 필요할 때마다 값을 생성하기 때문에 무한한 데이터 구조도 정의할 수 있습니다.
function* infinite() {
let index = 0;
while (true) {
yield index++;
}
}
const generator = infinite();
console.log(generator.next().value); // 0
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
제너레이터 내부 값 전달 예시
function* 제너레이터함수() {
const a = yield 10;
const b = yield a + 1;
return a + b + 100;
}
const 제너레이터객체 = 제너레이터함수();
console.log(제너레이터객체.next()); // { value: 10, done: false }
console.log(제너레이터객체.next(20)); // { value: 21, done: false }
console.log(제너레이터객체.next(30)); // { value: 150, done: true }
위 예시에서는 next()에 전달한 값이 제너레이터 내부 yield 표현식 결과로 들어갑니다. 따라서 첫 번째 yield는 10을 반환하고, 두 번째 yield는 외부에서 전달된 20을 사용해 21을 반환합니다. 마지막으로 30이 b 값으로 들어가 최종적으로 150을 반환하고 종료됩니다.
yield* 예시
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i); // 다른 제너레이터에 제어를 위임
yield i + 10;
}
const gen2 = generator(10);
console.log(gen2.next().value); // 10
console.log(gen2.next().value); // 11
console.log(gen2.next().value); // 12
console.log(gen2.next().value); // 13
console.log(gen2.next().value); // 20
👉 yield*를 사용하면 다른 제너레이터나 배열 같은 이터러블 전체를 위임하여 손쉽게 연결할 수 있습니다.
제너레이터 프로토타입
마지막으로, 제너레이터 객체가 어떤 공통 메서드를 가지는지 정리해보겠습니다.
여기서 말하는 프로토타입(prototype) 은 객체가 다른 객체로부터 속성과 메서드를 상속받는 연결 고리를 의미합니다.
제너레이터 객체는 공통된 Generator.prototype을 참조하여 다음과 같은 메서드를 사용할 수 있습니다.
- next(): yield된 값을 반환하며 실행을 멈추거나 이어갑니다.
- return(): 현재 위치에서 return문을 실행한 것처럼 제너레이터를 종료합니다.
- throw(): 현재 위치에서 예외를 발생시킨 것처럼 동작하며, 내부에서 에러 처리를 할 수 있습니다.
참고자료
'FE > JS' 카테고리의 다른 글
| Generator 이해하기(1), [Iterable, Iterator] (0) | 2025.09.24 |
|---|---|
| 자바스크립트 - 클로저 (0) | 2025.05.19 |
| JavaScript - this (0) | 2025.05.19 |
| 자바스크립트 실행 컨텍스트 (0) | 2025.05.19 |
| 자바스크립트 호이스팅 정리 (0) | 2025.05.18 |