✊ 필오의 개발일지
Back to Posts
2019년 2월 3일

코드스피츠77 ES6_3회차_Iteration & generator

코드스피츠77 ES6_3회차_Iteration & generator

🌕🌑🌑

자바스크립트에서도 일반명사처럼 생긴 고유명사가 많이 나오고, 그중에 하나가 interface. 자바에서의 interface는 따로 있지만, 자바스크립트에서의 interface는 무슨 의미인지, 뭐에 쓰는 용어인지 알아보자. 자바스크립트 스펙에 정의되어있는 interface 규격에 맞춰 자바스크립트의 루프가 구현되어있기 때문에 중요하며, loop를 배우기 전에 알아보자.

1. Interface in JS ### 1.1 Interface ECMAScript 공식문서에서는 interface에 대한 정의를 명확하게

내리고 있다.

  1. 인터페이스란 사양에 맞는 값과 연결된 속성키의 셋트
  2. 어떤 Object라도 인터페이스의 정의를 충족시킬 수 있다.
  3. 하나의 Object는 여러 개의 인터페이스를 충족시킬 수 있다.

interface 예제

  1. test라는 키를 갖고
  2. 값으로 문자열인지를 1개 받아 boolean 결과를 반환하는 함수가 온다.
{ test(str){ return true; } }

cf__1. es6 객체리터럴 test옆에 바로 괄호?

{ test: function (str){ return true; } }

자바스크립트 엔진 레벨에서 여러가지 interface를 정의하고 있다.


1.2 Iterator interface

  1. next라는 키를 갖고
  2. 값으로 인자를 받지 않고 IteratorResultObject를 반환하는 함수가 온다.
    • IteratorResultObject는 interface이다. (아래의 IteratorResult interface를 확인)
  3. IteratorResultObject는 valuedone이라는 키를 갖고 있다.
  4. 이 중 done은 계속 반복할 수 있을지 없을지에 따라 불린값을 반환한다.

위 조건만 만족하면 Iterator 객체로 본다. 아래는 타입스크립트에서 자바스크립트 내장 객체들을 정의해놓은 type definition 파일의 일부를 가져왔다.

// lib.es2015.iterable.d.ts interface IteratorResult<T> { done: boolean; value: T; } interface Iterator<T> { next(value?: any): IteratorResult<T>; return?(value?: any): IteratorResult<T>; throw?(e?: any): IteratorResult<T>; }
{ next(){ return {value: 1, done: false}; } }

그니까 iterator란

이터레이터 프로토콜(: 데이터 컬렉션을 순회하기 위한 프로토콜(미리약속된 규칙))은 next메소드를 호출하면 iterable을 순회하며 value, done 프로퍼티를 갖는 iteratorResultObject를 반환한다.

{ data: [1,2,3,4], next(){ return { value: this.data.pop(), done: this.data.length == 0 } } }

1.3 Iterable interface

  1. Symbol.iterator라는 키를 갖고
  2. 값으로 인자를 받지 않고 Iterator Object를 반환하는 함수가 온다. Iterator Object 인터페이스는 위 1.2의 interface
// lib.es2015.iterable.d.ts interface Iterable<T> { [Symbol.iterator](): Iterator<T>; } interface IterableIterator<T> extends Iterator<T> { [Symbol.iterator](): IterableIterator<T>; }
{ [Symbole.iterator](){ return { next(){ return {value: 1, done: false} } } } }

그니까 iterable란?

iterable은 Symbol.iterator 메소드를 구현하거나, 프로토타입 체인에 의해 상속한 객체를 말한다. Symbol.iterator 메소드는 이터레이터를 반환한다.

Symbol ?

iterator만 있으면 되지 않을까? 왜 iterable이 필요하지?


2. Loop to Iterator 왜 for, while, do..while을 쓰지 않게 하고 이런걸 제공할까 - 문이기 때문에

한번 실행하고 나면 사라진다.(노이만 머신의 구조) - 메모리에 남지않고, 실행된 후 사라진다. - 두번다시 반복시킬 수 없다. - 다시 호출을 위해서는 함수로 빼던지, 2번사용하던지 해야한다. - 여러번 다시 사용하는것은 안전하지 않다.. 어딘가 저장해놓고 재사용해야한다. - loop를 으로 바꾸고 싶다는 마음이 생김!!


