FEInterview Prep

javascript · high priority

이터레이터 · 제너레이터 · 비동기 이터레이터

for...of / yield / for await...of — 순회 가능한 모든 것의 한 가지 계약

intermediate 난이도4시간토스카카오네이버배민
시작 전
이해도
매우 낮음

학습 개요

탄생 배경

쉬운 설명

복잡한 개념을 실생활 비유로 설명합니다.

컨베이어 벨트 공장

이터러블은 "여기 컨베이어 벨트가 있습니다" 라는 문서, 이터레이터는 실제로 돌아가는 벨트 자체입니다. 제너레이터는 "벨트에 하나 놓을 때마다 자동으로 스위치가 꺼지는" 편의 기능이고, 비동기 제너레이터는 벨트가 네트워크 너머에서 부품을 가져오는 공장입니다. 소비자(`for...of`, `for await...of`) 는 필요한 만큼만 가져가고, 벨트는 그 때만 돌아갑니다.

핵심 개념

자바스크립트의 이터레이션은 두 개의 프로토콜로 정의됩니다. **Iterable 프로토콜**: 객체에 [Symbol.iterator]() 메서드가 있으면 이터러블이다. **Iterator 프로토콜**: 객체에 next() 메서드가 있고, 호출 시마다 { value, done } 을 반환하면 이터레이터다. for...of, 스프레드(...), 구조분해, Array.from, new Set([...]) 모두 내부적으로 이 계약에 의존합니다.

이터러블 직접 구현 — for...of 가 어디서 next 를 부르는지 보여줌typescript
1// 0..n-1 을 순회하는 이터러블
2function range(n: number): Iterable<number> {
3 return {
4 [Symbol.iterator]() {
5 let i = 0;
6 return {
7 next(): IteratorResult<number> {
8 return i < n
9 ? { value: i++, done: false }
10 : { value: undefined, done: true };
11 },
12 };
13 },
14 };
15}
16
17// for...of 는 내부적으로 [Symbol.iterator]() 를 호출
18for (const v of range(3)) console.log(v); // 0, 1, 2
19
20// 스프레드, Array.from, new Set, 구조분해 — 모두 같은 프로토콜
21console.log([...range(3)]); // [0, 1, 2]
22console.log(Array.from(range(3))); // [0, 1, 2]
23const [a, b] = range(5); // a=0, b=1

Iterable

[Symbol.iterator]() 메서드를 가진 객체. 이 메서드는 호출 시 Iterator 를 반환해야 한다.

Array, Map, Set, String, NodeList, generator 함수의 결과

Iterator

next() 메서드를 가진 객체. next(){ value, done: boolean } 을 반환한다.

IterableIterator

이터레이터 자신이 [Symbol.iterator]() 도 가지고 있어서, 호출 시 자기 자신을 반환하는 것. generator 결과가 이 형태라 "한 번 만들고 여러 자리에 그대로 전달" 가능.

이터레이터 메서드

return(value)throw(error) 는 선택적 메서드. for...of 가 break 되면 return() 이 호출되어 정리 기회를 가진다 — 제너레이터의 finally 가 실행되는 이유.

일반 객체는 이터러블이 아니다

String, Array, TypedArray, Map, Set, NodeList(브라우저), arguments 는 이미 Symbol.iterator 를 구현하고 있습니다. 하지만 일반 객체(plain object)는 **아닙니다** — for (const x of {a:1}) 는 즉시 TypeError. 객체를 순회하려면 Object.entries(obj) 처럼 이터러블을 반환하는 헬퍼로 감싸야 합니다.

실무 적용

어떤 상황에서 사용하는가

페이지네이션 API 에서 조건을 만족하는 사용자를 처음 만난 시점에 즉시 중단하고 싶다 + LLM 응답을 React 컴포넌트에서 실시간으로 보여주고 싶다.

어떻게 적용하는가

`async function*` 로 `fetchAllUsers()` 를 작성하고 cursor 기반 yield → 호출부는 `for await...of` 로 순회하다가 조건 만족 시 break. break 이후 다음 페이지 요청 안 일어남. LLM 응답은 `streamTokens(prompt, signal)` 같은 async generator 로 감싸고, useEffect 의 cleanup 에서 `controller.abort()` → 제너레이터 내부 `try/finally` 의 finally 가 `reader.releaseLock()` 으로 마무리.

흔한 실수와 안티패턴

  • 일반 객체(plain object)는 이터러블이 아니므로 `for...of` 에 바로 넣으면 TypeError — `Object.entries()` 로 감싸야 함
  • `async function*` 의 `return value` 는 `for await...of` 에서 받을 수 없음 — 마지막 값은 yield 해야 순회됨
  • Generator 는 일회용 — 한 번 소진하면 재시작 불가, 다시 순회하려면 다시 호출해 새 Generator 생성
  • AbortController 없이 긴 스트림을 `for await` 로 순회하면 컴포넌트 unmount 이후에도 요청이 계속됨 → 메모리 누수
  • `for...in` 으로 배열 순회 — 인덱스 외 enumerable 프로퍼티까지 돌아 의도와 다름. 배열은 항상 `for...of` 또는 `forEach`

면접 질문

중급토스카카오배민

답변 방향 힌트

이터러블 프로토콜의 계약이 무엇인지, 배열과 일반 객체에서 그 계약이 어떻게 다른지.

반드시 언급할 키워드

  • `for...of` 는 내부적으로 `[Symbol.iterator]()` 를 호출
  • 배열은 `Array.prototype[Symbol.iterator]` 가 정의되어 있음
  • 일반 객체는 `Symbol.iterator` 가 없어 TypeError
  • 객체는 `Object.entries/keys/values` 로 이터러블을 얻을 수 있음
  • 반면 `for...in` 은 프로토타입의 enumerable string key 를 순회 — 전혀 다른 메커니즘

예상 꼬리 질문

  • 스프레드 연산자 `[...obj]` 는 내부적으로 무엇을 호출하나요?
  • Map 을 `for...of` 로 순회하면 왜 `[key, value]` 쌍이 나오나요?

자기 점검

스크롤 올리지 말고 답해보세요. Iterable 과 Iterator 의 차이는?

기대 키워드

Symbol.iteratornextvaluedone계약/프로토콜

자주 하는 오해

Iterable 과 Iterator 를 같은 것으로 섞어 말하는 경우가 많습니다. Iterable 은 "이터레이터를 만들 수 있는 것(`Symbol.iterator` 보유)", Iterator 는 "실제로 값을 하나씩 뽑아내는 것(`next` 보유)" 으로 구분해야 합니다.

`for...of` 와 `for...in` 의 차이를 배열 순회 관점에서 설명해보세요.

기대 키워드

for...of이터러블for...inenumerable 키프로토타입

자주 하는 오해

`for...in` 을 배열 순회용으로 써도 된다고 착각하기 쉽습니다. 실제로는 인덱스 외의 커스텀 프로퍼티나 프로토타입의 enumerable 속성도 돌기 때문에 배열 순회는 반드시 `for...of` 또는 `forEach` 를 써야 합니다.

`async function*` 안에서 `try/finally` 가 왜 중요한지 한 문장으로 설명해보세요.

기대 키워드

breakthrowreleaseLock리소스 정리cleanup

자주 하는 오해

"정상 종료 시에만 finally 가 실행된다" 고 오해하는 경우가 있지만, 실제로는 break/throw/AbortError 등 모든 종료 경로에서 finally 가 실행되므로 스트림 reader 해제, WebSocket close 같은 리소스 정리를 넣기에 최적의 위치입니다.

학습 자료