요즘IT
위시켓
AIDP
콘텐츠프로덕트 밸리
요즘 작가들컬렉션물어봐
놀이터
콘텐츠
프로덕트 밸리
요즘 작가들
컬렉션
물어봐
놀이터
새로 나온
인기
개발
AI
IT서비스
기획
디자인
비즈니스
프로덕트
커리어
트렌드
스타트업
서비스 전체보기
위시켓요즘ITAIDP
고객 문의
02-6925-4867
10:00-18:00주말·공휴일 제외
yozm_help@wishket.com
요즘IT
요즘IT 소개작가 지원
기타 문의
콘텐츠 제안하기광고 상품 보기
요즘IT 슬랙봇크롬 확장 프로그램
이용약관
개인정보 처리방침
청소년보호정책
㈜위시켓
대표이사 : 박우범
서울특별시 강남구 테헤란로 211 3층 ㈜위시켓
사업자등록번호 : 209-81-57303
통신판매업신고 : 제2018-서울강남-02337 호
직업정보제공사업 신고번호 : J1200020180019
제호 : 요즘IT
발행인 : 박우범
편집인 : 노희선
청소년보호책임자 : 박우범
인터넷신문등록번호 : 서울,아54129
등록일 : 2022년 01월 23일
발행일 : 2021년 01월 10일
© 2013 Wishket Corp.
로그인
요즘IT 소개
콘텐츠 제안하기
광고 상품 보기
개발

자바스크립트에서 이터러블·이터레이터는 왜 필요할까?

효빈
9분
2시간 전
160
에디터가 직접 고른 실무 인사이트 매주 목요일에 만나요.
newsletter_profile0명 뉴스레터 구독 중

자바스크립트에서 순회 가능한 데이터 구조는 생각보다 훨씬 다양합니다. 배열과 문자열은 물론이고, Set과 Map 같은 컬렉션 타입도 자연스럽게 for…of 문으로 순회할 수 있고, 전개 연산자나 구조 분해 할당의 대상이 되기도 합니다. 우리는 이러한 문법을 매일 같이 사용하면서도, 어떤 객체는 반복이 되고 어떤 객체는 되지 않는지에 대한 기준이 무엇인지, 그리고 그 기준이 언어 차원에서 어떻게 정의되어 있는지는 깊이 생각하지 않는 경우가 많습니다.

 

이 순회 가능한 구조 뒤에는 이터러블과 이터레이터라는 특별한 프로토콜이 존재합니다. 이터러블 프로토콜과 이터레이터 프로토콜은 ECMAScript 명세에서 정의한 규약으로, 자바스크립트 엔진이 반복을 처리하는 방식을 표준화합니다. 이 글에서는 먼저 이터러블과 이터레이터가 무엇인지 개념을 정리하고, 자바스크립트에서 기본적으로 제공하는 이터러블 객체들을 살펴본 뒤, 직접 이터러블을 구현하는 방법과 제너레이터와의 관계까지 차근차근 정리해 보겠습니다.

 

이 글의 핵심 요약

  • 자바스크립트의 반복(for…of, 전개 연산자, 구조 분해)은 ECMAScript가 정한 이터러블/이터레이터 프로토콜로 표준화되어 있다.
  • 이터러블은 Symbol.iterator로 이터레이터를 제공하고, 이터레이터는 next()로 { value, done }를 반환하며 순회를 진행한다.
  • 제너레이터(function, yield)는 이 프로토콜 구현을 단순화해, 트리 순회나 무한 시퀀스 같은 복잡한 반복도 간결하게 작성하게 해준다. 
 

이터러블과 이터레이터란?

이터러블과 이터레이터는 자바스크립트의 순회 프로토콜을 구성하는 두 개의 축입니다. 이터러블은 “반복을 시작할 수 있는 주체”이고, 이터레이터는 “반복을 실제로 진행하는 실행자”라고 볼 수 있습니다. 이 둘은 서로 분리된 역할을 가지고 있지만, 프로토콜을 통해 느슨하게 연결되어 있기 때문에 다양한 종류의 객체가 동일한 순회 문법을 사용할 수 있게 됩니다.

 

1) 이터러블 프로토콜의 개념

이터러블은 ECMAScript 명세에서 정의한 “반복 가능한 객체”를 의미합니다. 어떤 객체가 이터러블이 되려면 Symbol.iterator라는 특수한 키를 가진 메서드를 가지고 있어야 합니다. 이 메서드는 호출되었을 때 이터레이터 객체를 반환해야 하며, 이 규칙을 만족할 경우 자바스크립트 엔진은 해당 객체를 for…of 문, 전개 연산자, 구조 분해 할당 등의 순회 대상으로 인정합니다.

 

