Cross Framework Component를 만드는 방법

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

Cross Framework Component(CFC)는 하나의 공통 모듈 기반으로 다양한 프레임워크를 지원하기 위한 효과적인 구조를 말합니다.

Cross Framework ComponentCross Platform의 방식을 착안했습니다. Cross Platform 하나의 소스코드를 다양한 플랫폼(운영체제, 기기)에서 사용할 수 있는 구조로 Xamarin, Flutter, NativeScript, React Native와 같은 도구를 통해 iOS, Android, Windows 등에서 모두 사용할 수 있습니다.

Cross Framework Component도 마찬가지로 하나의 바닐라 컴포넌트로 React, Angular, Vue등에서 모두 사용할 수 있습니다.

기존 바닐라 컴포넌트를 프레임워크에서 사용하는 방법이 2가지가 있습니다.

  1. 기존 바닐라 컴포넌트를 단순하게 래핑하는 방법
  2. 프레임워크 전용으로 새로 만드는 방법

기존 바닐라 컴포넌트를 단순하게 래핑하는 방법

1번(기존 바닐라 컴포넌트를 단순하게 래핑하는 방법)은 사용자들이 가장 많이 사용하는 방법입니다. 그 이유는 매우 간단하고 쉽기 때문입니다.

하지만 이 사용법은 돔의 변화(추가 / 제거 / 이동)가 없을 때 적합한 방법입니다. 왜냐하면 프레임워크에서는 사용자의 데이터를 DOM으로 동기화를 하기 때문입니다. 하지만 바닐라 컴포넌트가 돔을 조작하면 프레임워크에서 돔으로 동기화 하는 작업을 방해하게 됩니다.

그 순간부터 프레임워크에 데이터와 돔의 관계가 엉키게 됩니다. 실제로 컴포넌트에서 DOM을 제거하면 아래와 같은 에러가 발생할 수 있습니다.

DOM Error in React

왜냐하면 프레임워크에서 제거된 DOM을 찾고 있기 때문입니다. 그래서 기존 바닐라 컴포넌트를 단순하게 래핑하는 방법을 사용하려면 돔을 조작하지 않아야 합니다.

프레임워크 전용 컴포넌트로 새로 만드는 방법

2번째(프레임워크 전용 컴포넌트로 새로 만드는 방법)는 새롭게 프레임워크 전용 컴포넌트를 만들 때 쓰는 방법입니다. 하지만 바닐라 컴포넌트가 이미 있는데 프레임워크 전용으로 다시 만드는건 무슨 문제가 있을까요?

물론 프레임워크 전용으로 컴포넌트를 만들었기 때문에 해당 프레임워크에서는 원하는 기능들은 제대로 동작을 할 수 있습니다. 하지만 바닐라 컴포넌트가 이미 있는데 프레임워크 전용으로 다시 만드는 경우는 코드가 프레임워크 별로 여러 개가 존재하게 되면서 유지보수가 매우 어렵게 됩니다. 그렇기 때문에 이 방법은 바닐라 컴포넌트가 없는 상태에서 프레임워크 컴포넌트로 시작한 경우에 적합한 방법입니다.

egjs는 위 두 가지 방법에 대한 문제를 해결하기 위해 Cross Framework Component를 고안하게 됐습니다.

다음 내용에서는Cross Framework Component 가 어떤 원리로 문제를 해결했고 다른 바닐라 컴포넌트에서 어떻게 적용 해야하는지 소개하고자 합니다.

Cross Framework Component의 원리

앞에서 말했듯이 프레임워크는 DOM으로 동기화를 하고 있지만 바닐라 컴포넌트가 동기화를 방해한다고 했습니다.

그래서 Cross Framework Component에서는 바닐라 컴포넌트에서 돔을 조작하지 않는게 조건입니다. 대신 프레임워크에서 DOM으로 동기화가 끝난 후 동기화 된 DOM을 데이터로 동기화를 1번 더 합니다.

이렇게 하면 서로 방해하지 않고 동기화 순서를 지킴으로써 원하는 데이터도 얻을 수 있습니다. 그렇다면 DOM에서 데이터로 어떻게 동기화를 할까요?

프레임워크에서 DOM으로 동기화한 방법을 컴포넌트에서도 똑같이 적용하는 것입니다.

Synchronize in the same way

예를 들어 1, 2, 3, 4, 5, 6의 프레임워크 데이터가 있고 DOM도 1, 2, 3, 4, 5, 6순서대로 있고 컴포넌트의 데이터도 1, 2, 3, 4, 5, 6 순서대로 있습니다.

여기서 프레임워크 데이터6을 3앞으로 이동한다고 가정해봅니다.

그렇다면 DOM에서도 프레임워크 데이터를 동기화 함으로써 DOM에서도 6 엘리먼트가 3 엘리먼트 앞으로 이동합니다.

마지막 Vanilla의 데이터도 DOM을 동기화 함으로써 6데이터를 3데이터 앞으로 이동합니다.

이렇게 프레임워크와 같은 방법으로 동기화하면 됩니다. 하지만 React, Angular, Vue에서 사용하는 동기화 방법은 모르고 React, Angular, Vue에서 사용하는 방법도 모두 다릅니다. 그렇기 때문에 같은 방법으로 만들 수 없는 대신 비슷한 방법을 만들어 결과를 같게 할 수 있습니다.

ListDiffer

ListDiffer는 리스트(또는 배열) 데이터의 변화를 알아내고 변화 과정을 추적하는 비교 라이브러리입니다.

React, Angular, Vue에서도 분명 비슷한 비교 함수가 존재하고 변경 과정을 추적하는 함수가 있습니다.

