React datagrid component 제작기 (with ES6, TypeScript)

안녕하세요. 저는 CHEQUER에서 프론트-엔드 기술을 총괄하고 있는 장기영 개발자입니다. 주로 JavaScript를 기반으로 웹 기술을 다루고 있고, 수많은 웹 UI 컴포넌트를 개발해왔습니다. 최근 TypeScript와 React를 회사 표준 기술로 전환하면서 가장 난이도 있는 UI 컴포넌트인 Web Based DataGrid를 TypeScript와 React 기반으로 리팩토링한 스토리를 공유하고자 합니다.
React datagrid 프로젝트를 시작하면서 이런 상상을 해보았습니다. 이번 프로젝트가 어느 정도 진행이 되면(사실은 완성되면 이었지만….) ‘꼭 정리해서 발표자료를 만들어봐야지.’라고 다짐했었습니다. 이제 프로젝트를 시작한 지 만 6개월을 지나고 있습니다.
언젠가는 완벽한 코드를 만들고 말겠어!
데이터 그리드 컴포넌트 리팩토링을 시작하면서 JavaScript로 만들때와는 색다른 경험을 많이 했습니다. 여러 도전적인 상황들도 있었고 공유할만한 내용들이 꽤 많아서 KCD 2018 발표 준비겸 먼저 미디엄에 제작기를 작성해보았습니다.
먼저 데이터 그리드 리팩토링 과정에서 React 설계 도움을 주고 발표기회까지 제공해준 김동우씨에게 감사하다는말을 전하면서 글을 시작하겠습니다.

데이터 그리드란?

과거 필자가 만들었던 AXISJ, AX5UI에는 Grid라 이름 붙여진 UI 컴포넌트가 있다. 웹에서 엑셀과 비슷한 경험을 주는 컴포넌트로 웹에서 많은 데이터를 표현하기 위해 사용하는데, AXISJ, AX5UI 까지는 Grid라는 이름을 사용했는데 국/내외 다른 Web UI 컴포넌트 오픈소스들이 반응형 레이아웃(Responsive Layout) 을 위한 그리드 시스템(Grid System)의 의미로 그리드를 사용하고 있다 보니 이번 리팩토링을 진행하면서 이름을 데이터 그리드(datagrid)로 새롭게 지었다.