자바스크립트 엔진이 for…of 문을 만났을 때 하는 일은 매우 단순합니다. 먼저 반복 대상이 되는 객체에 obj[Symbol.iterator]가 존재하는지 확인하고, 그것이 함수라면 호출해 이터레이터를 얻습니다. 만약 이 메서드가 없다면 그 객체는 이터러블이 아니라고 판단하고 에러를 발생시킵니다. 이처럼 이터러블 프로토콜은 “순회를 시작할 수 있는 최소한의 조건”을 정의하며, 어떤 값을 어떤 순서로 내보낼지는 각 이터러블이 자유롭게 설계할 수 있습니다.

 

<출처: https://inpa.tistory.com/>

 

(참고) 위의 그림에서는 range가 Symbol.iterator 메서드를 가지고 있기 때문에 이터러블 객체이고, Symbol.iterator() 메서드에서 리턴한 객체에 {value:값, done: true/false}를 리턴하는 next() 메서드가 존재하기 때문에, 노란색 박스가 둘러진 객체가 바로 이터레이터 객체입니다. 

 

이 내용은 아래 ‘이터레이터의 구조와 역할’에서 자세하게 다루도록 하겠습니다.

 

2) 이터레이터의 구조와 역할

이터레이터는 실제로 순회를 진행하는 객체입니다. 이터레이터 객체는 반드시 next()라는 메서드를 가져야 하고, 이 메서드는 호출될 때마다 { value, done } 형태의 객체를 반환해야 합니다. 여기서 value는 현재 단계에서 꺼낸 값이고, done은 순회가 끝났는지를 나타내는 불리언 값입니다.

 

<출처 : https://inpa.tistory.com/>

 

배열은 예로 들면, 배열은 이터러블이기 때문에 arr[Symbol.iterator]()를 호출하면 배열 요소를 순차적으로 반환하는 이터레이터를 얻을 수 있습니다.

 

const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

 

이 코드를 조금 더 자세히 살펴보면, 첫 번째 next() 호출에서는 배열의 첫 번째 요소인 1과 함께 done: false가 반환됩니다. 두 번째 호출에서는 2, 세 번째 호출에서는 3이 동일한 형식으로 반환되죠. 이때 이터레이터 내부에는 “현재 몇 번째 요소까지 소비했는지”에 대한 상태가 숨겨져 있고, next()가 호출될 때마다 이 상태가 한 단계씩 앞으로 진행됩니다. 네 번째 호출에서는 더 이상 반환할 값이 없기 때문에 value는 undefined가 되고, done은 true가 되어 순회가 종료되었음을 알려줍니다.

 

자바스크립트의 for…of 문은 겉으로는 이런 복잡한 과정을 숨기고 있지만, 내부적으로는 위와 똑같이 next()를 계속 호출하면서 done이 true가 되는 시점까지 값들을 하나씩 꺼내오는 방식으로 동작합니다. 이 구조를 이해하면, 단순히 문법을 사용하는 수준을 넘어 실제로 엔진이 어떻게 순회를 수행하는지까지 명확하게 이해할 수 있습니다.

 

 

자바스크립트의 기본 이터러블 예시

이터러블과 이터레이터 프로토콜은 추상적인 개념 같지만, 사실 우리는 이미 매일 이 개념을 활용하고 있습니다. 자바스크립트의 다양한 내장 객체가 이터러블을 구현하고 있기 때문입니다. 이 섹션에서는 어떤 객체들이 기본적으로 이터러블인지, 그리고 우리가 자주 사용하는 문법이 이터러블과 어떤 관계를 가지는지 살펴보겠습니다.

 

1) 배열, 문자열, Set, Map

가장 대표적인 이터러블은 배열과 문자열입니다. 배열은 각 요소를 순서대로 반환하는 이터레이터를 제공하고, 문자열은 한 글자씩 순회할 수 있는 이터레이터를 제공합니다. 그래서 다음과 같은 코드가 자연스럽게 동작합니다.

 

for (const char of "hello") {
  console.log(char);
}

const numbers = [10, 20, 30];
for (const n of numbers) {
  console.log(n);
}

 

Set과 Map도 이터러블입니다. Set은 중복되지 않는 값들의 집합을 표현하며, 이터레이터를 통해 각 값을 순회할 수 있습니다. Map은 키와 값의 쌍을 저장하는 자료구조이고, 이터레이터는 [key, value] 형태의 배열을 순차적으로 반환합니다.

 

const set = new Set([1, 2, 2, 3]);
for (const v of set) {
  console.log(v); // 1, 2, 3
}

const map = new Map([
  ["a", 1],
  ["b", 2]
]);
for (const [key, value] of map) {
  console}

 

