svg를 컴포넌트로 만들어서 사용하자

우빈·2023년 7월 21일
33
post-thumbnail
post-custom-banner

계기

Next.JS 프로젝트에서 퍼블리싱을 진행하던 도중, 선배님께 한 가지의 코드 리뷰를 받았다.

바로 svg를 컴포넌트로 만들어서 사용하면 어떻겠냐는 코드 리뷰였다.
이 당시 svg 파일이 조금 많았었는데, 하나하나 바꾸려고 하니 조금 피곤한 작업인 것 같았다.
그렇다면, 컴포넌트를 사용하지 않은 기존 방식에는 어떤 문제가 있을까?

코드의 가독성이 낮아진다

보통은 Next.JS에서 svg를 사용할 때, 파일로 따로 선언해둔 뒤 이를 모듈로
import한 후, 'next/image'의 Image 컴포넌트에 src로 전달하는 식으로 사용한다.

import Image from "next/image"
import Like from "@/assets/like.svg"

const Component = () => {
	<Image src={Like} alt="좋아요" />
}

이렇게 되면 코드를 해석할 때, Image로 한 번, src 내에 있는 Like로 한 번,
총 두 번에 걸쳐 코드를 해석해야 하며, 넓게 보았을 때, Image라는 컴포넌트명은
어떻게 보면 조금 비명시적일 수 있다.

여기서 사용된 like.svg는 좋아요 버튼 역할을 하는 작은 svg인데, 이를 Image라고
명시하는 것도 문제가 있고, 이에 다른 css 속성을 주려면 스타일드 컴포넌트를 사용해야 한다는 번거로움이 있다.

스타일을 바꿀 수 없다

말 그대로이다. 스타일을 바꾸려면 svg 모듈 자체를 바꾸어야 한다.
좋아요를 눌렀을 때, 누르지 않았을 때 색깔을 바꾸는 코드를, 다음과 같이 구현할 수 있을 것이다.

import React from "react";
import Image from "next/image"
import UnLike from "@/assets/un_like.svg"
import Like from "@/assets/like.svg"

const Component = () => {
	const [isClicked, setIsClicked] = React.useState(false);
  
  	return (
    	<Image src={isClicked ? Like : UnLike} alt="좋아요" />
    )
}

이렇게 되면 두 가지의 svg 파일을 불러와야 하며, 해석해야하는 코드의 길이도 늘어나게 된다.
클린 코드를 짜던 입장에서는 기분이 유쾌하지 않을 수 있는 코드이다.
그럼 이런 문제를 어떻게 해결할 수 있을까?

svg를 컴포넌트로 사용하자

import React from "react";

interface SVGAttributeProps {
	width?: number;
	height?: number;
  	color: string;
}

const LikeIcon = ({ 
  width = 20, 
  height = 20, 
  color = "#727272" 
}: SVGAttributeProps) => {
  return (
    <svg
      width={width}
      height={height}
      viewBox="0 0 233 201"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M166.918..."
        fill={color}
      />
    </svg>
  );
};

export default LikeIcon;

다음과 같이 나타내고, 이를 메인 컴포넌트에서 사용해보자.

import LikeIcon from "@/assets/LikeIcon"

const Component = () => {
	return (
      <LikeIcon width={20} height={20} color="red" />
    )
}

LikeLogo라는 컴포넌트명을 사용함으로 훨씬 가독성이 좋아지고 명시적인 코드가 된다!
props를 통해서 내가 원하는 svg로 커스텀도 가능하기에, 아까처럼 클릭했을 때
색깔이 바뀌어야하는 경우라면 isClicked를 props로 받아 바꾸어줄 수도 있다!!

import React from "react";

interface SVGAttributeProps {
	isClicked: boolean;
}

const LikeIcon = ({ 
	isClicked
}: SVGAttributeProps) => {
  return (
    <svg
      width="20"
      height="20"
      viewBox="0 0 233 201"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path
        d="M166.918..."
        fill={isClicked ? "red" : "gray"}
      />
    </svg>
  );
};

export default LikeIcon;

다음과 같이 svg를 선언하고, 컴포넌트에서 사용할 때 props만 바꾸어준다면
훨씬 깔끔하고 훌륭한 코드를 작성할 수 있다!

화살표의 방향도 바꿀 수 있다

ArrowIcon.tsx

import React from "react";

interface SVGAttirbuteProps {
  direction: "top" | "right" | "bottom" | "left"
}

const path = {
  top: "M1.66419 ...",
  right: "M1.36271 ...",
  bottom: "M40.8592 ...",
  left: "M23.1588 ...",
};

const ArrowIcon = ({ direction }: SVGAttribute) => {
  return (
    <svg
      width={width || 41}
      height={height || 25}
      viewBox="0 0 41 25"
      fill="none"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path d={path[direction]} fill="none" />
    </svg>
  );
};

export default ArrowIcon;

Component.tsx
import ArrowIcon from "@/assets/ArrowIcon"

const Component = () => {
	return (
    	<ArrowIcon direction="bottom" />  
    )
}

이것은 혁신이다!! 방향대로 새로운 svg 모듈을 만들고 import해 사용하는 것이 아니라,
한 svg에서 상하좌우 네 방향의 path만 따로 객체로 만든 뒤 이를 props로 받아
사용하니 코드가 매우 기분이 좋고 깔끔하다!

이 외에도 여러가지 svg들을 입맛에 맞게 커스텀해 사용할 수 있다!

마무리

svg를 컴포넌트로 만드는 좋은 방법에 대해 알아보았다.
선언할 때에 조금의 귀찮음이 있긴 하지만, 이를 공통된 인터페이스나 여러가지 세팅을
통해서 최대한 간소화시켜 클린 코드를 작성하려고 노력해야겠다.

이 글을 쓰면서도 기존에 내가 쓰던 svg 컴포넌트의 코드를 다시 보니
리팩토링할 것들이 보인다..!! 조금 막노동이지만 리팩토링하러 가봐야겠다..!! 화이팅!

profile
프론트엔드 공부중
post-custom-banner

6개의 댓글

comment-user-thumbnail
2023년 7월 21일

유익추

1개의 답글
comment-user-thumbnail
2023년 7월 28일

대박 입니다!

1개의 답글