2.1 While문으로 살펴보는 Iterator

현대언어의 기본적인 패러다임은 문을 제거하고 전부 식(값)으로 바꾸버리는 것.

모든 문을 함수에 집어 넣어버리면 함수에서는 값이 반환되는 형태이고, 그 함수를 호출하면 문을 원하는 시점에 실행할 수 있게된다.

for문이나 while문을 으로 바꾸고 싶다. 반복 전용에 해당되는 객체로 바꿔주면 된다.

while vs iterator

  1. while
let arr = [1, 2, 3, 4]; while (arr.length > 0) { console.log(arr.pop()); }
  1. iterator
{ arr: [1,2,3,4], next(){ return { done: this.arr.length == 0, // while의 조건문에 해당 (계속 반복할지 판단.) value: console.log(this.arr.pop()) // while의 body에 해당 (반복시 처리할 것.) } } }

정리

  1. 반복자체를 하지는 않지만 (: iterator 객체와 next객체가 반복하진 않지만.)
  2. 외부에서 반복을 하려고 할 때
  3. 반복에 필요한 조건과 실행을
  4. 미리 준비해 둔 객체 (self description: 나 자신에 대해서 내부에 설명)

즉, 반복행위와 반복을 위한 준비를 분리

  1. 미리 반복에 대한 준비를 해두고
  2. 필요할 때 필요한 만큼 반복 3. 반복을 재현할 수 있음

반복 자체를 하지 않지만, 외부에서 iterator를 이용해서 반복하려고 하는 상황을 위해서, 반복에 필요한 조건과 실행을 미리 준비해둔 객체를 갖고 있는 것이다. (iteratorResult객체)

next만 부르면, 몇번이고 반복할 수 있다. 이제는 더이상 복잡한 loop의 상태조건이나 문의 실행을 다 빼버리고, 외부에서는 반복이라는 행위만 하면된다. => 반복기와 반복조건을 분리


3. ES6+ Loop ### 3.1 사용자 반복 처리기 직접 Iterator 반복처리기를 구현해보자. 커스텀

// 1. 반복기 const loop = (iter: IterableIterator<number[]>, f: Function) => { // iterable 가드 if (typeof iter[Symbol.iterator] == 'function') { iter = iter[Symbol.iterator](); // next확인하는 가드도 있어야함. } else return; // iterator 가드 if (typeof iter.next != 'function') return; do { const v = iter.next(); if (v.done) return; // undefined면 종료처리 f(v.value); // 아니면 f에 value 전달 } while (true); // 반복기일뿐!! 재귀함수로 짜도 상관없다. }; // 2. 반복되야할 조건이 있는 iterable const iter = { arr: [1, 2, 3, 4], [Symbol.iterator]() { return this; }, next() { return { done: this.arr.length == 0, value: this.arr.pop(), }; }, }; loop(iter, console.log);

반복되야되는 조건에 해당되는 값들과 반복기를 분리했더니 반복기쪽에서는 그냥 돌리기만 하면되는 책임으로 확 줄고 (loop함수), 나머지 상태관리나 루프에 대한 모든 책임은 다 iterator객체가 가져갔다.

개발자스스로 나름대로의 구조와 이름으로 짤수도 있다. 단지 이제는 자바스크립트 표준이 있다.

이터레이터 패턴을 구현하는데에 있어서 자바스크립트 표준 스펙이 나왔고, 이걸 구현하는 공식적인 방법이 스펙으로 정의되어있다. 만약 스스로 나름대로의 이터레이터를 구현해 왔다면, 이제는 자바스크립트 표준 인터페이스에 맞춰서 iterator를 구현하시는 쪽으로 바꿔야한다. 언어의 혜택이 많기 때문


3.2 내장 반복 처리기

언어의 지원을 받는다는 것은 무슨뜻일가. 언어가 iterator 인터페이스에 대해서 처리해주는 내장 기능이 있다. 우리가 만든 모든 객체가 iterator 인터페이스를 충족해주면, 언어가 제공하는 문법적인 요소를 다 사용할 수 있다. iterable객체가 아닌데 아래의 처리기들을 사용하면 스크립트가 죽는다.

어떤 객체가 Iterable이라면, 그 객체에 대해서 자바스크립트에서는 아래의 기능들을 사용할 수 있다.

  1. Array destructuring 배열해체
// iter는 iterable 객체. const iter = { [Symbol.iterator]() { return this; }, arr: [1, 2, 3, 4], next() { return { done: this.arr.length == 0, value: this.arr.pop(), }; }, }; const [a, ...b] = iter; console.log(a, b); // 4, [3,2,1]
  1. Spread 펼치기
const iter = { [Symbol.iterator]() { return this; }, arr: [1, 2, 3, 4], next() { return { done: this.arr.length == 0, value: this.arr.pop(), }; }, }; const a = [...iter]; console.log(a); // [4,3,2,1]
  1. Rest Parameter (나머지 인자)
const iter = { [Symbol.iterator]() { return this; }, arr: [1, 2, 3, 4], next() { return { done: this.arr.length == 0, value: this.arr.pop(), }; }, }; const test = (...arg) => console.log(arg); test(...iter);
  1. for ...of while, for처럼 권한이 있지않고 권한이 전혀 없는 for.
const iter = { [Symbol.iterator]() { return this; }, arr: [1, 2, 3, 4], next() { return { done: this.arr.length == 0, value: this.arr.pop(), }; }, }; for (const v of iter) { console.log(v); }

배열이나 object를 써야지 이 혜택을 받는 것이 아니라, iterator만 만들면 이 혜택을 받을 수 있다. 객체 만들때는 괴로울지 몰라도, 사용할때는 예쁘게 작업 가능.

신규로 출시되는 많은 API가 iterable을 포함하고 태어나기때문에 iterable interface는 es6세계에서 반드시 이해하고 외우고 있어야 하는 내용이다.

정리

자바스크립트 es6이후에는 반복을 위해서 iterable을 만든다.


3.3 연습

제곱을 요소로 갖는 가상컬렉션 😵😵😵

const N2 = class { constructor(max) { this.max = max; // 무한배열을 막기위해 } [Symbol.iterator]() { let cursor = 0, max = this.max; return { done: false, next() { // 반복기일 뿐 if (cursor > max) { this.done = true; } else { this.value = cursor * cursor; cursor++; } return this; }, }; } }; console.log([...new N2(5)]); for (const v of new N2(5)) { consoel.log(v); }

하나의 Object는 여러 개의 인터페이스를 충족시킬 수 있다. 이 객체는 Iterator객체임과 동시에 iteratorResultObject이기도 하다.


cf__2. 함수, 함수 스코프, 클로저

함수는 함수가 만들어지는 시점에 바깥쪽에 있는 변수들을 캡쳐해서 마치 지역변수로 쓸 수 있는 권한이 있다.

cf__3. 자바스크립트를 특정버전으로 열심히 공부해도..계속 바뀐다.


자료구조를 iterable로 구축하는 훈련이 되있어야하지만, 문법적인 혜택을 누릴 수 있다. 하지만 생각보다 iterable 객체를 만드는 것이 리소스가 많이 든다. generator함수로 해결할 수 있다.


4. Generator ### Iterator의 구현을 돕는 Generator (IteratorGenerator) function*()

generator 함수를 생성하는 리터럴.

const generator = function*(max){ let cursor = 0; while(cursor < max){ // iterator에서 next()와 같은 역할 yield cursor * cursor; cursor++; } } console.log([...generator(5)]); for(const v of generator(5)){ console.log(v); }

yield라는 키워드


참고자료 https://helloworldjavascript.net/pages/260-iteration.html  https://poiemaweb.com/es6-iteration-for-of  https://jusungpark.tistory.com/25 

PreviousCodeSpitz77 2회_Flow control statement (2)
Next코드스피츠77 ES6_4회차_Abstract loop & lazy execution

Related

© 2026 Felix