-
[svgr] SVG 아이콘 컴포넌트 생성 :: 마이구미알아두면 좋은 라이브러리 2022. 1. 28. 22:56반응형
이 글은 svg 아이콘과 관련된 라이브러리 SVGR 을 다룬다.
리액트에서 아이콘을 svg 파일로 관리하는 경우 도움을 줄 수 있는 라이브러리이다.
글에서 소개하는 예제들은 각 tag 를 checkout 을 통해 확인할 수 있다.
예제 - https://github.com/hotehrud/svgr-example
조금이나마 도움을 줄 수 있는 라이브러리들을 소개하는 카테고리로 분류된 글이다.
알아두면 좋은 라이브러리SVGR 은 svg 파일을 React 컴포넌트로 변환시켜주는 라이브러리이다.
원본 svg 와 svg 를 React 컴포넌트로 변환한 코드를 각각 확인해보자.
// icon.svg <?xml version="1.0" encoding="utf-8"?> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" xml:space="preserve"> <path d="M22,11h-4.2L21,7.8l-1.4-1.4L15,11h-2V9l4.7-4.7l-1.4-1.4L13,6.2V2h-2v4.2L7.8,2.9L6.3,4.3L11,9v2H9L4.3,6.3 L2.9,7.8L6.2,11H2v2h4.2L3,16.2l1.4,1.4L9,13h2v2l-4.7,4.7l1.4,1.4l3.3-3.3V22h2v-4.2l3.2,3.2l1.4-1.4L13,15v-2h2l4.7,4.7l1.4-1.4 L17.8,13H22V11z"/> </svg>
// Icon.tsx import * as React from "react"; const SvgAc = (props) => ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" xmlSpace="preserve" {...props} > <path d="M22 11h-4.2L21 7.8l-1.4-1.4L15 11h-2V9l4.7-4.7-1.4-1.4L13 6.2V2h-2v4.2L7.8 2.9 6.3 4.3 11 9v2H9L4.3 6.3 2.9 7.8 6.2 11H2v2h4.2L3 16.2l1.4 1.4L9 13h2v2l-4.7 4.7 1.4 1.4 3.3-3.3V22h2v-4.2l3.2 3.2 1.4-1.4L13 15v-2h2l4.7 4.7 1.4-1.4-3.3-3.3H22v-2z" /> </svg> ); export default SvgAc;
svg 파일은 svg 태그로 구성되어있고, React 컴포넌트는 랜더링을 svg 태그로 구성하고 있는 모습을 볼 수 있다.
그렇다면 왜 svg 를 React 컴포넌트로 변환해야하는가?
svg 를 활용해서 아이콘을 관리 및 사용하거나 아이콘 라이브러리를 쓰면서 커스텀 아이콘이 필요한 경우가 있다.
이러한 경우에는 <img src="icon.svg" /> 가 아닌 <Icon /> 형태를 더 선호할 것이다.
const Car = () => { return ( <svg ... /> ) } const App = () => { return ( <div> <Car /> <div> ) }
결국 우리는 React 컴포넌트를 작성하고 svg 태그를 입력하는 수동적인 작업이 필요한 것이다.
이러한 수동적인 작업을 svgr 에서 대신 해주는 것이다.
svg 확장자 파일을 jsx 확장자 파일로 생성해주는 것이다.
- svg/car.svg => components/Ac.jsx
- svg/airplane.svg => components/Airplane.jsx
- svg/heart.svg => components/Heart.jsx
원하는 아이콘 파일(svg)을 svg 디렉토리에 넣은 후, svgr 명령어를 실행해주면 된다.
$ svgr svg -d src/components
svg 디렉토리에 있는 svg 파일들을 기반으로 components 디렉토리에 React 컴포넌트가 생성된다.
그리고 생성된 컴포넌트들을 export 하는 index 파일까지 생성된다.
export { default as Ac } from "./Car"; export { default as Accessible } from "./Airplane";
위의 예제는 태그 01-basic 를 통해 확인할 수 있다.
그리고 svgr 을 활용해서 더 많은 기능들이 가능해진다.
여기서는 2가지 기능을 추가해본다.
- svg title 요소 넣기
- svg 를 emotion/styled 로 감싸기
우선 svg title 기능을 넣어보자.
svg 에서는 title 요소를 제공해준다. (https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title)
title 의 값이 마우스오버를 하면 툴팁처럼 보여지게 된다.
사전에 svg 파일에서 title 요소를 넣을 수도 있겠지만, title 의 값을 props 로 전달 받아야한다면 어떻게 할 수 있을까?
svgr 을 통해 생성되는 React 컴포넌트를 수정해야한다.
props 에 title 을 추가해야하고 랜더링 코드쪽에서는 <title>{props.title}</title> 같은 코드가 존재해야한다.
const SvgAirplane = ({ title, ...props }: SVGProps<SVGSVGElement> & { title: string, }) => { return ( <svg xmlns="http://www.w3.org/2000/svg" {...props} > {!!title && <title>{title}</title>} <path d="m6.568 4.675 6.302 2.76-2.228 2.326-4.78-4.224a.25.25 0 0 1-.042-.387l.44-.44a.251.251 0 0 1 .308-.035Zm10.403 6.847 2.76 6.315a.252.252 0 0 1-.036.309l-.44.439a.25.25 0 0 1-.387-.042l-4.23-4.8 2.333-2.221ZM6.84 15.22l-2.692-2.262a.25.25 0 0 1-.074-.404l.56-.56a.25.25 0 0 1 .31-.034l3.147.82-1.16 2.249c-.028.056-.064.12-.091.19Zm4.794 1.102.894 3.183c.024.087 0 .18-.064.245l-.598.596a.25.25 0 0 1-.404-.072l-2.275-2.707.17-.085 2.277-1.16Zm8.426-12.025.041.044.017.02c.22.273.25.64.266.864.074 1.216-.075 2.074-.47 2.698l-.036.06a1.713 1.713 0 0 1-.327.407l-7.48 7.127-.054.051-.065.033-2.817 1.437c-.346.188-1.266.442-1.737-.03-.51-.509-.192-1.438-.028-1.748l1.435-2.807.033-.065.05-.052 7.125-7.479c.303-.322.633-.48.951-.606.637-.24 1.447-.307 2.566-.205a.898.898 0 0 1 .53.251Zm-1.172 7.585a.248.248 0 0 1 .354 0l1.06 1.061a.247.247 0 0 1 0 .353l-1.385 1.386-.87-1.959.841-.84ZM11.11 4.104a.248.248 0 0 1 .354 0l1.06 1.061a.247.247 0 0 1 0 .353l-.842.841-1.958-.869 1.386-1.386Z" /> </svg> ); };
이것이 의미하는 것은 svgr 에서 자동으로 변환해주는 템플릿을 우리가 직접 커스텀 할 수 있어야하는 것이다.
이것을 위해 svgr 에서는 Custom Template 기능을 제공해주고 있다.
const template = (variables, { tpl }) => { return tpl` ${variables.imports}; ${variables.interfaces}; const ${variables.componentName} = (${variables.props}) => ( ${variables.jsx} ); ${variables.exports}; ` } module.exports = template
svgr.config.js 파일을 생성해서 기능에 대한 커스텀 및 확장을 할 수 있다.
const template = ({ componentName, jsx, props, imports, exports }, { tpl }) => tpl` ... `; module.exports = { template, jsx: { babelConfig: { plugins: [dynamicTitlePlugin], }, } };
템플릿을 우리가 원하는 형태로 작성하고, 랜더링하는 부분에 title 태그를 삽입하는 플러그인을 선언해주었다. (예제 코드 참고)
svg title 기능이 적용된 예제 코드는 태그 02-svg-title 를 통해 확인할 수 있다.
이처럼 커스텀이 자유롭다면 조금 더 나아가서 emotion/styled 로 랩핑하여 style Props 를 활용할 수 있다.
이것을 통해 얻는 이점은 일반적인 svg 태그가 아닌 Styled Component 처럼 사용하는 것이다.
그렇게 되면, svg 태그만이 가질 수 있는 속성 이외의 theme 과 같은 style Props 들을 이용할 수 있게 된다.
<Car color="primary" />
위처럼 사용하기 위해서는 몇가지 아이디어가 필요하다.
우선 Styled Component 로 제작된 컴포넌트(Svg.tsx)를 하나 더 생성해준다.
const color = (props) => { if (props.color) { return css` color: props.theme[props.color].base; ` } return null } const Svg = styled('svg')` flex: none; line-height: 1; ${color} ` export default Svg
그리고 svgr 을 통해 변환된 React 컴포넌트의 <svg ... /> 코드를 <Svg ... /> 로 변경해주어야한다.
이것을 위한 플러그인을 추가해준다. (예제 코드 참고)
// svgr.config.js module.exports = { template, jsx: { babelConfig: { plugins: [dynamicTitlePlugin, replaceSvgPlugin], }, } };
결과적으로 변환된 React 컴포넌트의 랜더링 부분은 svg 태그가 아닌 Styled Component 인 Svg 로 대체되는 것이다.
styled Compoent 가 적용된 예제는 태그 03-svg-emotion 를 통해 확인할 수 있다.
사실 CRA(Create-react-app) 를 사용해봤다면, 우리는 다음과 같은 코드를 볼 수 있다.
import { ReactComponent as Logo } from './logo.svg'; function App() { return ( <div> {/* Logo is an actual React component */} <Logo /> </div> ); }
위 코드는 CRA 설치 시 기본적으로 존재하는 코드이다.
svg 파일을 import 했는데 React 컴포넌트처럼 바로 사용하는 모습을 볼 수 있다.
이것이 가능한 이유는 이미 CRA 내부에 기본적으로 svgr 라이브러리를 지원하고 있기 때문이다.
https://create-react-app.dev/docs/adding-images-fonts-and-files/#adding-svgs
실제로는 priceline 디자인 시스템의 아이콘 모듈을 참고하였고, 본인의 예제는 간략하게 정리한 것이다.
더 세세한 코드를 원하면 참고하면 좋을 것 같다.
반응형'알아두면 좋은 라이브러리' 카테고리의 다른 글
[React] react-transition-group :: 마이구미 (1) 2022.04.28 [DOMPurify] XSS 공격 방지 :: 마이구미 (0) 2022.03.31 [detect-port] 사용 가능한 포트 찾기 :: 마이구미 (0) 2022.01.04 [use-memo-one] useMemo/useCallback 개선 :: 마이구미 (0) 2021.11.29 [memoize-one] 캐시를 활용한 라이브러리 :: 마이구미 (0) 2021.11.27