SVG를 CSS Background Image로 사용하기

soheekim·2025년 1월 4일

트러블슈팅

목록 보기
3/3

SVG를 CSS Background Image로 사용하기

배경

  • 프로젝트에서 React + SVG icon 라이브러리를 사용하는 중이었다.
  • SVG 아이콘 라이브러리는 컴포넌트로 아이콘을 불러와 사용하는데, select 컴포넌트의 화살표 아이콘처럼 background-image로 활용해야 하는 경우에는 적합하지 않았다. 브라우저의 기본 select 화살표를 숨기고(appearance: none) 커스텀 아이콘을 표시하기 위해서는 CSS background-image를 사용하는 것이 더 적절하다.
  • 이미지 파일을 assets 폴더에 추가한 다음 background-image로 넣어도 되긴 하지만 오직 select 만을 위한 이미지 에셋을 하나 추가함으로써 깨끗했던 폴더를 더럽히고(?) 싶지 않았다. 아직까지는 프로젝트에 background-image가 필요한 경우가 select 이외에는 없기 때문이다.
  • 폴더를 더럽히고 싶지 않다는 이유에는 설득력이 좀 없는 것 같아서 더 말이 되는 근거를 찾아 봤다. 번들 크기 최적화, 유지보수성, 동적 스타일링 관점에서도 svg를 이미지 대신 코드로 관리하는 게 좋다고 한다. 근사한 이유를 찾아서 기쁘다. 야호!
    • 번들 크기 최적화: 외부 이미지 파일은 추가적인 네트워크 요청이 필요하고 번들 사이즈를 증가시킨다.
    • 유지보수성: SVG 코드를 컴포넌트 내부에 직접 포함시키면 아이콘 관련 로직이 한 곳에 모여있어 수정이 용이하다.
    • 동적 스타일링: 코드로 관리하면 SVG의 색상이나 크기를 동적으로 변경하기 쉽다.
      (나의 경우에는 Select에 focus가 되었을 때 아이콘을 위아래로 뒤집겠다고 하면 유효한 근거인 듯)

SVG 인코딩 유틸리티

// utils/svg.ts
export const encodeSvgForCss = (svg: string) => {
  return encodeURIComponent(svg)
    .replace(/%3D/g, '=')
    .replace(/%3A/g, ':')
    .replace(/%2F/g, '/')
    .replace(/%22/g, "'")
    .replace(/%20/g, ' ');
};

사용 예시

import { encodeSvgForCss } from '@/utils/svg';

const svgCode = `<svg xmlns="http://www.w3.org/2000/svg" ...></svg>`;

const svgBg = {
  '--svg-bg': `url("data:image/svg+xml, ${encodeSvgForCss(svgCode)}")`,
  backgroundColor: '#FFFBEB',
} as React.CSSProperties;

export default function Component() {
  return (
    <div
      style={svgBg}
      className={cn('bg-[image:var(--svg-bg)] bg-no-repeat bg-[right_8px_center]')}
    />
  );
}

주의사항

  1. SVG 코드는 문자열로 정의해야 함
  2. CSS 변수를 통해 background-image 설정
  3. background-position, background-repeat 등의 속성은 tailwind 클래스로 적용 가능

내가 작성한 코드 예시

import { SelectHTMLAttributes } from 'react';
import { encodeSvgForCss } from '@/utils/svg';
import { cn } from '@/utils/cn';

interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
  options: { value: string; label: string }[];
}

const svgCode = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#85582B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>`;

const svgBg = {
  '--svg-bg': `url("data:image/svg+xml, ${encodeSvgForCss(svgCode)}")`,
  backgroundColor: '#FFFBEB',
} as React.CSSProperties;

export default function Select({ options, value, onChange, className, ...props }: SelectProps) {
  return (
    <select
      value={value}
      onChange={onChange}
      style={svgBg}
      className={cn(
        'w-full h-10 px-3 pr-8 rounded-lg border border-input-border text-sm text-pollloop-brown-01 focus-visible:outline-pollloop-brown-01 appearance-none bg-[image:var(--svg-bg)] bg-no-repeat bg-[right_8px_center]',
        className,
      )}
      {...props}
    >
      {options.map(option => (
        <option key={option.value}>{option.value}</option>
      ))}
    </select>
  );
}

0개의 댓글