[React] - SVG 컴포넌트 만들기

ain·2022년 8월 23일
0

React

목록 보기
2/3
post-thumbnail

SVG 컴포넌트를 만든 이유

이번에 포트폴리오를 만들면서 아이콘을 svg로 가져와야 하는 상황이 있었다. React를 다루기 전에 HTML만으로 svg를 가져올 때는 그냥 img 태그를 사용해서 src로 svg 파일을 가져오거나, 아니면 아예 svg 코드를 HTML에 넣었었는데, React에서 JSX에 그렇게 긴 코드를 넣으니까 되게 지저분해 보이고 어수선해 보이기까지 했다.

만약 아이콘이 두세개가 아니라 1억개라면(...)

...
// github 아이콘 출처: https://simpleicons.org/
<svg 
  fill='#6E6E6E'
  role="img"
  viewBox="0 0 24 24"
  xmlns="http://www.w3.org/2000/svg">
  <title>github</title>
  <path d="M3 0C1.338 0 0 1.338 0 3v18c0 1.662 1.338 3 3 3h18c1.662 0 3-1.338 3-3V3c0-1.662-1.338-3-3-3H3Zm6.883 6.25c.63 0 1.005.3 1.125.9l1.463 8.303c.465-.615.846-1.133 1.146-1.553.465-.66.893-1.418 1.283-2.273.405-.855.608-1.62.608-2.295 0-.405-.113-.727-.338-.967-.21-.255-.608-.577-1.193-.967.6-.765 1.35-1.148 2.25-1.148.48 0 .878.143 1.193.428.33.285.494.704.494 1.26 0 .93-.39 2.093-1.17 3.488-.765 1.38-2.241 3.457-4.431 6.232l-2.227.156-1.711-9.628h-2.25V7.24c.6-.195 1.305-.406 2.115-.63.81-.24 1.358-.36 1.643-.36Z"/>
</svg>
...

이렇게 한개만 들어가 있어도 긴 코드가 컴포넌트 하나에 1억개... 또 다른 컴포넌트에 1억개씩 더 있다고 상상을 해보면 정말 끔찍하다.
컴포넌트에 아이콘만 들어가는게 아니라 함수도 들어가 있을 것이고, UI를 그리는 다른 코드들도 존재 할텐데 아이콘만을 위한 코드가 이렇게 주인공인것 마냥 들어가있으면 보기에 되게 불편할 것이다.

React가 컴포넌트에 최적화된 라이브러리인만큼 SVG도 컴포넌트로 만들어 보면 어떨까?

아이콘들을 보다보면 role, viewBox, 그리고 xmlns 같은 속성들이 동일할 것이다. 나머지 fill, width, height, <title>, 그리고 path 등 속성은 우리의 입맛에 맞추어 설정을 하는 것은 그렇게 어렵진 않다.

SVG 컴포넌트 만들기

  1. 우선 SVG.js 파일을 따로 만들어준다.

  2. 컴포넌트를 만들 때처럼 SVG 함수를 만들어 준다.

  3. 우리의 입맛에 맞게 만들어 줄 속성들을 전달받기 위해 props를 넣어준다.

  4. 이제 공통된 속성들과 전달해줄 값들을 넣어줄 자리를 만들어준다.
    나는 아이콘의 크기를 일정하게 맞추고, 색깔은 각자 맞춰줄 것이기 때문에 아래 코드와 같이 입력해 주었다.
    (참고로 인라인 svg코드에 xmlns는 필요하지 않기 때문에 생략하여도 된다.)

import React from 'react';
const svg = {
  github:
    'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
}
// 컴포넌트 앱
export const SVG = (props) => {
  return (
    <svg
      width={props.width ? props.width : '20px'}
      height={props.height ? props.height : '20px'}
      fill={props.color}
      role='img'
      viewBox='0 0 24 24'
      <title>{props.name}</title>
      <path d={svg[props.name]} />
    </svg>
  );
}

path는 props로 받아온 name을 이용해서 객체 키에 접근하면 된다.

<SVG name='github' color='#181717'></SVG>
// or
<SVG name='github' width='10px' height='10px' color='#181717'></SVG>

이런식으로 props로 전달해주면 된다.

응용하기 (feat. useReducer)

포트폴리오를 만들면서 SVG 컴포넌트를 처음 사용하게 되었다. 포트폴리오에 나의 깃허브, 블로그, 이메일 등등 나에 대해 더 알 수 있도록 아이콘을 만들었는데 조금 더 재미있는 요소를 추가하고 싶었다. 모든 SVG 아이콘 색깔을 통일한 다음, 아이콘에 마우스를 올리면 해당 로고 색깔로 바뀌도록 하고 싶었다.

그러기 위해서 useReducer로 상태값(아이콘 색깔)을 관리하였다.

// useReducer의 initArg부분
export const initIconColor = {
  githubColor: #524f4a,
  velogColor: #524f4a,
  gmailColor: #524f4a,
};
// useReducer의 reducer부분
export const iconColorReducer = (state, action) => {
  switch (action.type) {
    case 'GITHUB':
      return {
        ...state,
        githubColor: palette.githubColor,
      };
      break;
    case 'VELOG':
      return {
        ...state,
        velogColor: palette.velogColor,
      };
      break;
    case 'GMAIL':
      return {
        ...state,
        gmailColor: palette.gmailColor,
      };
      break;
    case 'DEFAULT':
      return {
        ...state,
        githubColor: palette.subFontColor,
        velogColor: palette.subFontColor,
        gmailColor: palette.subFontColor,
      };
      break;
    default:
      break;
  }
};

마우스를 아이콘에 올려서 onMouseEnter 이벤트가 발생하면 각각 아이콘 로고에 맞는 색상으로 변경 되어야 하며, 마우스를 내렸을 때는 onMouseLeave 이벤트가 실행되어 case DEFAULT가 실행 되어야 한다.
이때 예를 들어서 깃허브 아이콘에 마우스를 올렸는데 나머지 두개도 같이 색상이 바뀌면 안되기 때문에 스프레드 구문(...state)을 사용하였다. 즉, 깃허브 아이콘에 마우스를 올렸을 때 깃허브 아이콘만 색상이 변해야 하고, 나머지 아이콘은 초기 색상 그대로 유지되어야 한다.

ex) 깃허브 아이콘

// useReducer 가져오기
const [iconColor, setIconColor] = useReducer(iconColorReducer, initIconColor);
// ui 부분
<a
  href='https://github.com/04ian80'
  rel='noopener noreferrer'
  target='_blank'
  onMouseEnter={() => {
    setIconColor({ type: 'GITHUB' });
  }}
  onMouseLeave={() => {
    setIconColor({ type: 'DEFAULT' });
  }}

  <SVG name='github' color={iconColor.githubColor} />
</a>

여기서 상태값은 iconColor이기 때문에 iconColor안에 각 아이콘 색상을 SVG 컴포넌트의 color로 넣어주면 된다.

마무리

처음에는 HTML에서 사용했던 것과 같이 인라인코드로 작성했었는데 컴포넌트로 만들어보니 너무 간단하고, 보기 편하고, 재사용이 가능하다는 점이 마음에 들었다.

profile
프론트엔드 개발 연습장 ✏️ 📗

0개의 댓글