[CryptoMeter] svg 파일들을 컴포넌트화 하기

0

CryptoMeter

목록 보기
4/6
post-thumbnail

프로젝트를 진행하면서 필요한 assets들 중 svg파일들이 많았다. 처음엔 svg파일들을 그냥 추출해서 assets 폴더에 넣어놓고 경로만 import해서 사용하려고 했는데, 아래와 같은 파일들이 중복되는 것이 싫었더.

svg파일들 가져오기

필요한 파일들이 많았는데, 피그마에서 분석을 통해 어떤 파일들이 필요하고, 특성을 어떠하며 반응형 레이아웃에서 크기가 어떻게 변하는지 알아야 했다.

크기별, 모양별로 분류하기

먼저 존재하는 모든 아이콘들과, svg로 사용할만한 이미지들을 전부 추출했다. 아이콘들마다 모바일에서 줄어드는 비율이나 상태별로 path가 어떻게 다른지 등이 다 달라서 시간이 꽤 걸렸다.

결과

다운받아 저장하기

우선 이 svg들을 피그마에서 전부 svg로 export 해준다.

그리고 vscode에서 이 svg들을 open하여 다음과 같이 내부 태그들을 볼 수 있게 하자.

<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 4C9.41167 4 2.5 10.2767 2.5 15.5869C2.5 19.5659 5.01011 23.0756 8.83328 25.1612L7.22539 31.239C7.082 31.7778 7.6765 32.2055 8.13244 31.8955L15.1843 27.0794C15.7788 27.1391 16.3846 27.1739 17 27.1739C25.0072 27.1739 31.5 21.9864 31.5 15.5869C31.5 10.2767 25.0072 4 17 4Z" fill="#161C2F"/>
</svg>

리액트 컴포넌트로 만들기

컴포넌트 리턴문에 복붙하기

아래와 같이 각 svg에 대응되는 컴포넌트를 만들고 해당 컴포넌트가 리턴하는 jsx에 복붙해준다. fill부분에는 colors.js에 미리 정의해둔 컬러 팔레트 색상을 적용하였다.

// KakaoLogo.jsx
const KakaoLogo = () => {
  return (
    <svg
      width="34"
      height="34"
      viewBox="0 0 34 34"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fillRule="evenodd"
        clipRule="evenodd"
        d="M17 4C9.41167 4 2.5 10.2767 2.5 15.5869C2.5 19.5659 5.01011 23.0756 8.83328 25.1612L7.22539 31.239C7.082 31.7778 7.6765 32.2055 8.13244 31.8955L15.1843 27.0794C15.7788 27.1391 16.3846 27.1739 17 27.1739C25.0072 27.1739 31.5 21.9864 31.5 15.5869C31.5 10.2767 25.0072 4 17 4Z"
        fill={colors.gray9}
      />
    </svg>
  );
};

styled svg로 만들기

이제 커스텀을 위하여 styled components를 활용할 차례다. 아래와 같이 S.Svg라는 styled svg태그를 생성하고, 기존의 svg태그를 S.Svg태그로 만들어준다. 피그마 기획안을 참고하여 모바일에서 해당 svg가 몇 픽셀로 줄어드는지 확인하고 @media문을 작성해준다.

const KakaoLogo = () => {
  return (
    <S.Svg
      width="34"
      height="34"
      viewBox="0 0 34 34"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fillRule="evenodd"
        clipRule="evenodd"
        d="M17 4C9.41167 4 2.5 10.2767 2.5 15.5869C2.5 19.5659 5.01011 23.0756 8.83328 25.1612L7.22539 31.239C7.082 31.7778 7.6765 32.2055 8.13244 31.8955L15.1843 27.0794C15.7788 27.1391 16.3846 27.1739 17 27.1739C25.0072 27.1739 31.5 21.9864 31.5 15.5869C31.5 10.2767 25.0072 4 17 4Z"
        fill={colors.gray9}
      />
    </S.Svg>
  );
};

const S = {};

S.Svg = styled.svg`
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;

  @media only screen and (max-width: 375px) {
    width: 18px;
    height: 18px;
  }
`;

모든 svg에 display: flex;를 설정해주었는데, 기본으로 svg가 inline이기 때문에 원활한 커스텀을 위해서는 블록 요소로 만들어야 했다. inline-block을 줘도 inline요소 특유의 4px (기본 line-height인가? 안없어진다..)정도의 픽셀 차이를 없애기 위함이었다.

props가 필요한 컴포넌트들 처리하기

가장 까다로운 부분이었다. 맨 위에서 보여줬던 다은과 같이 세 가지 상태를 가진 svg들을 prop을 이용하여 커스텀하는 과정이다.

Component props 사용

