as를 통한 SVG 컴포넌트 확장성 높이기

Yeom Jae Seon·2021년 1월 27일
3

개발일지

목록 보기
2/8
post-thumbnail

문제 상황

React에서 SVG를 사용중이였다.(styled-components 사용중.) 그런데 여러 SVG를 사용하는 상황에서 여러 SVG모두 같은 css를 받던 상황이였다. (심지어 props에대한 로직도 같은 상황)

  • 문제 상황
//style.ts
...
export const Icon = styled(HomeSVG)<PropTypes>`
  > path {
    stroke: ${(props) => {
      return (props.selected  ? mainColor : greyFontColor)}};
  }
  ${mobileQuery}{
    width: 24px;
    height: 24px;
  }
  export const Icon = styled(AnotherSVG)<PropTypes>`
  > path {
    stroke: ${(props) => {
      return (props.selected  ? mainColor : greyFontColor)}};
  }
  ${mobileQuery}{
    width: 24px;
    height: 24px;
  }
  export const Icon = styled(Another2SVG)<PropTypes>`
  > path {
    stroke: ${(props) => {
      return (props.selected  ? mainColor : greyFontColor)}};
  }
  ${mobileQuery}{
    width: 24px;
    height: 24px;
  }
  .....

styled-components에서 해당 SVG컴포넌트들에 동일하게 스타일을 주기엔 코드중복이 보이던 문제가 있었다.

이 문제는 styled-components의 as를 이용해 해결했다.!

import HomeSVG from '@assets/svg/home.svg';
import ClipBoardCheckSVG from '@assets/svg/clipboard-check.svg';
import DocumentTextSVG from '@assets/svg/document-text.svg';
import TemplateSVG from '@assets/svg/template.svg';
import UserSVG from '@assets/svg/user.svg';

다섯개의 SVG를 받아오고있었고 이 SVG들은 모두 같은 스타일링을 할 것이다.

const options = [
  { title: '홈', path: '/', svg : HomeSVG },
  { title: '강의평', path: '/lectures', svg :  ClipBoardCheckSVG  },
  { title: '강의자료', path: '/materials', svg :DocumentTextSVG  },
  { title: '시간표', path: '/timetables', svg : TemplateSVG  },
  { title: '마이페이지', path: '/mypage', svg : UserSVG   },
];

const BottomNav : React.FC = () => (
  <NavContainer>
    {options.map((option) => (
      <EachOption
        key={option.title}
        title={option.title}
        path={option.path}
        svg={option.svg}
      />
    ))}
  </NavContainer>
);

각각의 svg를 props로받는 EachOption컴포넌트들은
<Icon as={svg} selected={path === router.pathname}/> 이 styled-components를 리턴한다.

as 를이용하면 Icon컴포넌트가 어떤 tag를 가르키든 Icon 컴포넌트는 as가 가르키는 tag가 된다.

  • Icon
//style.ts
export const Icon = styled.svg<PropTypes>`
  > path {
    stroke: ${(props) => {
      return (props.selected  ? mainColor : greyFontColor)}};
  }
  ${mobileQuery}{
    width: 24px;
    height: 24px;
  }

Icon컴포넌트의 tag는 svg로 설정은하였지만 h1이던 div든 .. 뭐든 상관없다. 어짜피 as가 가르키는 string tag로 변경이 되기 때문이다.

헤맸던 부분

<SVG />SVG 차이를 몰라서 자꾸 에러가나서 힘들었다. console에 두개를 모두 찍어보면

//console.log(SVG)

ƒ SvgHome(props) {
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"]("svg", _extends({
    xmlns: "http://www.w3.org/2000/svg",
    fill: "none",
    viewBox: "0 0 24 24"
  }, p…

//console.log(<SVG />)

{$$typeof: Symbol(react.element), key: null, ref: null, props: {…}, type: ƒ, …}

즉, SVG 는 함수이다. 컴포넌트이므로 함수이다. <SVG />SVG의 리턴값인 객체이다. 이는 JSX인 것이고 JSX는 객체이다.(왜냐면 JSX는 React.craeteElement()로 트랜스파일링되고 React.createElement()는 객체를 리턴하기때문에 <SVG /> 는 객체이다. 여기서 리턴되는 객체는 React 엘리먼트라고 한다.)

그러므로 SVG는 JSX를 리턴하는 함수이고 <SVG />는 리턴된 JSX이므로 <SVG />는 하나의 만들어진 엘리먼트이다.

이 차이를 알고나면 어렵지 않은 문제였다.

결론

  • SVG<SVG />는 각각 함수와 함수의 리턴된 값인 JSX, 즉 엘리먼트(객체)이다.
  • styled-components의 as 를 이용하면 좀더 확장성 있는 컴포넌트의 작성이 가능하다.

0개의 댓글