이처럼 서로 다른 자료구조들이 같은 for…of 문법으로 순회될 수 있는 이유는, 모두 이터러블 프로토콜을 충족하고 있기 때문입니다. 

 

2) for…of, spread, 구조 분해 등과의 관계

for…of 문만 이터러블과 관련된 것은 아닙니다. 전개 연산자와 구조 분해 할당 역시 이터러블을 대상으로 동작합니다. 전개 연산자는 이터러블에서 값을 하나씩 꺼내 새로운 배열이나 인수 목록을 만드는 문법입니다.

 

const arr = [1, 2, 3];
const copy = [...arr];

const str = "hi";
const chars = [...str]; // ['h', 'i']

const [first, second] = "OK";

 

이러한 문법은 모두 내부적으로 Symbol.iterator를 호출하고, 얻은 이터레이터의 next()를 반복해서 호출하면서 값을 수집합니다. 따라서 이터러블이 아닌 일반 객체에 전개 연산자를 사용하면 순회할 수 없기 때문에 오류가 발생합니다. 이점에서 이터러블 프로토콜은 단지 반복문에만 관련된 것이 아니라, 여러 문법 요소들을 묶어주는 공통 기반이라고 볼 수 있습니다.

 

 

직접 이터러블 객체 만들기

이터러블을 단순히 “내장 컬렉션이 가진 성질”로만 이해하면 그 힘을 온전히 활용하지 못하게 됩니다. 이터러블 프로토콜은 개발자가 직접 구현할 수 있으며, 이를 통해 자신만의 순회 가능한 객체를 만들 수 있습니다. 이것이 가능해지는 순간, 반복 로직을 추상화하고 재사용하는 새로운 설계 방식이 열리게 됩니다.

 

1) Symbol.iterator 직접 구현

직접 이터러블 객체를 만드는 기본적인 방법은 객체에 Symbol.iterator 메서드를 정의하는 것입니다. 이 메서드 안에서 이터레이터를 반환하면, 그 객체는 즉시 for…of의 대상이 될 수 있습니다. 예를 들어 from부터 to까지의 숫자를 순회하는 range 객체를 만들어보겠습니다.

 

const range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        if (current <= last) {
          return { value: current++, done: false };
        }
        return { value: undefined,       }
    };
  }
};

}

 

이처럼 Symbol.iterator 안에서 이터레이터를 반환하고, 이터레이터에서 next() 메서드로 값을 하나씩 제공하면 어떤 형태의 객체든 순회 가능한 이터러블로 만들 수 있습니다.

 

2) 사용자 정의 이터레이터 구조

직접 이터레이터를 구현할 때의 장점은, 순회 규칙을 완전히 마음대로 설계할 수 있다는 점입니다. 예를 들어 짝수만 반환하는 이터러블, 특정 조건을 만족하는 요소만 골라내는 이터러블, 외부 API 호출 결과를 스트리밍처럼 순회하는 이터러블 등 다양한 패턴을 만들 수 있습니다. 이때 중요한 것은 “현재 어디까지 순회했는지”를 저장하는 내부 상태를 잘 설계하고, next()가 호출될 때 그 상태를 기반으로 value와 done을 적절히 반환하는 것입니다. 이런 방식으로 사용자 정의 이터레이터를 구현하면 단순한 반복문보다 훨씬 유연하고 재사용 가능한 순회 구조를 설계할 수 있습니다.

 

 

제너레이터와 이터러블

직접 이터러블과 이터레이터를 구현하는 방식은 개념 이해에는 좋지만, 코드가 다소 장황해지는 단점이 있습니다. 자바스크립트는 이러한 반복 패턴을 더 간결하게 작성할 수 있도록 제너레이터(generator)라는 문법을 제공합니다. 제너레이터는 이터러블과 이터레이터를 동시에 구현하는 특별한 함수입니다.

 

1) 제너레이터는 이터러블과 이터레이터를 동시에 구현

제너레이터 함수는 function* 키워드로 정의하며, 내부에서 yield 키워드를 사용해 순차적으로 값을 반환합니다. 제너레이터 함수를 호출하면 이터레이터 객체가 반환되는데, 이 객체는 동시에 이터러블이기도 해서 for…of 문에 바로 사용할 수 있습니다.

 

function* genNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

for (const n of genNumbers()) {
  console.log(n); // 1, 2, 3
}

 

이 코드에서는 getNumbers()를 호출하는 것만으로 이터러블이자 이터레이터인 객체를 얻을 수 있고, for…of문은 이터레이터의 next()를 자동으로 호출하면서 각 yield 지점에서 반환된 값을 순회합니다. 제너레이터는 우리가 직접 Symbol.iterator와 next()를 구현하지 않아도 되도록 추상화를 제공하는 셈입니다.

 