하지만 egjs는 React, Angular, Vue에서 모두 사용할 수 있는 라이브러리인 ListDiffer를 만들었고 이 라이브러리를 통해 동기화를 하려고 합니다. 자세한 내용을 다음 글에서 확인할 수 있습니다.

이렇게 ListDiffer를 사용하면 프레임워크에서 사용하는 방법을 몰라도 DOM에서 Component으로 동기화하는 작업을 할 수 있게 됩니다.

크로스 플랫폼이 다양한 운영체제에서 동작하기 위해서는 프레임워크 API와 운영체제 API 연결할 수 있는 서포터가 필요합니다.

Cross Framework Component도 마찬가지 입니다. 다양한 프레임워크에서 동작하기 위해서는 ListDiffer API와 Framework API 연결할 수 있는 서포터가 필요합니다.

Component에서는 ListDiffer API를 사용하지만 React용으로 만들면 React 컴포넌트로 결과물이 나오고 Angular용으로 만들면 Angular 컴포넌트, Vue용으로 만들면 Vue 컴포넌트가 쉽게 나올 수 있습니다.

Cross Framework Component의 준비물

Cross Framework Component 적용 방법에는 데이터 추적을 사용한 방법(효율 처리 방법)데이터 추적을 사용하지 않는 방법(일괄 처리 방법) 2가지가 있습니다.

데이터 추적을 사용한 방법(효율 처리 방법)

데이터 추적을 사용한 방법은 최대한 적은 횟수로 작업할 때 좋은 방법입니다.

  1. 프레임워크용 ListDIffer
  2. 렌더링 외부화 옵션
  3. 추가 메소드
  4. 삭제 메소드

기존 바닐라 컴포넌트를 Cross Framework Component형태로 만들기 위해서는 바닐라 컴포넌트의 내부 DOM 조작을 선택적으로 할 수 있어야 됩니다. 이 방식을 렌더링 외부화 옵션이라고 지칭했습니다. 바닐라 컴포넌트를 사용할 때는 추가, 삭제메소드에서는 appendChild, removeChild같은 DOM메소드를 사용하겠지만 프레임워크에서는 렌더링 외부화 옵션을 활성화 해서 추가, 삭제메소드에서는 appendChild, removeChild같은 DOM메소드를 막으면 됩니다.

다음과 같이 removed > ordered > added 순서대로 코드를 작성하면 사용할 수 있습니다.

  1. removed는 삭제할 인덱스가 담긴 배열입니다. remove 메소드를 통해 해당 인덱스의 데이터를 제거합니다.
  2. ordered는 움직일 시작 인덱스와 도착할 인덱스가 담긴 배열입니다. remove 메소드를 통해 해당 인덱스의 데이터를 제거하고 insert 메소드를 통해 도착할 인덱스에 추가함으로써 자리를 이동할 수 있습니다.
  3. added는 추가할 인덱스가 담긴 배열입니다. insert 메소드를 통해 해당 인덱스에 데이터를 추가합니다.

메소드만 일치하다면 이 코드는 복사/붙이기만 해도 실제로 적용될 수 있는 코드입니다.

데이터 추적을 사용하지 않는 방법(일괄 처리 방법)

데이터 추적을 사용하지 않는 방법은 변경 과정을 쓰는 방법과 같이 개별적으로 처리하는 상황보다는 일괄적으로 처리할 때 좋은 방법입니다.

다음은 일괄 처리 방법을 만들기 위해 필요한 항목들입니다.

  1. 프레임워크용 ListDIffer
  2. 렌더링 외부화 옵션
  3. sync 메소드

데이터 추적을 사용한 방법에서는 추가 메소드, 삭제 메소드가 있었지만 데이터 추적을 사용하지 않는 방법에서는 일괄적으로 처리하는 동기화 메소드가 필요합니다.

데이터 추적을 사용하지 않는 방법은 removed는 상황에 따라 생략할 수 있고, maintained > added 순서를 기억하시면 됩니다.

Flicking 3은 데이터 추적을 사용하지 않는 방법으로 만들어졌으며 다음 코드는 플리킹의 일부분입니다.

maintained를 통해 이전 데이터에서 유지된 부분만 가져오고added를 통해 새로운 데이터를 추가할 수 있습니다.

플리킹의 마지막 메소드 호출 caculateSize()에서는 일괄적으로 DOM의 사이즈를 가져오는 작업이 있습니다. 만약 플리킹을 데이터 추적을 사용한 방법으로 만든다면 레이아웃이 매번 발생할 수 있으므로 오히려 성능문제가 발생할 수 있습니다.

Flicking 3

FlickingCross Framework Component가 적용되어 react-flicking, ngx-flicking, vue-flicking 3가지 주요 프레임워크에서 지원을 하고 있고 Flicking의 대부분 기능을 그대로 사용할 수 있습니다.

결론

이 글에서 Cross Framework Component의 특징, 원리와 사용법을 설명했습니다.

Cross Framework Component를 적용할 다음 모듈은 바로 InfiniteGrid입니다. InfiniteGrid는 React에서 제한적으로 지원을 하고 있지만 곧 대부분의 기능을 지원이 가능한 React, Angular, Vue 컴포넌트로 만나보실 수 있습니다.

많은 분들이 egjs를 쓰고 계시며 React와 Angular, Vue 등과 같은 프레임워크도 많이 적용하고 계십니다. 이전까지는 코드 2벌로 관리하고 있기 때문에 처리 시간이 2배로 걸릴 수 밖에 없었습니다. 앞으로 FlickingInfiniteGrid는 앞으로 Cross Framework Component 구조로 통합 관리 되므로 문의에 대해 안정적인 대응을 할 수 있으며 다양한 기능을 조금 더 빨리 만나 보실 수 있게 됩니다.

Cross Framework Component 소개를 마지막으로 이 글을 마치겠습니다.

감사합니다.

--

--