I code, therefore I exist.

웹 프론트 엔드 개발을 공부하고 있는 Ocean이라고 합니다. 만나서 반갑습니다.

WEB/JAVASCRIPT

이터러블(Iterable)과 이터레이터(Iterator)

Ocean 2025. 5. 2. 18:30

들어가며..

 

안녕하세요, Ocean입니다.

 

오늘은 평소 잘 모르던 개념인

자바스크립트의 이터러블(Iterable)이터레이터(Iterator)를 확실하게 정리해 보겠습니다.

 

참고 글

코어 자바스크립트 - iterable 객체


1. 이터러블이란?

이터러블(iterable, 반복 가능한) 객체는, 이름 그대로 순회 가능한 객체를 말합니다.

데이터를 순회하는 일은 매우 흔한 작업이며, 우리가 잘 알고 있는 배열은 순회하기에 적합한 구조를 가지고 있습니다.

 

자바스크립트에서는 어떤 객체가 특정한 조건을 가지면 배열이 아니더라도 배열처럼 순회할 수 있습니다.

이 때, 이 특정한 조건을 공통된 인터페이스로 추상화하여 객체에 적용하면 해당 객체를 이터러블 객체라고 부를 수 있게 됩니다.

 

이터러블 객체의 조건

  1. `Symbol.iterator`라는 메서드를 가지고 있어야 합니다.
  2. `Symbol.iterator`이터레이터 객체를 반환해야 합니다.
    1. 이터레이터 객체는 next 메서드를 가지고 있어야 하며, 이 메서드는 { done: Boolean, value: any } 형식의 객체를 반환해야 합니다.

이 조건을 모두 만족하면 해당 객체는 이터러블, 즉 반복 가능한 객체가 됩니다.

자바스크립트에서 이터러블 객체는 for..of문으로 순회가 가능하며 전개 연산자를 사용할 수 있게 됩니다.

 

정리하자면,

이터러블: [Symbol.iterator]라는 메서드를 가지고 있는 객체, 이 때 Symbol.iterator는 반드시 이터레이터를 반환해야 한다.

이터레이터: next 메서드를 포함하고 있는 객체, next 메서드는  { done: Boolean, value: any }를 반드시 반환값으로 가져야 한다.

 


2. 이터러블 구현

실제 구현 코드

let iAmIterableObj = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    this.current = this.from;
    return this;
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

for (let num of iAmIterableObj) {
  alert(num); // 1, then 2, 3, 4, 5
}


위의 iAmIterableObj 객체는 이터러블 객체입니다.

  1. [Symbol.iterator] 메서드를 가지고 있습니다. [Symbol.iterator]는 자기 자신(this)를 반환합니다.
  2. 자기 자신, 즉 this 내부에는 next 메서드를 가지고 있습니다.
  3. next 메서드는 { done: Boolean, value: any }을 반환합니다.

이터러블의 조건을 모두 만족하는 객체입니다. iAmIterableObj는 for..of전개 연산자를 모두 사용할 수 있습니다!


하지만 위 객체는 조금 독특한 특징이 있습니다.

 

이터러블은 이터레이터 메서드를 [Symbol.iterator]라는 키 값으로 가지고 있는 것을 말합니다.

또한 이터레이터 메서드는 next 메서드를 포함하고 있습니다.

위의 iAmIterableObj 객체는 [Symbol.iterator]라는 키 값으로 함수를 가지고 있으면서 next 메서드 또한 가지고 있습니다.

즉 iAmIterableObj은 이터러블하면서 이터레이터입니다.

 

이러한 경우에, 동시에 여러 개의 for..of를 실행하면 상태가 공유됩니다.

for (let num of iAmIterableObj) {
  console.log("first", num);
  break; // current가 2가 됨
}

for (let num of iAmIterableObj) {
  console.log("second", num); // 2부터 시작하게 됨
}
  • iAmIterableObj 객체는 이터러블인 동시에 이터레이터 객체 그 자체
  • next()의 상태 current는 iAmIterableObj 객체 내부에 직접 저장
  • 한 번 루프를 돌고 break 하더라도 current++는 이미 실행되어 값이 2가 됨
  • for..of를 다시 돌려도 새로운 상태를 만들지 않고, 이미 실행된 이터레이터의 current 값을 가져와서 이터레이터가 초기화되지 않음.

이 문제는 이터러블 객체와 이터레이터를 분리하면 해결할 수 있습니다.

이터러블과 이터레이터를 분리한 코드

let iAmIterable = {
  from: 1,
  to: 5
};

