thumbnail
[React] React에서 svg파일을 사용하는 방법
React
2023.04.26.

svg란?

우리에게 친근한 비트맵 이미지(jpeg, jpg, png)는 사각형의 픽셀이 모여 만들어진 이미지이기 때문에 확대시 깨짐현상이 나타난다.

svg는 웹 친화적인 XML기반 벡터 파일 포맷이다.

장점으로는 벡터 기반으로 각 좌표에 점을 이어서 만들기 때문에 확대를 해도 깨짐현상이 나타나지 않는다! 또, svg 파일은 텍스트를 디자인이 아닌 텍스트 그대로 처리하기 때문에 스크린 리더가 svg 이미지에 포함된 모든 단어를 스캔할 수가 있다. 그래서 웹 페이지를 읽어야할 때 매우 유용하다. (SEO에 좋다)

단점으로는 웹 그래픽에 적합하지, 픽셀이 부족하므로 고품질 디지털 사진을 표현하는데는 한계가 있다. 디테일이 풍부한 사진에는 jpeg 파일이 더 좋다. 그리고 최신 브라우저에서만 svg이미지를 지원하므로 옛 브라우저는 사용하기 어려울 수 있다!

React에서 svg파일을 사용해보자

먼저 svg 파일을 확인해보자!

참고 : 피그마에서 svg를 따왔는데 이 파일은 png이미지를 svg로 감싼 파일이라 확대시 이미지가 깨질 수 있다..!

<!--hana.svg-->

<svg width="37" height="37" viewBox="0 0 37 37" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="18.5" cy="18.5" r="18.5" fill="url(#pattern1)"/>
<defs>
<pattern id="pattern1" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_423_577" transform="scale(0.0149254)"/>
</pattern>
<image id="image0_423_577" width="67" height="67" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>

보다시피 xml으로 되어있다. 쉽게 말해 이미지가 코드로 표현되었다고 보면 된다.

// src/svg/index.ts

export { ReactComponent as BC } from './bc.svg';
export { ReactComponent as HANA } from './hana.svg';
export { ReactComponent as HYUNDAI } from './hyundai.svg';
export { ReactComponent as KAKAOBANK } from './kakaobank.svg';
export { ReactComponent as KUKMIN } from './kukmin.svg';
export { ReactComponent as LOTTE } from './lotte.svg';
export { ReactComponent as SINHAN } from './sinhan.svg';
export { ReactComponent as WOORI } from './woori.svg';

위처럼 ReactComponent를 이용하여 svg를 컴포넌트로 만들어 줄 수가 있다! 페이먼츠 미션에서 나는 이렇게 사용했다.

// CardCompanyButton.tsx

import { BC, HANA, HYUNDAI, KAKAOBANK, KUKMIN, LOTTE, SINHAN, WOORI } from '../svg';

const CARD_COMPANY_LOGO = {
  BC카드: <BC />,
  신한카드: <SINHAN />,
  카카오뱅크: <KAKAOBANK />,
  현대카드: <HYUNDAI />,
  우리카드: <WOORI />,
  롯데카드: <LOTTE />,
  하나카드: <HANA />,
  국민카드: <KUKMIN />,
} as const;

추가 tip

위 svg파일에 보면 width, height 등 속성이 있는데, 속성 값을 'current'로 바꿔준다면 바꿔준 속성은 사용하는 컴포넌트에서 props로 custom할 수 있다고 한다!

위의 내용은 잘못된 정보다.

Create-React-App 으로 프로젝트를 생성하면 react-app-env.d.ts 파일이 생성된다. <reference types="react-scripts"/>한 줄이 있는데, react-script에 들어가보면 svg에 관련된 선언코드가 있다.

// react-script/lib/react-app.d.ts

declare module '*.svg' {
  import * as React from 'react';

  export const ReactComponent: React.FunctionComponent<React.SVGProps<
    SVGSVGElement
  > & { title?: string }>;

  const src: string;
  export default src;
}

위 선언문을 보면 svg 파일을 ReactComponent로 선언하여 사용할 시 svg 컴포넌트의 타입은 React.FunctionComponent<React.SVGProps<SVGSVGElement>> 인 것을 확인할 수 있다. 이 타입을 타고 들어가다보면 SVGAttributes<T> 타입이 있는데 여기에 width, height 등 svg 속성들의 타입이 다 정의 되어있다. 즉, 속성 값을 'current' 로 바꿔주지 않아도 예를들어 <BC width={20}, height={30}/> 이렇게 커스텀을 할 수 있고 동작도 잘한다!

여기서 주의!!

Create-React-App을 사용하여 생성한 프로젝트가 아니라면 webpack에서 file-loader에 대한 설정을 해줘야 사용이 가능하다고 한다!

reference

반갑습니다. 누군가에겐 도움이 되는 글을 쓰도록 노력하겠습니다.
© October.2022 yeopto, Powered By Gatsby.