데이터의 변화를 알아내고 변화 과정을 추적 하는 ListDiffer

Daybrush (Younkue Choi)
NAVER FE Platform
Published in
7 min readAug 5, 2019

ListDiffer는 리스트(또는 배열) 데이터의 변화를 알아내고 변화 과정을 추적하는 비교 라이브러리입니다. 리스트를 비교하여 얻을 수 있는 변화 값은 데이터의 추가, 제거, 이동입니다. 하지만 이전 데이터에서 이후 데이터로 변화했을 때 과정은 알 수 없을까요? 그래서 ListDiffer에서 추가, 제거, 이동의 변화 과정을 추적하는 기능을 만들었습니다.

데이터 추적의 목적은 최소한의 접근으로 이전 데이터에서 현재 데이터로 만들기 위함입니다. 대표적인 예로 데이터의 변화로 DOM을 동기화하는 React, Angular, Vue와 같은 프레임워크라고 볼 수 있습니다. 프레임워크는 데이터가 변화할 때 전체 데이터 개수만큼 변경하면 성능이 저하되기 때문에 데이터 추적을 사용 해서 DOM의 변경을 최소화해서 성능 향상에 도움을 줄 수 있습니다.

다음 본문에서는 데이터의 변화데이터의 추적을 어떻게 사용할 수 있는지 자세히 설명하려고 합니다.

데이터의 변화

데이터가 변화를 하면 얻을 수 있는 정보 4가지가 있습니다.

  1. added(추가): 현재 리스트에서 데이터가 추가된 인덱스들의 배열입니다.
  2. removed(제거): 이전 리스트에서 데이터가 제거된 인덱스들의 배열입니다.
  3. changed(이동): 이전 리스트에서 현재 리스트로 변경된 인덱스 배열입니다. 배열의 각 요소는 이전 리스트에서의 인덱스와 현재 리스트의 인덱스를 쌍으로 구성됩니다.
  4. maintained(유지): 이전 데이터에서 데이터가 삭제되지 않고 남아있는 이전 인덱스에서 현재 인덱스의 배열들입니다.

다음은 added, removed를 사용한 간단한 워드 비교 예제입니다.

데이터의 추적

이전 데이터에서 현재 데이터로 변화했을 때 다음 ordered, pureChanged 2가지 정보를 조합하여 변화 과정을 추적할 수 있습니다.

  1. ordered(상대적인 변화 과정): removedadded의 사이 중간단계를 나타내며 데이터를 삭제한 상태에서 데이터를 추가하기 전의 상태로 동기화하기 위한 배열입니다. 각 배열의 요소는 이전, 이후 인덱스 쌍으로 구성되며 changed의 개수보다 대부분 적거나 혹은 같습니다.
  2. pureChanged(절대적인 변화 과정): changed의 부분집합으로 데이터를 추가하기 전 직접 움직인 데이터의 prevList에서 인덱스와 list에서 인덱스의 배열들입니다. ordered의 절대적인 인덱스 배열들을 나타내기도 합니다.

데이터의 추적의 목적은 위에서도 언급했듯이 최소한의 접근으로 이전 데이터에서 현재 데이터로 만드는 작업입니다. orderedpureChanged를 사용하면 변화하는 횟수보다 추적을 사용해 변경한 횟수가 적기 때문에 성능 향상에 도움을 줄 수 있습니다.

예를 들어 1, 2, 3, 4, 5, 6, 74, 3, 8, 2, 1, 7로 변했다고 가정하면 8번 데이터 추가를 제외하고 인덱스가 바뀐 데이터 (changed)는 총 5개 입니다.

바뀌기 전
바뀌는 과정
바뀐 후

하지만 위에서 설명했던 것처럼 인덱스가 바뀐 데이터가 5개라고 해도 5개의 아이템이 전부 움직일 필요가 없습니다. ordered, pureChanged를 사용하면 어떻게 5개보다 덜 움직일 수 있을지 다음 애니메이션을 통해 알 수 있습니다.

바뀐 후

위 애니메이션 처럼 ordered, pureChanged를 사용하면 3번의 움직임만으로 자리를 바꿀 수 있있습니다.

위 과정을 코드로 나타내면 다음과 같습니다.

다음은 Data와 DOM을 동기화하는 예제입니다. DOM을 최소한으로 이동시켜서 성능을 최적화할 수 있습니다.

DOM의 변경을 알아내는 ChildrenDiffer

Cross Framework Component(CFC)는 하나의 공통 모듈 기반으로 다양한 프레임워크를 지원하기 위한 효과적인 구조입니다. CFC의 주원리는 DOM의 변화를 추적해서 Data를 변경을 하는 것입니다.

Cross Framework Component(CFC)에 대해 자세히 알고 싶으면 다음 링크를 참조하시길 바랍니다.

ListDiffer 시간 복잡도 O(n)을 만들기 위해 Map API를 사용합니다. 하지만 Map API는 모던 브라우저에서만 지원하기 때문에 오래된 브라우저에서는 제대로 사용을 못 하고 성능이 매우 안 좋아집니다. 그래서 DOM이 Data인 ListDiffer에서 DOM비교에 최적화 된 ChildrenDiffer를 만들었습니다.ListDiffer의 사용법과 같으며 따로 callback함수는 넣어줄 필요가 없습니다.

다음 예제는 ListDiffer를 통해 Data를 DOM을 변경사항을 알아내고 DOM을 ChildrenDiffer를 통해 Data로 반영하는작업을 합니다.

다음으로 CFC를 적용하기 위해서는 각 프레임워크 전용 ChildrenDiffer가 있어야 합니다. 왜냐하면 프레임워크마다 문법에 맞는 사용법과 동작 구조가 다르기 때문입니다. 다음은 CFC를 지원하는 프레임워크 전용 ChildrenDiffer입니다.

결론

이 글에서 ListDiffer의 특징과 사용할 수 있는 방법을 간단히 설명하고 그 예시를 데모를 통해 보여줬습니다. 이 list-differ 라이브러리를 사용해 CFC(Cross-Framework Component)를 만들 수 있었습니다.

@egjs/flickingCFC가 적용되어 주요 프레임워크를 지원할 수 있는 캐러샐 컴포넌트입니다. 또한 주요 프레임워크 이외의 다른 프레임워크에서도 코드 몇 줄로 아주 쉽게 @egjs/flicking를 이식할 수 있습니다.

CFC를 적용할 다음 프로젝트인 @egjs/infinitegrid에서도 곧 React, Angular, Vue 컴포넌트로 사용해보실 수 있습니다.

코드와 프로젝트는 GitHub에서 확인 할 수 있으며 메소드 / 이벤트 등의 사용법은 API문서를 참고하시길 바랍니다.

ListDiffer사용법 소개를 마지막으로 이 글을 마치겠습니다.

npm 또는 script 을 통해 사용하시려면 다음 명령어를 실행합니다.

  • npm
npm install @egjs/list-differ
  • script
<script src="//naver.github.io/egjs-list-differ/release/latest/dist/list-differ.min.js"></script>

궁금한 점이나 요청사항이 있으면 Github Issues에 자유롭게 등록해주세요.

감사합니다.

--

--