iAmIterable[Symbol.iterator] = function() {
  return {
    current: this.from,
    last: this.to,

    next() {
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  alert(num); // 1, then 2, 3, 4, 5
}
  1. iAmIterable 객체는 [Symbol.iterator] 메서드를 가지고 있음.
  2. 이 메서드는 current, last, next()를 포함한 이터레이터 객체를 반환
  3. for..of를 만날 때 마다 [Symbol.iterator]를 호출하고 호출될 때 마다 새로운 이터레이터 객체를 생성합니다. 따라서 for..of를 여러 번 호출해도 서로 영향을 주지 않습니다.

위 개념은 관심사의 분리, 즉 SOC와 연관이 있습니다.

 

iAmIterable 내부에는 next 메서드가 없고, 대신 iAmIterable[Symbol.iterator]를 호출해서 만든 이터레이터 객체와 해당 객체의 메서드의 next 메서드로 반복에 사용될 값을 만들어 냅니다. 

이렇게 하면 이터레이터 객체와 반복 대상인 객체를 분리할 수 있습니다.

위와 같이 이터러블은 순회 대상인 객체와 순회 로직을 수행하는 이터레이터 객체를 구분함으로써, 각자의 역할을 분리할 수 있습니다.


 3. 이터러블의 종류

이터러블의 종류는 다음과 같습니다.

타입 설명
Array 배열은 대표적인 이터러블 객체
String 문자열은 문자 단위로 순회가 가능한 이터러블 객체
Map 키-값 쌍을 순회할 수 있다
Set 중복 없는 값들의 집합, 각 값 순회 가능
TypedArray(ex: Uint8Array, Float32Array) 이진 데이터 배열도 이터러블
arguments 객체 함수 내의 유사 배열이지만 이터러블합니다.
DOM 컬렉션(ex: NodeList) 브라우저에서 반환되는 일부 DOM 컬렉션도 이터러블
Generator 이터레이터이자 이터러블

 

이 중 대표적인 문자열 이터레이터를 명시적으로 호출해보겠습니다.

let str = "Hello";

let iterator = str[Symbol.iterator]();

while (true) {
  let result = iterator.next();
  if (result.done) break;
  alert(result.value); // 글자가 하나씩 출력됩니다.
}

 

또한 이터러블인지 확인하려면 다음과 같은 코드를 사용해볼 수 있습니다.

function isIterable(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

isIterable([]);           // true
isIterable("hello");      // true
isIterable(new Map());    // true
isIterable({});           // false

4. 이터러블 객체와 유사 배열 객체

이터러블과 비슷해 보이지만 다른 개념인 유사 배열 객체라는 개념이 있습니다.

 

유사 배열 객체는 다음과 같은 특징을 가집니다.

  1. 배열처럼 인덱스를 가지고 있다.
  2. 배열처럼 length 프로퍼티를 가지고 있다.
let arrayLike = { // 인덱스와 length프로퍼티가 있음 => 유사 배열 객체
  0: "Hello",
  1: "World",
  length: 2
};

for (let i = 0; i < arrayLike.length; i++){
	console.log(arrayLike[i]);
}

for (let item of arrayLike) {} // 유사 배열은 Symbol.iterator가 없으므로 에러 발생

 

특별한 기능은 없고, 위와 같이 length와 index를 통해 배열처럼 사용할 수 있는 것을 유사 배열 객체라고 합니다.

유사 배열 객체는 말 그대로 배열과 유사한 객체이기 때문에 배열처럼 push, pop 메서드는 사용할 수 없습니다.

 


5. Array.from

범용 메서드인 Array.from은 이터러블 객체 혹은 유사 배열 객체를 받아 진짜 Array 형태로 만들어 줍니다.

// 유사 배열 객체
let arrayLike = {
  0: "Hello",
  1: "World",
  length: 2
};

let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (메서드가 제대로 동작합니다.)

// 이터러블 객체
let iAmIterable = {
  from: 1,
  to: 5,
  
  [Symbol.iterator]() {
    let current = this.from;
    let last = this.to;
    return {
      next() {
        if (current <= last) {
          return { done: false, value: current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// Array.from은 이터러블(이터레이터 반환 객체)도 배열로 바꿔줌
let arr = Array.from(iAmIterable);

console.log(arr); // [1, 2, 3, 4, 5]

요약

이터러블: [Symbol.iterator]라는 메서드를 가지고 있는 객체, 이 때 Symbol.iterator는 반드시 이터레이터를 반환해야 한다.

이터레이터: next 메서드를 포함하고 있는 객체, next 메서드는  { done: Boolean, value: any }를 반드시 반환값으로 가져야 한다.

유사 배열 객체: 배열처럼 index, 0 부터 시작하는 정수를 키로 가지면서, length 프로퍼티가 있는 것

 

이터러블과 유사 배열 객체는 Array.from을 통해서 배열 형태로 변환할 수 있다.


'WEB > JAVASCRIPT' 카테고리의 다른 글

자바스크립트의 모듈 시스템 (CommonJS, ES Module)  (0) 2025.01.12
이벤트 캡쳐링, 이벤트 버블링  (0) 2024.02.13
Promise, 프로미스  (0) 2024.02.03
Rest 파라미터, 스프레드 문법  (0) 2023.04.10
클로저  (0) 2023.02.15