React에서 SVG 요리하기
15 Apr 2020React에서 SVG를 사용하는 방법은? 사실 간단합니다.
import React from "react";
const ImageList = () => (
<div>
<ul>
<li>
<img src="[path-to-svg-icon]/desktop.svg" />
</li>
<img src="[path-to-svg-icon]/tablet.svg" />
<li>
<img src="[path-to-svg-icon]/mobile.svg" />
</li>
</ul>
<div>
);
보통 이런식으로 사용하죠.
여기엔 하나의 문제가 있습니다. 모든 아이콘이 전부 desktop/tablet/mobile 총 3가지 옵션이 있다고 생각해 보세요. 아이콘의 개수는 3배가 됩니다. 관리하기 어렵고 불필요하게 많습니다.
이 경우, 같은 아이콘이라면 desktop/tablet/mobile을 하나의 파일로 합쳐 관리하는 것이 편합니다.
<!-- dog_desktop.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<!-- all the paths and shapes for desktop.svg -->
</svg>
<!-- dog_tablet.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<!-- all the paths and shapes for tablet.svg -->
</svg>
<!-- dog_mobile.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8">
<!-- all the paths and shapes for mobile.svg -->
</svg>
이런 식으로 같은 아이콘이 desktop/tablet/mobile 별로 나눠져 있다고 해보겠습니다. 이 경우, 다음처럼 합칠 수 있습니다.
dog.svg
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="desktop" viewBox="0 0 24 24">
<!-- all the paths and shapes for desktop.svg -->
</symbol>
<symbol id="tablet" viewBox="0 0 16 16">
<!-- all the paths and shapes for tablet.svg -->
</svg>
<symbol id="mobile" viewBox="0 0 8 8">
<!-- all the paths and shapes for mobile.svg -->
</svg>
</svg>
자, 이제 React에서 사용하는 문제가 남았네요. React에서는 기본적으로 아래처럼 사용할 수 있습니다.
import React from "react";
const ImageList = () => (
<div>
<ul>
<li>
<img src="[path-to-svg-icon]/dog.svg#desktop" />
</li>
<img src="[path-to-svg-icon]/dog.svg#tablet" />
<li>
<img src="[path-to-svg-icon]/dog.svg#mobile" />
</li>
</ul>
<div>
);
desktop/tablet/mobile은 아이콘의 path에 “#”을 붙여 구분할 수 있습니다.
dog.svg#desktop
이런 식으로요.
하지만 이런 식의 사용 방법은 두 가지 문제가 있습니다.
- path를 string으로 고정하여 사용해서 실제 asset의 path가 변경될 경우, 코드도 함께 변경해 줘야 합니다.
- 사용할 때마다 매번 asset의 path를 하나하나 세며 확인하고 입력해줘야 합니다.
예를들어,
제가 만든 <ImageList />
컴포넌트는 src/components/Main/List/
에 있고,
dog.svg
는 src/assets/icons/
에 있다고 한다면,
제가 src
로 입력해야 하는 경로는 ../../../assets/icons/dog.svg
가 됩니다.
솔직히 정확한 경로를 입력한다는 것이 ../
가 반복될수록, 즉 디렉토리가 깊을수록 참 힘들고 헷갈립니다.
그래서 간단하게 다음과 같은 해결책을 제안합니다.
[path-to-svg-icon]/index.tsx
import React from "react";
const Icons = require("./dog.svg");
interface Props {
size: number;
media: string;
className: string;
}
export const Icon = ({ size, media, className }: Props) => (
<svg className={className} width={size} height={size}>
<use xlinkHref={`${Icons}#${media}`} />
</svg>
);
즉, dog.svg
가 있는 디렉토리 안에 index.tsx
파일을 하나 생성하고, 거기서 <Icon />
컴포넌트를 만들어 제공하는 겁니다.
이렇게 사용하면 index.tsx
가 dog.svg
와 같은 디렉토리에만 있는 한, dog.svg
가 속한 디렉토리가 src/assets/icons
에서 src/icons
로 변경된다 하더라도 문제가 발생하지 않습니다.
또, 복잡한 상대경로를 입력하지 않아도 되죠.