ax5ui-grid (http://ax5.io/ax5ui-grid/demo/12-body-grouping.html)

대용량의 데이터 처리

데이터 그리드는 웹에서 처리하기 어려운 크기를 가진 데이터를 표현할 수 있어야 한다. 지금까지 개인적인 경험상의 데이터로 레코드가 1,000건이 넘는 경우 일반 테이블로 데이터를 표현하면 브라우저의 성능이 저하되어 문제가 발생한다. (단 이런 수치는 사용자의 컴퓨팅 환경에 의존적이므로 정확히 몇 건까지가 한계라고 말하기는 어렵다.)

데이터관리

데이터 그리드의 가장 큰 이점 중 하나는 바로 수만~수십만의 대량 데이터를 손쉽게 다룰 수 있다는 것이다. 일반적으로 전통적인 테이블 마크업으로 1,000건이 넘는 데이터를 표현하면 브라우저의 성능이 상당히 저하되어 사용자에게 좋은 사이트 경험을 주기가 어렵고 다량의 데이터 정렬, 편집, 구조화에 어려움이 많다.

필수기능

사용자가 데이터 그리드를 통해 얻고자 하는 경험이 결국 ‘엑셀’과 비슷하므로 데이터 그리드는 상당히 많은 기능을 제공하고 있다. 그 기능이 워낙 방대해서 딱히 표준은 없지만 필자는 최소 다음 10가지 정도의 기능을 만족해야 데이터 그리드로서 가치가 있다고 생각한다.

  • 원하는 영역 선택 후 클립보드에 저장하기
  • 데이터 정렬
  • 데이터 필터링
  • 특정 데이터를 간단하게 수정하기 (inline edit)
  • 셀의 데이터를 서식에 맞게 보여주기 (formatter)
  • 셀에 수식적용하기 (formatter)
  • click, keyDown 이벤트 등에 따라 특정 작업 수행하기
  • 멀티컬럼헤더(이건 한국인들만 원하는 게 아닐까..)
  • 특정열 또는 행까지 틀 고정
  • 컬럼의 너비 조정
  • 데이터 그룹화 기능
  • 데이터 소계 및 합계

빠른처리속도와 개발편의성

컴포넌트 내부에서는 위에서 요구하는 기능들을 수행하기 위해 복잡을 작업을 수행하지만, 사용자는 매우 가볍게 느끼도록 할 수 있어야 제대로 된 데이터 그리드 컴포넌트라고 할 수 있다.

하지만 막상 데이터 그리드를 개발하고자 한다면 많은 도전적 과제들이 기다리고 있다. 사람들은 MS사의 엑셀에 매우 익숙해져 있고 이와 비슷한 경험을 웹에서 하고 싶어 하므로 빠른 로딩, 부드러운 스크롤링, 풍부한 기능을 모두 담으면서도 동시에 컴포넌트를 사용하는 개발자들이 쉽고 간편하게 사용할 수 있도록 인터페이스를 설계해야 한다. 따라서 요구사항 수집 및 분석, 설계 단계에서부터 많은 고민을 시작할 수밖에 없다.

웹 애플리케이션에서 데이터 그리드사용예 (협찬 sqlgate)

데이터 그리드를 이용해서 개발해본 경험이 있는 개발자는 데이터 그리드가 얼마나 편리한지, 또 얼마나 많은 개발자의 노력을 줄여줄 수 있는지 알 수 있다. 결국, 한 번도 안 써본 사람은 있어도 한 번만 사용한 사람은 없을 정도로 강력한 컴포넌트이다.

데이터 그리드를 만드는 이유

과거에는 데이터 그리드처럼 복잡하고 어려운 UI를 좋아하는 나라는 아마 한국이 유일하지 않겠냐는 생각을 했었다. 하지만 최근 다양한 웹 애플리케이션이 나오고 예전보다 다양한 분야에서 매우 복잡한 작업을 수행하는 웹 애플리케이션들이 만들어지고, 보편화 되면서 데이터 그리드의 중요성이 높아지고 있다.

사실 필자는 JavaScript UI를 수차례 제작한 경험이 있다. 공식적으로는 2013년도에 발표된 AXISJ라는 이름의 Javascript UI framework 가 처음이지만 AXISJ를 발표하기 전에 이미 2차례 더 개발한 경험이 있다. 그 후에도 AXISJ 를 개발하면서 아쉬웠던 부분을 개선하고자 AX5UI라는 이름의 jQuery UI plugins도 만들었다.

왜 또 만들고 싶어졌을까?

최근 웹 개발환경은 하루가 멀다 하고 빠르게 변화하고 있다. 2015년부터 그 변화는 더욱 가속화되어 에디션 5.1 으로 제작된 코드는 이제 구식으로 느껴지고 있는 현실이다.

History of ECMAScript

그에 따라 jQuery를 이용하는 코드는 점점 배제되고 있다. 필자 역시 작년부터 jQuery를 이용한 코드를 사용하는 것을 꺼리게 되었고 새로운 프로젝트에서 ES6를 이용하길 원하고 있다.

React.js에 대한 믿음

최근 사용자가 원하는 기능이 점점 더 많아지고 웹 애플리케이션으로 할 수 있는 일들이 많아지면서 프로젝트의 복잡도는 증가하고 보다 많은 상태를 일관되게 관리할 수 있는 프레임워크가 필요해졌다. React는 이런 일들을 가능하게 해주는 훌륭한 프레임워크들 중의 하나이고 미래에도 계속 사용될 가능성이 매우 큰 프레임워크라고 생각된다. (https://jsdev.kr/t/topic/3107)

React를 이용하여 개발하게 되면 코드가 좀 더 간결해지고 불확실성을 줄여줄 수 있다. jQuery를 이용하는 방식이 React처럼 만들 수 없는 것은 아니지만 믿음직한 Virtual DOM이 개발자의 개인 능력 차를 줄여줄 수 있으므로 그렇다고 할 수 있다.

jQuery Sample

위 코드는 jQuery를 이용하여 간단하게 구현한 샘플코드이다. button을 클릭하면 count 변수의 값을 증가시키고 증가한 값을 출력한다.

jQuery Sample 실행화면

샘플코드가 간단하여서 별문제는 없어 보인다, 하지만 이렇게 작성된 코드는 코드를 유지 관리하고 일관된 성능을 보장하는 데 몇 가지 문제가 예상된다.

  • count 변수가 원치 않는 곳에서 변경될 가능성
  • 불편한 코드 재사용성
  • #target과 #btn-1, #btn-2 엘리먼트의 존재 불확실성
  • 불필요한 jQuery selecting
  • 상태변화에 따른 의존성 관리의 불편함.
React sample

같은 기능을 하는 프로그램을 React를 이용해 만들면 어떻게 다를까? 위의 코드는 React를 이용해 같은 역할을 수행하는 프로그램 코드이다. 위에서 언급한 문제들을 모두 해결 할수 있다. 물론 React를 이용했다고 해서 완전한 코드를 보장한다고 할 수는 없다. 개발자가 어떻게 코드를 짜는가에 따라 다르기 때문이다. 하지만 일반적인 오류들은 코드가 실행될 수 없으므로 기존의 방식보다 낫다고 할 수 있다.

jQuery를 이용하는 방식은 개발자가 미리 렌더링 된 엘리먼트를 조작하거나 렌더링할 엘리먼트를 만들어 특정 위치에 주입하는 방식이었다면, React의 방법은 Render에 정해둔 템플릿을 React 엔진이 가장 최적의 방법으로 관리하는 방식이다.

React Sample 실행화면

사실 믿음을 가지기까지는 시간이 조금 필요하다. React를 믿어야만 React를 사용할 수 있는데 믿음에는 시간이 필요한 것 같다.

새로운 데이터 그리드 UI 가 필요해.

이런 변화를 경험하고 나니 기존 코드로는 새로운 프로젝트를 하기는 싫어졌다. 이제는 레거시가 되어버린 프로젝트들과 웹 애플리케이션 이 되기에는 부족한 각종 어드민 시스템을 제외하고 (여전히 고전적인 방식의 웹 개발이 더욱 편한 경우가 많습니다) 왠지 Brand New라고 할 수 있는 프로젝트들에서는 React를 사용하고 싶고, 거기에 코드도 기왕이면 ES6를 사용하고 싶었다. 거기에다가 한발 더 나아가 TypeScript도 사용해서 보다 아름다운 코드를 작성하고 싶다는 강한 욕구가 생겼다.

이런 마음이 생기고 나니 자연스럽게 새로운 방식으로 제작된 UI 가 필요해졌다. 언제나 그랬듯이 데이터 그리드는 UI 프로젝트의 시작이자 끝을 담당했으므로 자연스레 그 시작은 데이터 그리드 로 하기로 했다.

설계

현재는 어느 정도 정리가 되어 있지만, 처음 시작할 때는 그야말로 제로였다. 하지만, 아무것도 없는 상황에서 무언가를 만드는 일은 언제나 그렇지만 참 설레는 일이다.

DOM 엘리먼트 구조설계

프로젝트를 시작하기로 한 후 몇 차례 데이터 그리드를 만들어본 경험으로 보름 이상 머릿속으로만 그림을 그렸다. 하지만 곧 머리속으로 상상하는 것 보다는 그림으로 그려야 한다는 사실을 깨닫고 몇장의 설계도를 그렸다.

설계 1단계

데이터 그리드도 크게 보면 Table의 일종으로 볼 수 있다. 구성요소를 크게 나누어 생각하면 데이터의 라벨값을 모아두는 Header, 데이터가 표현되는 Body, 데이터의 합계표현을 담당하는 Footer로 나누어 생각할 수 있다. Header와 Footer는 데이터 조작을 하거나 사용자 편의를 위한 tool 들을 담아두는 공간으로 사용될 수도 있다. 데이터 그리드가 일반 Table과 크게 다른 점 이라면 body 영역에 스크롤을 두어 일반 Table과 비교하면 좀 더 유연하게 표현할 수 있다는 점이다.

여기까지는 매우 간단하다. 하지만 데이터 그리드가 이 정도의 구성요소로 해결될 수 있다면 이런 글을 시작하지도 않았을 것이다.

설계 2단계

원하는 기능을 담을 수 있는 데이터 그리드를 만들기 위해 설계한 panel 구조도를 보면 header를 aside-header, left-header, header로 삼 등분 했다. aside는 데이터 맨 왼쪽에 위치하면서 줄번호 등을 표시하는 역할을 하는 공간이고, left는 틀고정(frozenColumn) 했을 때 고정된 헤더를 출력하기 위한 공간이다. 그리고 header는 스크롤이 될 수 있어야 하므로 container와 scroll의 구조로 설계했다. (header가 가로 스크롤 될 때 처리해야 함)

body의 경우는 header보다 복잡하다. 틀고정을 가로와 세로까지 할 수 있으므로 top-aside-body, top-left-body, top-body, aside-body, left-body, body로 나눈다. (top-aisde-body와 top-left-body는 스크롤이 되지 않는데 왜 같은 구조인지 라고 의문을 가질 분도 있을 것 같다. 이유는 코드의 편의를 위해 일관되게 처리했다.)

scroll은 가로 스크롤과 세로 스크롤을 담당하는 horizontal, vertical이 있는데. native 스크롤을 사용하게 되면 이벤트 반응속도를 마음대로 할 수 없어서 스크롤을 직접 만들어 사용하기로 했다. (native 스크롤을 이용할 때는 onScroll이벤트를 이용해야 하는데 스크롤 포지션 변화가 생겼을 때 이벤트 발생을 약간 느리게 주게 되어 있고, 브라우저마다 그 설정에 차이가 있다)

scroll-container와 scroll

하나의 스크롤 컨테이너는 위의 그림과 같은 구조를 가지게 되는데, body-scroll이 가지고 있는 모든 데이터와 매치되는 DOM 엘리먼트를 모두 가지고 있는 것이 아니라, scrollLeft, scrollTop, containerWidth, containerHeight에 따라 출력이 필요한 영역의 데이터와 매치되는 DOM엘리먼트만 출력시켜주는 방법으로 개발하여 최적의 성능을 낼 수 있도록 설계하였다.

scrollTop(ST)와 scrollBarTop(BT)를 구하기 위해서는 약간의 수학지식이 필요하다. scrollContainerHeight(SCH), scrollHeight(SH)와 scrollBarContainerHeight(BCH), scrollBarHeight(BH) 의 비율이 ST와 BT의 비율과 동일하다는 원리를 이용하면 된다.

ST : SH - SCH = BT : BH - BCH
BT = ST * (BH - BCH) / (SH - SCH)
ST = (SH - SCH) * BT / (BH - BCH)

단, 데이터 그리드 한 행의 높이가 미리 결정되어 있어야 SH를 구할 수 있고 BH는 상황에 따라 너무 작게 계산될 수 있으므로 최솟값을 설정해두고 그 이하로 값이 작아지게 하면 안 된다.

프로젝트 폴더 구성

프로젝트 루트 폴더의 구성

시작할 때부터 프로젝트 소스코드는 gitHub에 두고 코드가 npm에 손쉽게 배포되어야 하고 프로젝트 웹사이트는 gh-pages를 이용하는 것을 목표로 하고 있었다.

오픈소스 프로젝트를 gh-pages로 서비스하는 것은 이점이 매우 많다. 일단 돈이 전혀 들지 않고, 전 세계 어디에서든 빠르게 서비스된다. 사실 이점이 많다는 표현보다는 안 쓰면 바보라고 할 수 있겠다.

npm배포의 경우 프로젝트 코드 전체를 npm publish 하는 것보단 배포대상을 정해서 꼭 필요한 코드만 publish 해야 한다. 자칫 잘 못 했다가는 인터넷 세상에 쓰레기를 배포하는 개발자가 될 수도 있다.

src폴더에서 데이터 그리드코드들을 두고 dev폴더 안에서 src폴더의 데이터 그리드를 사용자가 사용하는 방식과 유사하게 세팅해두고 개발하기로 했다. 그러기 위해선 webpack과 tsconfig에서 ‘datagrid-ts’ alias를 추가하여 ‘src’를 바라보도록 했다.

dist폴더는 gulpfile에서 npm 배포를 처리하기 위해 src폴더의 소스코드를 용도에 맞게 처리하고 dist 폴더를 npm 서버에 배포한다.

npm publish에 대한 설명은 다음의 링크(https://docs.npmjs.com/getting-started/publishing-npm-packages)에서 설명하고 있듯이 정해진 폴더에서 publish command를 터미널에서 입력해주면 그만이다.

하지만, npm version 을 알맞게 해주면서 publish 하는 일이 귀찮기도 하고 실수도 있을 수 있어 https://www.npmjs.com/package/gulp-shell을 이용하여 gulp task를 만들어 주고 intelliJ IDEA > gulp탭에서 마우스 클릭으로 관리 하고 있다. (npm publish에 관한 내용은 마지막에 좀 더 자세히 다루기로 하겠다.)

얼마 전 지인들과 VSCode가 좋은가 IntelliJ가 좋은가에 대해 이야기한 적이 있었는데. 개인적인 결론은 노트북이 엄청 좋고 IntelliJ 라이선스를 감당할 능력이 있다면 IntelliJ가 좋다는 결론을 내렸다. 다행히도 나의 경우엔 JetBrains사의 은총을 받아 axisj.com의 이름으로 오픈소스 라이선스를 지원받고 있다.

끝으로 docs폴더는 webpack 으로 dev폴더안의 프로젝트를 production모드로 빌드하면서 docs폴더를 만들어내도록 했다. docs폴더가 만들어지고 git에 push 되면 github에서 gh-page로 자동빌드 해준다.

Github > repo > setting > GitHub Pages

GitHub repo 설정에 사이트를 서빙 방법을 정하는 옵션을 결정 할 수 있다. 예전에는 Branch를 이용해야만 가능했는데 최근에는 docs 폴더를 선택할 수 있도록 기능이 추가 되었고, 개인적으로 Branch보다는 docs 폴더를 이용하는 방식을 더 선호 하고 있다.

좀 더 직관적인 방식이라 생각되고 프로젝트를 좀더 쉽게 설명해줄 수 있다고 생각한다. 그리고 Branch의 용도로 걸맞지 않다고 생각하고 있다. 도큐먼트 사이트가 master branch와 별개로 작동한다는 사실이 이상하다고 생각하기 때문이다.

src 폴더의 구성

소스 폴더의 구조를 정하면서 내공의 부족을 가장 많이 느꼈다. 딱히 문제 될 것은 없는 구조이지만 늘 부족하다고 생각이 되고 있다. 프로그래밍하다 부족함을 깨닫게 되면 좀 더 많은 개발자분들과 이야기 하고 싶다는 생각을 많이 하게 된다.

폴더를 언제 만들 것 인가에 대해 고민하다 나름의 원칙을 정하고 결정하기로 했다.

  • 같은 종류의 파일이 한 개 이상이 되면 폴더를 만든다.
  • 하나의 파일로 만들 수 있는 경우라도 가능한 한 잘게 나누고 폴더에 index로 export 한다.
  • 파일의 내용이 짧다면 파일로도 충분하다. (객관적인 기준은 없지만 한눈에 읽기 거북해지는 순간 파일을 분리하기로 했다)

WEBPACK

Webpack설정은 webpack.config.js 파일에서 webpack.dev.js와 webpack.production.js 로 env에 따라 분기처리 하도록 설정해두고.

Webpack Environment Variables(https://webpack.js.org/guides/environment-variables/)

dev 모드일때는 webpack-dev-server를 사용하고 localhost:4000으로 개발할 수 있도록 하고 production 모드일때는 UglifyJsPlugin, HtmlWebpackPlugin, CnameWebpackPlugin등을 이용하여 docs 폴더에 배포될 수 있도록 하였다.

주요 코드 살펴보기

React를 이용하여 개발하면서 가장 어려웠던 점은 state와 props을 활용하여 상태를 기준으로 코딩하는 발상의 전환이었다. 그동안 자바스크립트로 개발을 하면서는 조작을 원하는 DOM 엘리먼트를 찾아서 스타일이나 속성을 효율적으로 변경하는데 집중했었는데, React를 이용해 개발하면 DOM을 찾는 것을 지양하는 쪽으로 개발해야 했기 때문이다.

예를 들어 스크롤바를 마우스로 잡아 아래로 내리는 기능을 구현하려 할 때, 기존의 방식이라면 scrollBar의 top 스타일 속성의 마우스의 다운후 이동량에 맞게 조절하는 일을 해야 했었다. 그런 다음 scrollContent의 top스타일 속성을 그것에 맞게 변경해주어야 한다. (모든 작업을 위해서는 scrollBar와 scrollContent 엘리먼트를 찾거나 미리 찾아둔 상태여야만 한다.)

React를 이용하면 state에 scrollTop를 선언해두고 scrollBar를 마우스를 잡을때의 y값과 이동한 y값을 비교하여 scrollTop을 변경해주기만 하면 끝이다.

React를 이용하지 않아도 React로 개발하는 방식으로 작동하도록 할 수도 있다. 하지만 기존에 이런 방식을 사용하지 못했던 이유는 성능 때문이다. 브라우저는 DOM 조작하는데 엄청난 비용을 사용한다. 자바스크립트로 1,000라인의 코드를 처리하는 시간보다 DOM를 조작하는데 들어가는 비용이 더 크다. (구글에 ‘Why slow DOM manipulation’ 같은 키워드로 검색을 해보면 조작이 느리다는 표현이 맞지 않지만, 편의상 조작이라고 표현했습니다.)

REDUX

index.tsx

index 파일은 GridRoot를 가져와서 react-redux connect로 GridRootConnected를 만들어 반환해준다. 맨 처음 프로젝트를 시작할 때 redux store에 데이터 그리드의 데이터를 두는 모습으로 개발을 진행했는데, 그런 구조의 단점(구체적으로 속도)이 있어 데이터 그리드의 데이터와 정렬정보, 데이터 필터링 정보만 store에 두는 형태로 변경하였다.

GridRoot.tsx

datagrid Container Dimension

GridRoot component를 만들면서 가장 까다로웠던 부분은 react의 특성 때문에 componentDidMount가 되기 전에는 DOM엘리먼트의 크기를 알 수 없는데 render 함수가 실행한 후에야 componentDidMount 이벤트가 실행되지 않는다는 점이었다.

mounted 상태 값을 선언하여 문제를 해결하기로 했다. mounted: false 일 땐 render 함수에서 dimension들을 구하기 위한 최소한의 component element만 render에 return하고 DidMount 함수가 발동하면 mounted: true 로 상태 값을 변경하고, 그 후 setState에 의해 render가 발동되면 모든 component element들을 render에 return하도록 하였다.

line 4~5에서는 window resize시에 데이터 그리드 컨테이너 크기를 다시 구해야 하므로 throttle 를 이용하여 100밀리세컨드마다 updateDimensions를 실행하도록 했다.

public componentWillUnmount() {
window.removeEventListener(
'resize',
this.throttled_updateDimensions
);
}

이렇게 했을 경우 데이터 그리드가 전역의 window에 eventListner를 연결하였기 때문에 componentWillUnmount에서 리스너를 제거해주어야만 한다. componentDidMount나 componentWillUnmount같은 함수는 “React LifeCycle” 이란 키워드로 검색하면 자세히 알 수 있으니 React를 처음 접하시는 분들은 참고하면 좋겠다.

Convert Columns

multi row column을 지원하기 위한 여러가지 방법들이 있겠지만 직관적인 columns 설정을 위해 그림의 1번과 같은 방법을 선택하게 되었다. 하위 레벨의 columns 를 column 아래에 두기 때문에 트리구조로 설명할 수 있다.

하지만 tree 형태의 데이터를 가지고 table 형태로 출력하려면 성능이 저하 될 수 있으므로 미리 table 데이터로 변경해두어야 한다.

그리고 만약에 틀고정 컬럼인덱스(frozenColumnIndex)가 병합된 셀의 중간을 지나게 된다면 병합된 된 셀을 적절하게 잘라서 두쪽으로 나눌 수 있어야 한다.

위의 코드에서는 props.columns를 table데이터로 치환하여 state.headerTable에 저장하고 divideTableBy… 함수로 테이블 데이터를 asideHeader, leftHeader, header로 나누어 준다.

이렇게 빠르게 테이블을 그려낼 수 있는 데이터로 props를 치환해두고 필요할 때 사용한다. 그리고 props.columns가 변경될 때 propsToState가 실행되어 columns에 따라 표현할 수 있다.

Virtual Scroll

데이터 그리드의 데이터로 10,000건의 레코드가 전해진다면 전달받은 데이터를 store.setData가 storeState에 저장하고 gridBodyPanel로 전달되면 gridBodyPanel이 component를 반환하게 된다.

사용자가 gridRoot위에서 마우스 훨 이벤트를 발생시키거나 스크롤바를 움직이면 gridRoot state에있는 scrollLeft, scrollTop값이 gridBody props에 전달된다.

그에 따라 출력을 해야 하는 sRow, eRow 값을 구하여 gridBodyPanel에 전달하면 Range( sRowIndex, eRowIndex ).map으로 필요한 엘리먼트만 만들게 한다. 이렇게만 하면 스크롤 이동이 자연스럽게 처리되지 않는데(스크롤 이동을 한 줄보다 작게 했을 때 중간 상황을 표현할 수 없음) 이점을 해결 하기 위해 panel에 paddingTop 값을 조절하여 자연스러운 표현이 가능하게 처리한다.

1만건의 데이터를 처리하는 데이터 그리드

위의 그림 ‘1만 건의 데이터를 표현하는 데이터 그리드’에서 볼 수 있듯이. 데이터 그리드가 가진 데이터의 레코드 개수는 1만 건 이지만 gridBody가 가진 tr 의 개수는 16건이 된다. 스크롤 바를 움직이거나 마우스 휠을 작동하면 gridRoot.state.scrollTop값이 그에 따라 변경되고 그에 따라 차례대로 작업을 전파되어 사용자에게는 언제나 필요한 엘리먼트만 노출이 되게 해주는 것이다.

성능비교

그럼 이렇게 만든 데이터 그리드는 전작에 비교해 어떤 차이가 있을까? 개발자가 느끼는 차이는 매우 크지만, 이 차이를 증명하는 일이 간단하지는 않았다. 성능 차이를 비교하기 위해 크롬 브라우저의 Performance 탭을 활용하기로 했다. 각각 1만 건의 데이터를 처리하는 ax5ui-grid와 axui-datagrid를 두고 10초 동안 마우스 휠로 스크롤을 아래로 이동시킨 후 비교하였다.

위 영상은 ax5ui-grid와 axui-datagrid의 성능을 비교한 영상이다. jQuery를 사용하여 제작된 ax5ui-grid의 경우에는 같은 시간 동안 2887행까지 스크롤이 되었고, React를 이용하여 제작된 axui-datagrid의 경우에는 3741행까지 스크롤 되었다. 그리고 너비가 더 넓어 칼럼이 더 많이 출력되면 그 차이는 더욱 벌어졌다. jQuery로 제작된 ax5ui-grid의 경우는 눈에 띌 정도로 껌벅이는 현상까지 나타난다.

또한, jQuery를 이용한 경우에는 Rendering과 Painting에 많은 자원을 사용하였고 React의 경우 Scripting에 많은 자원을 사용한 것으로 나타났다. 따라서 React로 제작된 데이터 그리드가 Rendering과 Painting에 더욱 유리하고 사용자에게 더욱 쾌적한 경험을 가져다준다고 볼 수 있겠다.

배포하기

이렇게 제작된 ui component를 개발자에게 사용할 수 있도록 제공하기 위해서는 npm에 package로 등록하는 방법을 선택할 수 있다. 이 글을 읽은 분들이 npm을 사용한 경험이 있다는 가정하에서 npm 사용법에 관한 이야기는 생략하겠다.

npm에 등록하고 싶은 폴더로 이동하여 package.json을 규칙에 맞게 만들고 npm publish command를 이용하여 npm 서버에 소스를 업로드 시킨다.

다음은 배포하면서 겪은 필자의 경험을 정리한 몇 가지 사항이다. 나와 같은 경험을 하게 될 개발자에게 도움이 됐으면 하는 마음에서 정리해 보았다.

package 이름은 신중하게 결정.

npm사이트가 가면 현재 64만 개가 넘는 package가 등록되어 있다. 그리고 find package 하면 내가 원하는 package 명은 이미 누군가 사용 중이다. package 이름은 먼저 등록하는 사람이 주인이 되는 데다. 잘 못 정한 이름 때문에 고생하여 만든 코드가 퇴색될 수도 있다.

그렇다고 필자가 성공적인 package 이름을 가지고 있다거나 유명한 package의 개발자도 아니다. 그리고 이름이 별로라고 해도 package의 내용이 깡패라면 훌륭한 package가 될 수도 있다. 하지만 이왕이면 ‘다홍치마’라 하지 않았던가. 부디 이 글을 읽는 분들은 유명한 npm package의 개발자가 되기를 바란다.

.npmignore 파일을 잘 활용하자

https://docs.npmjs.com/misc/developers에서 npmignore의 사용법을 확인할 수 있다. 필자의 경우 처음 npm publish 할 때 이 부분을 잘 몰라서 npm package에 필요 없는 코드를 대량으로 업로드 한 경험이 있다.

npm 세상에는 너무도 많은 npm package가 존재하고 개발자들은 node_modules 폴더에 정확히 어떤 package 들이 있는지 일일이 알 수 없을 정도로 많은 package들을 프로젝트에 가지고 있게 된다.

그러므로 npm package를 publish 할 때는 반드시 꼭 필요한 코드만 담길 수 있도록 배려해야 한다.

Readme파일에 badge를 잘 활용하자

GitHub을 돌아다니다 보면 repo Readme 파일에서 위와 같은 badge들을 볼 수 있다. 왠지 있어 보인다. (난 언제나 badge를 왕창 단 프로젝트를 만들 수 있으려나…)

[![Build Status](https://travis-ci.org/ax5ui/ax5ui-grid.svg?branch=master)](https://travis-ci.org/ax5ui/ax5ui-grid)
[![npm version](https://badge.fury.io/js/ax5ui-grid.svg)](https://badge.fury.io/js/ax5ui-grid)
[![](https://img.shields.io/npm/dm/ax5ui-grid.svg)](https://www.npmjs.com/package/ax5ui-grid)

처음에는 어떻게 하는지 몰라 마냥 부러워만 했던 기억이 있다. 하지만 알고 나니 오픈소스 프로젝트에 badge 공짜로 제공하는 다양한 서비스들이 있고 위와 같이 markdown 문법에 맞게 Readme 파일에 잘 넣어주기만 하면 끝이다.

끝으로.

최근에 데이터 그리드를 어느 정도 만들고 나서 오픈소스 활동을 같이하는 혜미 씨에게 소스코드를 보여주었다. 아직은 부족함이 많은 코드여서 걱정을 많이 했는데. 뜻밖에 답변이 돌아왔다.

코드를 이해하기 쉬운데요

참으로 기분 좋은 답변이었다. 이번 프로젝트에서는 React와 TypeScript를 사용했기 때문에 가능했다고 생각한다. React의 탄탄한 라이프사이클이 코드의 흐름을 제삼자에게 전달하기 쉽게 만들었고 TypeScript가 변수의 모호함을 없애 주었기 때문이다.

항상 그렇지만 프로젝트를 진행하다 보면 시작과 달리 아쉬움 마음이 늘 생긴다. 데이터 그리드를 다시 만들면서 처음 기대에 어느 정도 도달했는지 아직은 잘 모르겠다. 하지만 지속해서 노력하고 갈고닦는다면 언젠가는 완벽한 코드를 짜는 사람이 되어 있지 않을까 기대한다.

긴 글 읽어 주셔서 대단히 감사합니다. 조금은 길고 지루한 내용이었지만 누군가에게는 도움이 되는 글이었으면 좋겠습니다.
GitHub : https://github.com/axui/datagrid (개발진행중)