티스토리 뷰

발생일: 2015.05.13

키워드: React, Immutable, Pure Render Mixin, immutable-js

문제:
Facebook의 Immutable-js 모듈에 대해 살펴보다가,
리액트 컨퍼런스(React.js Conf 2015)에서 Immutable 의 저자(Lee Byron)의 발표 영상을 봤다.

어떤 관점에서 라이브러리를 만들었고,
어떻게 구현했는지, 왜 필요한지에 대해 아주 잘 설명하고 있는 영상이다.

발표자의 말이 너무 빨라서 (ㅠ_ㅠ) 중간 중간 메모하며 들었는데,
내용을 정리해두면 다른 사람들도 도움을 얻을 것 같아 좀 더 손봤다.

(주의: 오역이 많을 수 있다.ㅎㅎ)


해결책:



Immutable JS and React (React.js Conf 2015)



'Immutable'의 의미는 한 번 생성하면 더 이상 변경되지 않는다는 것.
'Persistent'는 값을 변경하는 API를 제공하지만, 매번 새 객체를 리턴하고 이전 객체는 계속 유지된다는 의미이다.


예를 들면,



`push()`로 값을 변경하지만 새로운 객체가 생성되고, 기존 객체는 그대로 유지된다.
이게 persistent 의 의미이다.


그럼 push() 는 어떻게 동작했을까?


대략 위와 같은 코드인데, 복사본을 만들고, 이걸 수정한 다음에 리턴하는 방식이다.


근데, 이렇게 하면 복사본을 엄청나게 생성할 텐데, 느리지 않을까?

꼭 그렇지만은 않다.
리액트도 전체 UI를 다시 렌더링하는데 빠르지 않나~?


이건 인터페이스와 구현의 차이라고 얘기할 수 있다.


Immutable 인터페이스의 컨셉은, 변경하는 API를 호출할 때마다 매번 새 복사본을 생성하는 것이다.
하지만, 실제로 구현할 때는 이것보다 훨씬 더 효율적으로 처리할 수 있다.
CPU 타임이나 메모리 공간을 포함해서 말이다.


일반적으로 Immutable 은 Mutable 보다 느릴 수 있다.
아래 이미지는 Mori 라는 라이브러리이고, 최적화되어 있지만 실제로 네이티브 배열보다는 느리다.



자, 여기서 하나.
Immutable JS 는 Structural Sharing 에 기반을 두고 있다.



오래된 객체들을 최대한 재활용하는 것인데,
복사하는 작업을 줄일 수 있을 뿐 아니라, 메모리도 적게 쓸 수 있다.


Structural Sharing 은 "DAG" 라는 스트럭처로 구현할 수 있다.



Acyclic 은 비순환적으로 뜻, 즉, 단방향으로만 참조하는 걸 의미한다.
자식 노드는 부모 노드를 참조하지 않는다.

아래 트리처럼 말이다.



이 구조대로라면, 특정 노드가 변경되었을 때 많은 부분을 재활용해서 복사본을 생성할 수 있다.


위 그림처럼, 변경이 일어난 후(노란 원)에도, 오래된 객체와 새로운 객체를 동시에 유지(녹색 원)할 수 있다.
이게 'persistent'를 의미하는 것이다. 물론, 오래된 객체의 참조가 사라지면 가비지 컬렉팅 될 것이다.


하지만, 내 애플리케이션의 모든 데이터 구조가 DAG 같지는 않는데 어떻게 할까?



물론, Arrays 와 Objects 는 DAG 와 동일한 구조가 아니다.
하지만, 다행이도 우리에겐 Trie 가 있었다.




Trie 를 사용하면, 리스트를 key/value 로 처리할 수 있다.
IndexTrie 가 있는데, 각 노드는 다음 노드를 가리키며 고정 사이즈의 배열 형태이다.



그리고, 트리의 끝에는 값이 있다.
이 Trie 는 리스트와 동일하게 동작할 수 있는데, 리스트보다 훨씬 빠르게 값을 가져올 수 있고, 모든 값에 순서대로 접근할 수 있다.


예를 들어, 141 (0x01110010) 번째 값에 접근한다고 해보자.



트리의 첫 번째 뎁스부터, 01_11_00_10 노드를 찾아가면서 아주 빠르게 값을 찾을 수 있다.
일반적으로는 4개의 바이트보단, 32 바이트를 사용한다.

값을 설정할 때에도 같은 방법으로 접근해 처리한다.



다음으로 해시 트리가 있다.



해시 트리는 리스트와는 다르게, 마지막 뎁스까지 가지 않아도 값을 갖고 있는 노드가 있을 수 있다.



해시 트리는 정렬 순서를 보장할 수 없는데, 이렇게 함으로써 성능을 올릴 수 있었다.
실제로 ES6의 맵은 순서를 보장하지만, 이를 사용하지 않은 이유이기도 하다.


해시 맵의 셋팅도 리스트와 비슷한다.


예를 들어, 키가 'R'이라면, 'R'을 바이너리로 변경한 값 (0x01010010)으로 10_00_01 을 찾아들어가 가져올 수 있다.
셋팅도 마찬가지다.


구현은 트리(Trie)로 되어 있지만, 인터페이스는 리스트와 맵이다.




http://ohgyun.com/586 에서 계속...

반응형
댓글
공지사항