티스토리 뷰
오늘은 svg를 다루면서 불편했던 점과 이를 조금 개선한 결과입니다.
프로젝트 상황이 디자인시스템이 완벽하고 디자이너와 아이콘 네이밍 규칙이 다 정해져 있다면,
그리고 화면에 svg가 많고 성능을 고려한다면 sprite svg파일을 만들어서 use태그로 랜더링 하는 방식을 적용할 수 있을 것 같습니다.
상황에 따라 방식마다 트레이드오프가 존재하는데요,
저의 상황은 타입스크립트가 없이 js로 개발해야하고 아이콘도 수시로 추가되고 제거되며 네이밍도 이제 막 맞춰가는 단계입니다.
이런 상황에서 sprite svg 방식을 취하면 매번 sprite파일을 만들어줘야 하기 때문에 오히려 더 귀찮고 느려질 것입니다.
기존에 저는 svg를 다루기 위해 리액트 컴포넌트로 만들어서 랜더링 하는 방식을 취했습니다.
그냥 svg를 이미지태그로 보여주면 사이즈, 색상별로 다양한 svg가 필요하지만
리액트컴포넌트로 만들면 props로 상황에 맞게 조절해서 사용할 수 있기 때문입니다.
import React from 'react';
const Close = ({ fill, onClick, size }) => {
return (
<svg
fill={fill}
height={size}
width={size}
onClick={onClick}
...
>
<path ...../>
</svg>
);
};
export default Close;
이렇게 사용하다 보니 몇가지 불편함이 느껴졌습니다.
1. 매번 width, height, size, fill, stroke 등. props를 설정하고 props가 들어갈 자리를 만들어주는 일이 반복됐습니다.
2. 컴포넌트가 점점 많아지면서 특정 아이콘이 존재하는지 판단하기가 쉽지 않습니다. 컴포넌트 이름만 다르게 중복되어 작성되는 경우도 빈번하게 생깁니다.
3. 2번과 비슷한 이유로 import할때마다 icons폴더에서 파일명을 하나하나 확인하고 import 해야 했습니다.
4. import 해서 사용한 컴포넌트에서 엘리먼트 이름만 보고는 대체 이게 svg컴포넌트인지 다른 역할을 하는 컴포넌트인지 알 수가 없어서 import문을 확인해야하는 불편함이 있었습니다. 예를들어 위의 예제에서는 svg컴포넌트 이름을 Close라고 했는데, 나중에 가져다 쓸때는 이게 svg의 Close인지.. 뭔가 닫는 역할을하는 컴포넌트인지 알수가 없습니다.
위 불편함을 해결하기 위해 제가 원하는 기능은
1. vscode에서 svg 이름 자동완성 기능
타입스크립트를 사용했다면 props에 keyof 연산자를 활용해서 아주 쉽게 자동완성을 할 수 있지만
자바스크립트 프로젝트에서는 jsdoc이란 것을 사용해서 자바스크립트에서 타입힌트를 줄 수 있었습니다. (참고자료)
2. 매번 props를 보내고 들어갈 자리를 만드는 일을 하지 않기
path를 감싸고 있는 svg태그를 공통으로 반환하도록 작성하면 좋겠습니다.
그렇다면 props이름에 따라 <path />는 적절하게 바뀌어야 하고 <svg></svg>는 항상 공통으로 반환되어야 합니다.
3. svg를 랜더링 하는 컴포넌트는 이름만 보고도 인지하도록 하기
아예 위 역할을 하는 재사용 컴포넌트를 만들어줍니다.
/**
* @typedef {"heart_fill"|"heart"} IconName - 아이콘 이름
*/
/**
* @typedef {object} SvgIconProps
* @property {number} [height=24]
* @property {IconName} name
* @property {number} [width=24]
* @property {string} [stroke='#464C53']
* @property {number} [strokeWidth=2]
* @property {Function} [onClick]
* @property {string} [fill='#464C53']
*/
/**
* @param {SvgIconProps} props - SvgIcon 컴포넌트의 props
* @returns {JSX.Element | null} - SvgIcon 요소 또는 null
*/
const SvgIcon = ({
fill = '#464C53',
height = 24,
name,
onClick,
stroke = '#464C53',
strokeWidth = 2,
width = 24,
...props
}) => {
const icons = {
heart:(
<path
...
/>
),
heart_fill: (
<path
...
/>
)
};
if (!name) return null;
if (!icons[name]) {
throw new Error(`SvgIcon: ${name} is not a valid icon name`);
}
return (
<svg
fill={fill}
height={height}
stroke={stroke}
strokeWidth={strokeWidth}
viewBox="0 0 24 24"
width={width}
onClick={onClick}
{...props}
>
{icons[name]}
</svg>
);
};
export default SvgIcon;
이제 컴포넌트에서 항상 <SvgIcon />을 import 해서 사용합니다.
더 이상 제대로 된 이름을 찾기 위해 폴더를 열어보지 않아도 @typedef 부분에 선언된 스트링이
vscode에 자동완성되어 나옵니다.
무엇보다 마음에 드는 점은 props를 매번 새로 지정해주지 않아도 된다는 점입니다.
위 SvgIcon을 잘 사용하다가 디자인시스템이 어느 정도 구성되면 sprite svg방식을 도입해 보고 성능상의 차이도 다뤄보도록 하겠습니다.
참고자료
https://dev.to/michaliskout/handling-svg-icons-in-react-2cpb
Handling SVG Icons in React
Topics Understanding SVG icons Structure of SVG Icons SVG accessibility Choosing the...
dev.to
https://medium.com/mad-semicolon/do-you-manage-svgs-well-in-react-563cb7188ade
Do you manage SVGs well in React?
A better way to organize SVG icons in your application
medium.com
https://benadam.me/thoughts/react-svg-sprites/
The "best" way to manage icons in React.js
How to remove icons from JavaScript bundles without losing the flexibility of inline SVG
benadam.me
'React' 카테고리의 다른 글
React Compound Pattern - 코드 사용자를 배려하는 패턴 (6) | 2024.04.03 |
---|---|
React Server Component 는 무엇을 해결하기 위한 것일까? (2) | 2024.01.12 |
useReducer, Context 로 복잡한 상태(state) 개선하기 (5) | 2023.11.08 |
비제어 컴포넌트(Uncontrolled Components)로 랜더링 개선 (0) | 2023.07.28 |
react lazy import 이후 발생한 loading chunk failed 에러 해결 (0) | 2023.05.26 |