JavaScript에서의 생성기 함수

JavaScript에서의 생성기 함수

이 글에서는 JavaScript의 생성기 함수에 대해 설명하겠습니다.

YouTube Video

JavaScript에서의 생성기 함수

JavaScript 생성기 함수는 **지연 실행(lazy execution)**과 중단 후 재개가 가능한 점에서 일반 함수와 다른 특수한 함수입니다. 생성기 함수에 대해 이해하면 비동기 작업 및 대량 데이터를 보다 효과적으로 처리할 수 있습니다. 여기에서는 생성기 함수의 작동 방식, 사용 방법 및 실용적인 사용 사례를 자세히 설명하겠습니다.

생성기 함수란 무엇인가요?

생성기 함수는 **function* (별표가 포함된 함수 정의)**로 정의되며, 일반 함수와 달리 실행을 중간에 중단하고 상태를 유지하면서 다시 재개할 수 있습니다. 생성기 함수는 **"지연 실행"**을 허용하며 결과를 점진적으로 반환하여 효율적인 메모리 관리와 순차 처리에 기여합니다.

문법

1function* generatorFunction() {
2    yield 'First value';
3    yield 'Second value';
4    return 'Done';
5}

이와 같은 방식으로 생성기 함수는 여러 개의 yield 표현식을 포함할 수 있으며, yield를 사용하여 실행을 중단합니다. 생성기 함수가 호출되면 함수 본문이 즉시 실행되지 않고 생성기 객체가 반환됩니다. 이 객체에서 next() 메소드를 호출하여 중단된 지점부터 함수를 다시 실행할 수 있습니다.

생성기 함수의 기본 사용법

다음으로 생성기 함수를 사용하는 기본 예제를 살펴보겠습니다.

 1function* simpleGenerator() {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = simpleGenerator();
 8
 9console.log(gen.next()); // { value: 1, done: false }
10console.log(gen.next()); // { value: 2, done: false }
11console.log(gen.next()); // { value: 3, done: false }
12console.log(gen.next()); // { value: undefined, done: true }

여기서 중요한 점은 생성기가 각 호출 시 yield로 값을 반환하며, done 속성이 false인 한 추가로 반환할 값이 더 있다는 것을 나타냅니다. 마지막으로 next()를 호출하면 done: true가 반환되며, 이는 생성기가 완료되었음을 나타냅니다.

yield 키워드와 값 중단

yield는 생성기 함수 내에서 중단 지점을 나타내는 키워드입니다. yield의 오른쪽에 있는 값은 next()를 호출할 때 반환됩니다. 또한, yield는 양방향 통신을 허용합니다. 즉, next() 메소드의 인수로 값을 전달하면, 해당 값이 생성기 함수로 전달됩니다.

 1function* generatorWithYield() {
 2    const value1 = yield 'First yield';
 3    console.log('Received value:', value1);
 4    const value2 = yield 'Second yield';
 5    console.log('Received value:', value2);
 6}
 7
 8const gen = generatorWithYield();
 9
10console.log(gen.next());        // { value: 'First yield', done: false }
11console.log(gen.next('Apple')); // Received value: Apple
12                                // { value: 'Second yield', done: false }
13console.log(gen.next('Banana'));// Received value: Banana
14                                // { value: undefined, done: true }

이 예제에서 next('Apple') 호출은 값 'Apple'을 생성기 함수로 보내며, 해당 값은 함수 내부에서 사용됩니다.

생성기 상태 관리하기

생성기는 실행 상태를 유지할 수 있어 긴 루프나 순차 처리 작업을 간결하게 표현할 수 있습니다. 다음 예제는 숫자를 무한히 생성하는 제너레이터를 보여줍니다.

 1function* infiniteGenerator() {
 2    let i = 0;
 3    while (true) {
 4        yield i++;
 5        if (i > 10) {
 6            break;
 7        }
 8    }
 9}
10
11const gen = infiniteGenerator();
12
13console.log(gen.next().value); // 0
14console.log(gen.next().value); // 1
15console.log(gen.next().value); // 2
16// Continues...

이 제너레이터는 while(true) 루프를 사용하여 숫자를 지속적으로 생성하며, 필요에 따라 값을 얻을 수 있습니다. 이는 대규모 데이터 세트를 효율적으로 처리할 수 있게 합니다.

제너레이터 함수의 활용 사례

제너레이터 함수는 여러 프로세스를 순차적으로 실행하는 데 적합합니다. 예를 들어, API 요청을 순차적으로 처리하거나 대용량 파일을 나눠 처리하는 데 유용합니다.

1function* apiRequestGenerator() {
2    yield fetch('https://codesparklab.com/json/example1.json');
3    yield fetch('https://codesparklab.com/json/example2.json');
4    yield fetch('https://codesparklab.com/json/example3.json');
5}

따라서 비동기 처리를 지원하는 제너레이터는 효율적인 순차 데이터 처리에 매우 유용합니다.

비동기 제너레이터

ES2018에 도입된 비동기 제너레이터asyncyield를 결합하여 비동기 값을 순차적으로 반환할 수 있습니다. 이를 통해 await와 함께 간결하게 비동기 처리를 작성할 수 있습니다.

문법

 1async function* asyncGenerator() {
 2    yield await Promise.resolve(1);
 3    yield await Promise.resolve(2);
 4    yield await Promise.resolve(3);
 5}
 6
 7const gen = asyncGenerator();
 8
 9(async () => {
10    for await (const value of gen) {
11        console.log(value); // 1, 2, 3
12    }
13})();

비동기 제너레이터는 for await...of 루프를 사용하여 값을 순차적으로 가져올 수 있습니다. 이 패턴은 특히 비동기 데이터 스트림을 처리할 때 유용합니다.

실용적인 예: 제너레이터로 비동기 처리 단순화하기

제너레이터 함수는 비동기 처리 흐름을 단순화하는 데에도 사용됩니다. 예를 들어, 아래와 같이 yieldPromise를 결합하면 비동기 작업을 마치 동기 작업처럼 작성할 수 있습니다.

 1function* asyncFlow() {
 2    const data1 = yield fetch('https://codesparklab.com/json/example1.json');
 3    console.log(data1);
 4    const data2 = yield fetch('https://codesparklab.com/json/example2.json');
 5    console.log(data2);
 6}
 7
 8const gen = asyncFlow();
 9
10function handleAsync(generator) {
11    const next = (promise) => {
12        promise.then((result) => {
13        const { value, done } = generator.next(result);
14        if (!done) {
15            next(value);
16        }
17        });
18    };
19
20    next(generator.next().value);
21}
22
23handleAsync(gen);

이 코드는 제너레이터를 사용하여 API 요청을 순차적으로 수행하고 결과를 처리하고 있습니다.

요약

제너레이터 함수는 자바스크립트의 강력한 기능 중 하나로, 함수 실행을 멈추고 다시 시작할 수 있는 능력을 가지고 있습니다. 이를 통해 순차적 처리, 비동기 처리, 대규모 데이터 세트의 효율적 처리가 가능합니다. 제너레이터를 이해하는 것은 자바스크립트 고급 기술을 익히는 데 중요한 단계입니다.

위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.

YouTube Video