[ProPro] Component 추상화(1)

jiseong·2022년 4월 27일
0

T I Learned

목록 보기
232/291
post-custom-banner

[ProPro] component 개선 방식에 이어서 고민을 해보면서 web component를 사용해볼까란 생각도 했는데 그러면 너무 많은 부분이 바뀌게 될 것 같아 포기를 했다. 그래서 기존의 컴포넌트 형태로 추상화한 Core Component를 건드리게 되었다.

자바스크립트에서 자바와 같이 추상클래스 구현은 어렵지만 동일한 클래스 구조를 가질 수 있도록 하는 목적통일함으로써 유지보수성을 높이고자 Core Component를 만들어두었었다. 하지만 돌이켜보니 원래 의도와는 다르게 팀원들간의 코드가 통일이 되어있지 않고 불필요한 메서드가 존재해 사소하지만 이런 부분들을 수정해보는 작업을 해보았다.

메서드 분리

class Component {
  // 생략...
  createDom = (tagName, attrs) => {
    const $dom = document.createElement(tagName);
    for (const [key, value] of Object.entries(attrs)) {
      $dom[key] = value;
    }
    return $dom;
  };

  replaceElement = ($old, $new) => {
    $old.parentElement.replaceChild($new, $old);
  };

  appendRoot = ($root, $new, isNav = false) => {
    if (isNav) {
      if ($root.childNodes[0]) $root.replaceChild($new, $root.childNodes[0]);
      else $root.appendChild($new);
      return;
    }

    if ($root.childNodes[1]) $root.replaceChild($new, $root.childNodes[1]);
    else $root.appendChild($new);
  };
}

다음과 같은 메서드들은 Element를 생성하고 수정하는데 쓰이는 목적이기 때문에 유틸함수로써 빼두는게 좋다고 생각하여 분리하였다.

이제 사용하고자 하는 컴포넌트에서 필요한 함수만 불러오면 된다.

import { createDom } from '../../utils/dom';

const logo = createDom('a', {
  className: 'logo router',
  href: '/',
});

클래스 통일

render()

render()메서드는 새로운 Element을 생성해주는 역할, 즉 새로운 컴포넌트를 생성해준다. 그런데 최상위 부모 컴포넌트 역할을 하는 페이지와 페이지가 아닌 컴포넌트에서 render()하는 방식이 다르다.

페이지의 경우에는 네비게이션 컴포넌트와 충돌을 피하기 위해 appendRoot라는 커스텀 함수를 사용해서 id="root"를 가진 Element 하위에 appendChild() 메서드를 사용하여 props로 전달받은 요소에 새로운 Element를 삽입한다.

export default class ProfilePage extends Component {
  constructor(props) {
    super(props);
    // 생략...
    this.$dom = this.createDom('div', {
      className: 'profile-page-wrapper',
    });

    this.render();
  }
  // 생략 ...
  
  render = () => {
    this.$dom.innerHTML = `
        <div class="container">
           // 생략...
        </div>
    `;

    this.appendRoot(this.props, this.$dom);
  };
}

반면에 페이지가 아닌 컴포넌트에서는 innerHTML 프로퍼티를 사용하여 Element를 생성하고 해당 컴포넌트를 호출한 부모 컴포넌트에서 생성된 Element를 삽입하는 방식을 사용하고 있다.

// 부모 역할하는 컴포넌트 예시

export default class ProfilePage extends Component {
  // 생략 ...
  
  appendChild = () => { 
    const btns = this.$dom.querySelector('.clearfix');
    
  	 this.updateBtn = new Button({
      className: 'updateBtn',
      buttonText: '수정 완료',
      onClick: this.submitProfileData,
    });
 	
    btns.appendChild(this.updateBtn.$dom);
  }
// 자식 역할하는 컴포넌트 예시
export default class Button extends Component {
  constructor(props) {
    super(props);

    this.$dom = this.createDom('button', {
      className: props.className,
    });

    this.render();
  }

  render = () => {
    this.$dom.innerHTML = `
      <span>${this.props.buttonText}</span>
    `;
  };
  // 생략...
}

그래서 이런 부분들을 통일해주고 싶어 컴포넌트를 생성할때 생성한 Element를 삽입해줄 사용자 지정 Element역할을 해주는 container를 전달해주기로 했다.

class CustomComponent {
  constructor({ container, props }) {
    this.container = container;
    this.props = props;
  }
  // 생략...
}

그러면 Core 역할을 하는 component에서 container라는 이름의 property로 가지고 있어 container.innerHTML를 사용하여 삽입해주면 되기 때문에 모든 컴포넌트에서 동일한 Element 생성방식을 사용할 수 있게 된다.

그리고 처음에 innerHTML, appendChild을 섞어 구현했던 이유 중에는 innerHTML 특성상 삽입해버린 내용으로 덮어씌워버리기 때문에 각 페이지의 렌더링 요소와 네비게이션 요소를 분리시켜주었다.

post-custom-banner

0개의 댓글