본문으로 건너뛰기
기술스택 · stack/javascript/이터러블과 유사 배열: for...of와 펼치기가 되는 조건

JavaScript · 5장 자료구조와 자료형

이터러블과 유사 배열: for...of와 펼치기가 되는 조건

어떤 값은 for...of로 돌 수 있고, 어떤 값은 is not iterable 오류가 나요. 어떤 값은 length도 있고 번호로 읽히는데 map을 부르면 is not a function이 나요. 두 오류가 어디서 오는지 알고, 둘 다 진짜 배열로 바꾸는 법을 정리해요.

#5.6

01. 이터러블: 하나씩 꺼낼 수 있는 값

[Symbol.iterator] 메서드가 있어서 값을 하나씩 꺼내 줄 수 있는 값을 이터러블이라고 해요(원리는 Symbol.iterator에서 봤어요). 배열 · 문자열 · Map · Set이 이터러블이고, 보통 객체는 아니에요.

이터러블이면 for...of만 되는 게 아니라, 값을 하나씩 꺼내 쓰는 문법이 모두 돼요. 펼치기(...), 구조 분해, Array.from, new Set(…) · new Map(…), Promise.all이 다 같은 약속을 써요.

console.log([...'안녕']);                // ['안', '녕'] — 문자열 펼치기
console.log(Math.max(...[3, 7, 2]));     // 7 — 배열을 인자로 펼치기

const [first, ...rest] = new Set(['a', 'b', 'c']); // Set 구조 분해
console.log(first, rest);                // a ['b', 'c']

const user = { name: '하늘' };
console.log([...user]);                  // 보통 객체를 펼치면?

마지막 줄은 user is not iterable 오류예요. 객체의 키나 값을 돌고 싶으면 Object.keys · Object.entries로 먼저 배열을 만들어요(객체를 배열로). 참고로 { ...user }처럼 중괄호 안에서 펼치는 건 이터러블과 상관없는 객체 복사 문법이라 잘 돼요.

02. 유사 배열: 배열처럼 생겼지만 배열이 아닌 것

length가 있고 0, 1, 2… 번호로 값을 읽을 수 있지만, 배열은 아닌 객체를 유사 배열이라고 해요. 번호로 읽는 건 되는데 map · filter 같은 배열 메서드가 없어요. 이터러블이 아닌 것도 있어요.

const users = { 0: '하늘', 1: '가람', length: 2 }; // 오래된 라이브러리가 주는 모양

console.log(users[0], users.length); // 하늘 2 — 번호로는 읽혀요
console.log(Array.isArray(users));   // false
console.log(users.map(u => u + '님')); // 배열 메서드는?

실무에서 만나는 유사 배열은 주로 브라우저에서 나와요. document.querySelectorAll()이 돌려주는 NodeList, 함수 안의 arguments, 파일 입력의 input.files, 문자열이 그래요. 이 중 NodeList · 문자열처럼 이터러블이기도 한 것은 for...of는 되지만, 배열 메서드가 없거나 일부만 있어요.

①왼쪽 원은 for...of로 돌 수 있는 이터러블, 오른쪽 원은 length와 번호가 있는 유사 배열이에요. 배열 · 문자열 · NodeList는 둘 다, Map · Set은 이터러블만, { 0: 'a', length: 1 }은 유사 배열만이에요. 보통 객체와 숫자는 어디에도 들지 않아요. Array.from은 두 원 안의 값을 모두 배열로 바꿔요.

03. Array.from: 무엇이든 진짜 배열로

Array.from(값)은 이터러블이든 유사 배열이든 진짜 배열로 바꿔 줘요. 이터러블이라면 [...값]도 같은 일을 하지만, 유사 배열은 이터러블이 아닐 수 있어서 Array.from이 더 넓게 통해요. 두 번째 인자로 함수를 주면 바꾸면서 map까지 해요.

const users = { 0: '하늘', 1: '가람', length: 2 };
console.log(Array.from(users).map(u => u + '님')); // ['하늘님', '가람님']
console.log(Array.from(new Set([1, 1, 2])));      // [1, 2]

// 번호 목록 만들기: length만 있는 유사 배열 + 함수
console.log(Array.from({ length: 5 }, (_, i) => i + 1));      // [1, 2, 3, 4, 5]
console.log(Array.from({ length: 3 }, (_, i) => `${i + 1}월`)); // ['1월', '2월', '3월']

Array.from({ length: n }, 함수)는 페이지 번호, 달력 칸, 별점 다섯 개처럼 개수만 정해진 목록을 만들 때 자주 써요. 함수의 첫 인자는 비어 있는 값(undefined)이라 _로 적고, 두 번째 인자인 번호를 써요.

// 브라우저에서: NodeList를 배열로 바꿔 배열 메서드 쓰기 (이 블록은 실행되지 않아요)
const titles = Array.from(document.querySelectorAll('h2'), h => h.textContent);

목록 화면 아래에 페이지 번호를 그리려고 해요. 전체 글 수 total과 한 페이지 글 수 perPage로 페이지 번호 배열을 만들어 보세요. 지금 값이면 [1, 2, 3, 4, 5]가 나와야 해요. 반복문 없이 써 보세요.

const total = 47;
const perPage = 10;

const pages = []; // 여기를 채워요
console.log(pages);
해답 보기

페이지 수는 Math.ceil(47 / 10) = 5예요(남는 글 7개도 한 페이지가 필요하니 올림). 그 길이의 배열을 Array.from으로 만들면서 번호 + 1을 채워요.

const pages = Array.from({ length: Math.ceil(total / perPage) }, (_, i) => i + 1);
// [1, 2, 3, 4, 5]

04. 실전 포인트

  • 언제 쓰나요? is not iterable · map is not a function 오류가 나면 값이 이터러블인지 · 진짜 배열인지 의심해요. NodeList · input.files · arguments는 Array.from으로 바꿔서 배열 메서드를 써요.
  • 대신 쓰는 것: 이터러블이면 [...값]이 짧아요. 보통 객체는 Object.keys · Object.entries로 배열을 만들어요. 함수에서 arguments 대신 나머지 매개변수 (...args)를 쓰면 처음부터 진짜 배열이에요.
  • 흔한 실수: 보통 객체를 for...of로 돌리는 것, NodeList에 map을 부르는 것, Array.from({ length: n })의 함수에서 첫 인자를 번호로 착각하는 것이에요.

정리

  • 이터러블은 [Symbol.iterator]가 있는 값이에요. for...of · 펼치기 · 구조 분해 · new Set()이 모두 받아요.
  • 유사 배열은 length와 번호는 있지만 배열 메서드가 없는 객체예요.
  • Array.from은 둘 다 진짜 배열로 바꾸고, 두 번째 인자로 바꾸면서 map도 해요.
{ 0: 'a', length: 1 }은 [...obj]와 Array.from(obj) 중 어느 쪽으로 배열이 될까요?

Array.from(obj)만 돼요. 이 객체는 유사 배열이지만 이터러블은 아니라서, 펼치기는 is not iterable 오류가 나요.

Array.from({ length: 3 }, (_, i) => i * 2)의 결과는?

[0, 2, 4]예요. 길이 3인 빈 배열을 만들면서, 각 칸의 번호 i(0, 1, 2)에 2를 곱한 값을 채워요.

Map과 Set은 이터러블일까요, 유사 배열일까요?

이터러블이에요. for...of와 펼치기가 되지만 length와 번호로 읽기는 없어요(크기는 size로 봐요).

SIMPLE · 기술스택 / JavaScript — 5장 자료구조와 자료형