선택자 점수 낮추기

tohero·2022년 2월 2일
0

이번에 팀프로젝트를 진행하며, Table 형식의 Grid 컴포넌트 구현을 담당하게 되었다. 이때 가장 중요하게 생각했던 것은 사용성이다.

Grid처럼 다양한 곳에서 재활용되는 필수 컴포넌트의 경우, 호출되는 상황에 따라 css를 변경할 일이 빈번히 발생한다. props로 css를 변경할 수 있는 선택지를 준다면 좋겠지만, 그렇지 않고 className에 새로운 class를 삽입하여 css를 적용하는 경우 점수가 더 높은 쪽이 채택되기 때문에 필수 컴포넌트에서는 최대한 낮은 점수로 css를 적용하는 것이 유리하다.

예시

Grid.js

function Grid({className, children, pdLv}) {
  return <div className={classNames(className, pdLv)}>
    {children}
  </div>
}

GridItem.js

function GridItem({className, children}) {
  return <div className={classNames('gridItem', className}>
    {children}
  </div>
}

Grid.css

.pdLv > .gridItem {
	padding: 1rem;
}

GridItem.css

.pdLv {
	padding: 10rem;
}

App.js

function App() {
  return <div>
    <Grid pdLv={5}>
      <GridItem />
      <GridItem pdLv={10}/>
    </Grid>
  </div>
}

위 코드의 목적은 Grid에 pdLv이 입력될 경우 하위 gridItem에 전부 padding을 적용시키는 것이다. 그런데 일부 gridItem에서 pdLv을 별도로 적용시키고 싶을 때, GridItem.css의 선택자는 10점이고, Grid.css의 선택자는 20점이기 때문에 개별적용이 힘들어진다. 이때 important 혹은, id 선택자로 선택이 가능하다. 하지만 important의 경우 반드시 적용되어야하고 수정되면 안되는 경우에 사용해야하며, id 선택자를 사용하면 100점이 적용되기 때문에 추후에 더 높은 점수를 사용해야해서 최대한 낮은 점수를 사용하려는 편이다. 또 Grid와 같은 필수 컴포넌트들은 작성자 본인 뿐만아니라 여러 사용자와 공유하기 때문에 사용자 모두에게 점수를 신경쓰지 않고 편리하게 이용하도록 만들고 싶다면 더욱 낮은 점수로 만들 필요가 있다.

어떻게 점수를 줄일 수 있을까?

Grid.css에서 .pdLv > gridItem 구조를 없애야 한다. 즉 .pdLv을 class로 적용하지 않고, 인수로 pdLv이 넘어온 경우에만 gridItem에 padding 속성이 적용되도록 만들면 된다. 이 때 styled-component를 사용했다면 간편하다. Grid에서 pdLv 인수가 입력되면, styled component의 props로 pdLv을 넘겨주고, .gridItem을 선택하여 padding: ${(pdLv) => pdLv}px 이렇게 사용하면 끝이다.

하지만 css-in-js 가 아닌 곳에서는 어떻게 적용해야 하는가? 위에서 말한 구조 자체가, js의 props에 따라 css를 컨트롤해야하는 상황이다. 일반적으로 불가능한 구조인데, React.cloneElement를 사용하면 가능하다.

React.cloneElement && React.Children.map

본인은 addClassToElement 함수를 만들었다. 인수로 element, props가 입력된다. 내부적으로 React.Children.map 과 React.cloneElement를 사용했는데, React.Children.map은 Array.prototype.map과 동일한 방식으로 동작하는 메서드다. 즉 child마다 className을 적용시키기 위해 사용했다. React.cloneElement는 입력된 React Element와 동일한 새로운 React Element를 생성한다. 이 때 원하는 props와 children을 수정할 수 있다. 이 메서드를 이용해서 pdLv에 따라 className을 유동적으로 입력하면 된다.

이 구조를 사용하면 props에 따라 children의 className을 유동적으로 바꾸어 줄 수 있다.

참고: https://reactjs.org/docs/react-api.html

const addClassToElement = (() => {
  const _addClassToElement = (element, className) => {
    return React.cloneElement(element, {
      className: classNames(element.props.className, className),
    });
  };

  return (element, className) => {
    if (Array.isArray(element)) {
      return React.Children.map(element, child => _addClassToElement(child, className));
    } else {
      return _addClassToElement(element, className);
    }
  };
})();

느낀점

위 방싱이 썩 좋다고 느껴지지는 않는다. 공수가 들어가기 때문이다. 하지만 프로젝트의 성향에 따라 시간이 있다면 적용함으로써 사용자의 편의성을 높이는 것도 개발자의 역할 중 하나라고 생각했다. 그리고 결과적으로 낮은 선택자를 통해 사용성을 높이는데 성공했기에 만족한 프로젝트다. 앞으로 공수가 덜들어가며 선택자 점수를 낮추는 다른 좋은 방법이 있다면 탐구해서 다음에 포스팅하도록 하겠다.

profile
Front 💔 End

0개의 댓글