🚀

급구 프론트 라이브러리 제작

프로젝트 개요
도입배경
Cordova 기반의 SPA(Single Page Application) 프로젝트인 클라이언트 프로젝트가 급격하게 커짐에 따라서 jQuery 를 통해서 Dom 을 변경하는 코드가 유지보수가 힘들게 됨.
React를 사용하려 했으나, 기존의 popUp control 함수와 호화이 어려울 것 같다고 판단하여 React의 개념을 도입한 커스텀 라이브러리를 제작하기로 결정
요구사항
가상돔(Virtual DOM)을 통해서 실제 DOM 의 변경에 따르는 성능을 향상시키고, 상태값을 통한 DOM의 변경을 이루는 Component 개념이 필요함.
그리고 기존의 프로젝트 구조를 (특히 popup 구조 관련) 유지하면서 도입할 수 있는 라이브러리가 필요
구성
snabbdom 의 third party 라이브러리인 snabby 를 사용해서 구현함. React 에서는 props, state 로 나뉘어있지만 해당 라이브러리에서는 모두 props 를 통해서 상태값을 컨트롤함
구현
코드
const html = require('snabby/create')([ require('snabbdom/modules/class').default, require('snabbdom/modules/props').default, require('snabbdom/modules/style').default, require('snabbdom/modules/attributes').default, require('snabbdom/modules/eventlisteners').default, ]); const _ = require('underscore'); class SJVirtualDomPop extends SJComponent { /** * @description 급구의 popup. 해당 함수를 통해서 popup을 띄우면서 virtual dom 을 연결 * @return SJVirtualDomPop 인스턴스 */ pop (id) { this.beforePop(); const tempView = '<div id="' + id + '"></div>'; const component = this.getComponent(); const isPopSuccess = popUpFullScreen('add', tempView, 'animated fadeIn', id); // popup 함수 if (isPopSuccess) { this.virtualBindDom = document.getElementById(id); const node = html`${component}`; html.update(this.virtualBindDom, node); this.virtualBindDom = node; this.afterRenderVDom(); // this.regProxyHandler(); this.afterPop(); } return this; } beforePop () { // pop 함수 실행 전 실행되는 함수 } afterPop () { // pop 함수 실행 전 실행되는 함수 } }; class SJComponent { constructor (props) { if (!props) props = {}; this.props = props; this.beforeComponentStart(); if (props !== undefined && Array.isArray(props) && !(props instanceof Object)) { // props 있다면 오로지 Object형이여야함 throw new Error('props have to Object instance'); } } /** * @description 매개변수 props 를 통해서 변경된 데이터가 확인되면 DOM 을 update 하는 함수 * React setState 와 같은 역할 */ setProps (props) { this.beforePropsUpdate(); const oldProps = (this.props) ? this.props : {}; const newProps = _.clone(oldProps); // shallow clone const keys = Object.keys(props); let isChangeProps = false; _.each(keys, (key) => { const oldVal = oldProps[key]; const newVal = props[key]; if (oldVal !== newVal) isChangeProps = true; newProps[key] = newVal; }); if (isChangeProps) { this.props = newProps; const dom = this.virtualBindDom; const getComponent = this.getComponent.bind(this); const component = getComponent(); const newDom = html`${component}`; html.update(dom, newDom); this.virtualBindDom = newDom; this.afterPropsUpdate(); } } render(html) { // 실제 뷰를 구성하는 함수 } getComponent () { // snabby를 통해서 생성된 snabbdom instance 를 return 하는 함수 return this.render(html); } afterPropsUpdate () { // setProps 함수를 실행한 후 실행되는 함수 } beforePropsUpdate () { // setProps 함수를 실행하기 전 실행되는 함수 } beforeComponentStart () { // SJComponent가 최초로 render 되기 전에 바로 실행되는 함수 } }
JavaScript
example
class CounterComponent extends SJComponent { beforeComponentStart() { this.props = { counter: 0 }; } render (html) { return html` <div> <div>${this.props.counter}</div> <button @on:click=${() => this.adcount()}>adcount</button> <button @on:click=${() => this.discount()}>discount</button> </div> `; } adcount () { this.setProps({ counter: ++this.props.counter }); } discount () { this.setProps({ counter: --this.props.counter }); } beforePropsUpdate () { console.log(this.props.counter) } afterPropsUpdate () { console.log(this.props.counter) } } class CounterPopUp extends SJVirtualDomPop { render (html) { return html` <div id="counterPopUp"> <button @on:click=${closeFullPop} /> ${new CounterComponent.getComponent()} </div> `; } } new CounterPopUp().pop('counterPopUp');
JavaScript
후기
오랫동안 쓰면 쓸 수록 아쉬운 점은 그냥 React를 사용할 걸 그랬다는 생각이...
일단 도입 후 기존의 jQuery 를 통한 DOM 업데이트에서 상태값을 통한 Component 업데이트로 바뀌면서 프로젝트의 구조나 가독성 등이 현저히 향상되었다는 것을 느꼈다. 팀원들도 만족을 한 결과물이긴 했지만, React를 사용해본 나로선 아쉬움이 많은 프로젝트였다. 사실 React를 참조해서 만들었지만 원본인 React의 하위 호환격인 느낌이라 사용하면서 'react엔 이런게 있는데...' 하고 생각할 때가 많았다. React와 같이 빠른 업데이트와 좋은 써드파티 및 라이브러리들을 사용할 수 없다는 점,( 예를 들어, Flux 개념을 도입하려면 Redux 가 아니라 새로 구현을 해야한다) 에러 및 사용에 있어서 참조할 점이 많이 없다는 점 등, 이런 것들을 보면 그냥 React를 도입해서 popup 시스템을 어떻게든 개선해서 사용할 수 있도록 했어야 했다는 생각이 들기도 한다. 현재 서비스에서 사용하면서 아쉬운 점이 많다보니 결국에 React, 혹은 React-Native 를 사용하기로 했지만 투자가 필요한 시기라 지표 상승에 부스트가 필요한 시기이다 보니 일단은 미뤄지게 되었다. (언젠가...)
하지만 개인적으로는 가상돔을 사용하는 라이브러리를 직접 만들어보면서 조금 더 프론트 라이브러리에 대한 이해도가 넓어졌다고 생각한다. 왜 이런 방법을 사용하는지, React를 참조하면서 React가 어떤 점이 좋은지, 어떨 때 사용하면 좋은지 등...