2) 실전 예시: 제너레이터로 순회형 데이터 구조 만들기

제너레이터는 복잡한 데이터 구조를 순회할 때 그 진가가 드러납니다. 특히 트리 구조처럼 중첩된 노드를 가진 데이터는 기존 반복문으로 처리하면 코드가 복잡해지기 쉬운데, 제너레이터를 사용하면 이를 선언적으로 표현할 수 있습니다.

 

예를 들어, 각 노드가 자식들을 배열로 가지고 있는 트리 구조를 생각해 보겠습니다. 이 트리를 깊이 우선 탐색 방식으로 순회하는 이터러블을 제너레이터로 구현하면 다음과 같이 작성할 수 있습니다.

 

class TreeNode {
  constructor(value, children = []) {
    this.value = value;
    this.children = children;
  }

  *[Symbol.iterator]() {
    yield this.value;
    for (const child of this.children) {
      yield* child;
    }
  }
}

const tree = new TreeNode(1, [
  new TreeNode(2]);

}

 

여기서 *[Symbol.iterator]() 안의 yield* child 구문은 해당 자식 노드의 이터러블을 이어서 모두 순회하라는 의미입니다. 이 덕분에 재귀적 구조를 아주 간결한 코드로 표현할 수 있습니다. 실무에서는 이러한 패턴을 이용해 메뉴 트리, DOM트리, 카테고리 계층 구조 등을 순회형 API로 노출할 수 있고, 그 위에 필터링, 매핑, 페이징 등 다양한 고수준 기능을 쌓아 올릴 수 있습니다.

 

또한 제너레이터는 무한 시퀀스나 지연 평가가 필요한 경우에도 유용합니다. 예를 들어, 특정 규칙에 따라 끝없이 숫자를 생성하는 이터러블을 만들고, 실제로는 필요할 때마다 일부만 소비하는 구조를 구현할 수 있습니다. 이렇게 하면 모든 값을 한 번에 메모리에 올리지 않고도 논리적으로 “무한한” 데이터를 다루는 것이 가능해집니다.

 

 

마치며

자바스크립트의 순회 가능한 구조는 단순히 for 문으로 반복하는 것 이상의 의미를 담고 있습니다. 이터러블과 이터레이터 프로토콜은 언어 내부에 정의된 명확한 규약이며, 이 규약을 통해 배열, 문자열, Set, Map, 제너레이터 등 서로 다른 형태의 구조들이 동일한 문법으로 순회될 수 있습니다.

 

이 개념을 이해하면 단지 “되니까 쓴다”라는 수준을 넘어, 자바스크립트가 데이터를 어떻게 소비하고 흐름을 어떻게 제어하는지를 근본적으로 이해할 수 있게 됩니다. 또한 직접 이터러블을 구현하고 제너레이터를 활용해 자신만의 순회 규칙을 정의함으로써, 더 선언적이고 재사용 가능한 코드를 작성할 수 있습니다. 프론트엔드 개발자로서 이터러블과 이터레이터 프로토콜을 이해하는 것은 코드의 가독성과 유지보수성을 높이고, 복잡한 데이터 흐름을 다루는 능력을 한 단계 끌어올려 줄 것입니다.

 

함께 생각해 볼 질문(Q&A)

Q. 자바스크립트에서 for...of는 아무 객체나 돌 수 있나요?

A. 기본 객체(plain object)는 이터러블이 아니라서 불가능합니다. 다만 객체가 Symbol.iterator를 구현해 이터레이터를 반환하도록 만들면 for...of로 순회할 수 있습니다.

 

Q. 전개 연산자(…)와 구조 분해도 이터러블이랑 관련 있나요?
A. 네. 둘 다 내부적으로 Symbol.iterator를 호출해 값을 꺼내는 방식이라, 이터러블이 아니면 동작하지 않습니다. (여기서 말하는 구조 분해는 배열 구조 분해 기준입니다.)

 

Q. 이터러블을 직접 구현하면 얻는 가장 큰 장점은 무엇인가요?

A. “1~5 범위(range)”, “조건 만족 값만”, “API 결과를 스트리밍처럼” 등 원하는 순회 규칙을 직접 설계하고, 이를 for...of 같은 표준 문법으로 재사용할 수 있다는 점입니다.

 

©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

.log(key, value);
done
:
true
};
for
(
const
n
of
range) {
console
.log(n);
// 1, 2, 3, 4, 5
),
new
TreeNode(
3
, [
new
TreeNode(
4
),
new
TreeNode(
5
)])
for
(
const
v
of
tree) {
console
.log(v);
// 1, 2, 3, 4, 5