JavaScript

Javascript Iterator

La.place 2018. 10. 12. 21:18

ECMA2015 문법이 추가되면서 javascript는 큰 변화를 겪어가는 중이다. 개발하는데 정말 유용한 개념들이 되었는데, 한꺼번에 많은 스펙들이 추가되면서 생각보다 조명받지 못한 개념들도 많다. 그 중에서 우리가 많이 사용하지만, 또 간과하기 쉬운 개념인 Iterator(반복자)에 대해 알아보도록 하자.



for-of
Iterator에 대해 이야기 하자마자 갑자기 for-of가 나온 것인지 의아할 것이다. 하지만for-of 가 그 무엇보다 Iterator와 매우 밀접한 연관을 지닌 문법인데 이제부터 차근차근 알아가 보도록 하자.

for-of 가 생긴 배경에 대해 알아보기 위해서 약간 과거로 거슬러 올라가보도록 하자. ES5 시절, 자바스크립트에서 object 를 순회하는 방법은 한가지 뿐이었다. 바로 for-in 문법을 사용하는 것인데 이 문법을 사용하면 key 를 이용해서 object를 쉽게 순회할 수 있다. 그렇다면 for-in 을 사용해서 배열(Array)를 순회하면 어떨까? 결론부터 말하자면 순회는 가능하지만, index가 문자열이라는 점, prototype chain까지 순회를 한다는 점 등 배열을 순회하기엔 이것저것 문제점이 많다. 그렇다면 forEach 를 사용하면 되지않느냐 라고 말할 수 도 있겠다. 물론 forEach 도 훌륭한 함수이지만 사용자의 의도대로break; 를 하기가 쉽지 않다는 문제점이 있다.

이러한 for-in 의 문제점을 보완하기 위해서 ECMA2015에 새로운 문법이 추가되었는데 그것이 바로 for-of 이다. for-of 를 사용하면 배열은 물론 object까지 쉽게 순회가 가능하다(물론 Obejct.key()를 사용해야 하지만). 하지만 for-of 의 강력한 힘은 따로 있는데. 바로 열거가능(enumerable)한 객체라면 모두 순회가 가능하다는 것이다.

그렇다면 열거가능한 객체란 무엇인가? 배열(Array)는 물론, TypedArray, 문자열(String), Map, Set, DOM Collection 등을 말한다.


그렇다면 이렇게 반복 가능한 객체들은 어떤 원리를 통해 for-of 를 이용해서 순회를 할 수 있는 것일까? 여기에서 바로 오늘의 주인공인 Iterator가 등장한다. 위 Collection들은 내부적으로 Iterable Protocol을 구현한다. 그렇다면 Iterable Protocol 이란 무엇인가. 자세하게 알아보도록 하자.

Iterable Protocol
어떤 객체가 반복되기 위해서는 Iteration 동작에 대해 정의되어 있어야 한다. 자바스크립트에서 정의하는 Iterator protocol에 의하면 Iterator는 next() 함수를 구현해야 하고 이 함수는 결과값으로{value: someValue, done: Boolean} 와 같은 객체를 반환해야 한다. 아래 makeRangeIterator() 함수는 앞에서 언급한 protocol을 구현한 함수이다.


하지만 ECMA2015에서 오면서 Iterator protocol을 이용하여 열거가능한 객체를 만들 수 있게 되었다. [Symbol.iterator]() 와 next()를 이용하면 쉽게 정의 할 수 있게 되었는데, for-of 순회의 비밀은 여기에 있다. for-of는 [Symbol.iterator]()를 호출하여 반복을 실행하게 된다(여기서 새로운 원시타입으로 추가된 Symbol 에 대한 개념은 다루지 않도록 하겠다).


그렇다. Iterable Protocol만 잘 지켜 구현한다면 어떤 객체든 for-of 를 이용해서 순회 가능하게 정의할 수 있다. 뿐만 아니라, Iterator가 구현된 객체들은 for-of 뿐만 아니라 전개 연산자(spread operator)와 만나서 강력한 시너지를 발휘한다.