의외로 위의 예시를 처리하는 것은 쉬웠는데, 컴포넌트에 들어오는 props를 검사하여 각 path마다 fill값을 다르게 주면 끝이었다. 다음과 같이 처리해주면 된다.

const OrderIcon = ({ asc, desc }) => {
  return (
    <S.Svg
      width="16"
      height="16"
      viewBox="0 0 16 16"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        fillRule="evenodd"
        clipRule="evenodd"
        d="M7.02951 3.44559C7.25372 3.53846 7.3999 3.75724 7.3999 3.99992V12.3999C7.3999 12.7313 7.13127 12.9999 6.7999 12.9999C6.46853 12.9999 6.1999 12.7313 6.1999 12.3999V5.44845L4.42417 7.22418C4.18985 7.4585 3.80995 7.4585 3.57564 7.22418C3.34132 6.98987 3.34132 6.60997 3.57564 6.37566L6.37564 3.57566C6.54724 3.40406 6.80531 3.35272 7.02951 3.44559Z"
        fill={asc ? colors.gray7 : colors.gray4}
      />
      <path
        fillRule="evenodd"
        clipRule="evenodd"
        d="M9.37039 12.9543C9.14619 12.8614 9 12.6427 9 12.4V3.99998C9 3.66861 9.26863 3.39998 9.6 3.39998C9.93137 3.39998 10.2 3.66861 10.2 3.99998V10.9515L11.9757 9.17572C12.2101 8.9414 12.5899 8.9414 12.8243 9.17572C13.0586 9.41003 13.0586 9.78993 12.8243 10.0242L10.0243 12.8242C9.85267 12.9958 9.59459 13.0472 9.37039 12.9543Z"
        fill={desc ? colors.gray7 : colors.gray4}
      />
    </S.Svg>
  );
};

이 svg 컴포넌트를 사용하는 곳에서는 아래와 같이 심플하게 사용할 수 있다.

<OrderIcon />      //default
<OrderIcon asc />  //ascending order
<OrderIcon desc /> //descending order

styled-component props 사용

이 단계가 가장 어려웠다. 아래와 같은 아이콘을 하나의 svg 컴포넌트로 전부 대응하게 해줘야 했기 때문이다.

참고로 오른쪽이 데스크탑이고 왼쪽이 모바일인 것이 아니다. 같은 svg인데 쓰이는 곳이 다르다.

쓰이는 곳에 따라 색상과 크기가 다르다. 그리고 위와 같이 드롭다운이 열린 상태와 닫힌 상태의 모양이 다르다.

색상과 크기는 다음과 같이 컴포넌트 props로 처리하였다.

const DropdownHandleIcon = ({
  w = 15,
  h = 15,
  fill = colors.white,
}) => {
  return (
    <S.Svg
      width={w}
      height={h}
      viewBox="0 0 15 16"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M8.06444 12.2439L13.3144 6.24388C13.7388 5.75894 13.3944 5 12.75 5L2.25001 5C1.60564 5 1.26125 5.75894 1.68557 6.24388L6.93557 12.2439C7.23438 12.5854 7.76563 12.5854 8.06444 12.2439Z"
        fill={fill}
      />
    </S.Svg>
  );
};

회전율같은 경우 styled component의 props를 사용하기로 했다. 다음과 같이 S.Svg에 props를 사용하여 조건부로 transform속성을 넣어준다.

const S = {};

const rotate = css`
  transform: rotate(-180deg);
`;

S.Svg = styled.svg`
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  ${(props) => props.rotate === "true" && rotate}
  transition: transform 0.5s ease-in;
`;

그리고 컴포넌트에서도 rotate라는 prop을 받아 S.Svg로 그대로 전달해주면 된다.

const DropdownHandleIcon = ({
  w = 15,
  h = 15,
  fill = colors.white,
  rotate,
}) => {
  return (
    <S.Svg
      ..
      rotate={rotate}
      ..
    >
      ...
    </S.Svg>
  );
}    
  

후기

사실 이렇게 커스텀이 가능하게 전부 만들어놓긴 했지만 작업을 진행하면서 이런 방법이 맞는건가 싶은 의문도 들었다. 보통 svg를 리액트에서 사용하는 법을 검색하면 전부 CRA로 만든 앱에서 svg파일을 import할 때 ReactComponent로 가져와서 사용하는 자료만 나온다.
나 또한 그렇게 해 보았지만, path별로 색상을 다르게 하는것이 불가능했다. 무슨 이유에서인지 실제로 svg가 DOM에 반영될 때 하나의 path로 합쳐져 있었고, path별로 id값을 다르게 줬었는데 id속성도 실제 요소 검사에는 찾아볼 수 없었다.
더 공부할게 많아보이지만, 만들어놓은 컴포넌트들이 잘 동작하는 것으로 보아 일단은 성공이라 할 수 있겠다.

0개의 댓글