만약 크기가 100이고 0으로 초기화 된 배열이 필요하다고 하자 . 가장 쉬운 방법은 반복문을 사용하는 것이다.


물론 위 방법도 좋은 방법이긴 하지만, Iterator와 spread를 이용하면 좀더 간편하게 생성 할 수 있다. (Functional 한것은 덤이다!) 




Generator
다음 내용으로 Generator가 나올 것을 예측했다면, Iterator에 대해 어느 정도 잘 알고 있는 사람이라 할 수 있겠다. Iterator와 비슷한 이녀석은 일종의 코루틴(Co-Routine)인데, 다른 언어에서는 곧 잘 사용하는 개념이다 (Finite State Machine, Automaton 을 만들때 자주 사용하곤 했다).

Generator는 함수 실행도중에 잠시 멈췄다가 다시 실행할 수 있는 독특한 함수이다. Generator는 function*키워드를 사용해서 생성하며, Generator를 호출하면 실행되는 것이 아니라 Iterator객체가 반환된다. 따라서 Iterator에서 구현한 next() 함수를 호출하면 Generator가 실행되면서 yield 를 만날 때까지 실행되고, 이때 컨텍스트는 저장된 상태로 남아 있게 된다.


Generator를 사용하면 Iterable Protocol을 구현하는 것보다 좀 더 쉽게 Iterator를 사용할 수 있다. 컨텍스트를 저장하기 때문에 Dynamic Programming을 구현하는데도 매우 유용하다. 아래 함수는 유명한 fibonacci수열을 Generator로 구현한 것이다.

Generator의 진면목은 비동기 프로그래밍에서 볼 수 있다. 함수가 실행 도중에 멈춘다니. 언제 응답이 올지 알 수 없기 때문에, callback을 등록하는 비동기 프로그래밍에 응용하면 callback hell을 탈출할 수 있지 않을까? 이 주제에 대한 답은 ES6 제네레이터를 사용한 비동기 프로그래밍 이라는 포스트를 꼭 읽어 보도록 하자. 일목요연하게 Generator를 이용한 비동기 프로그래밍에 대해 설명하고 있다.

비동기 프로그래밍이라면 async-await라는 더 쉽고, 더 직관적인 멋진 문법이 있지 않은가? 굳이 비동기 제어를 위해 generator를 알아야 할까? 라고 생각하는 사람들도 있을 것이다. 그러나 만약 async-await를 제대로 이해하고 싶다면 generator에 대해 알아야 한다. async-await의 동작 원리에는 generator와 promise가 깊게 관여하기 때문이다. 이 관계에 대해서는 나중에 자세히 다뤄보도록 하겠다.



이번에는 Iterator에 대해서 간략하게 다뤄보았다. 다른 언어에서는 기본적으로 제공하는 Iterator를 javascript에서는 잘 지원하지 않았던 점이 아쉬웠는데 ECMA2015에 추가 되면서 요즘 나의 호기심을 독차지하고 있다. 물론 javascript에서 다루는 Iteraotr에 대해서는 아직도 쓰임새라던가 한계 등 의견이 분분하다. 그러나 Iterator에 대해서 다루기 시작하면, 페이지 하나 둘 쯤으론 끝나지 않을 만큼 그 사용법은 동기/비동기를 막론하고 정말 무궁 무진하다. 이번 포스트에서는 정말 간략하게만 다뤘지만 관심이 많은 사람들은 꼭 깊게 공부해 보길 바란다.

Origin: https://medium.com/@la.place/javascript-iterator-b16ca3c51af2

'JavaScript' 카테고리의 다른 글

Async-await는 어떻게 구현하는가  (1) 2018.10.28
Higher-Order Function 이란 무엇인가  (1) 2018.10.28
알아두면 쓸만한 Chrome Console 기능  (0) 2017.12.31
React v16 무엇이 바뀌었나  (1) 2017.10.08
Currying in Javascript  (0) 2017.07.23