Next에서 svg를 컴포넌트로 사용하기
저번 테오 스플린트에서 처음으로 svg를 사용해봤다. icon을 사용하려면 보통 Font Awesome을 사용했었는데 설치를 해야한다는 단점과 좀더 얇았으면, 빈칸이 채워져있으면, 하는 아쉬운 점이 있어서 이번 블로그 프로젝트에서도 svg를 사용해보기로 했다!! 다만 저번에는 React였는데 이번에는 Next라는 점이 달랐다. 똑같은 줄 알고 저번과 똑같이 적용하려고 했었는데 안됐다..ㅎㅎ 그래서 이번 포스팅에서는 Next에 svg를 컴포넌트처럼 사용하는 방법에 대해 나열해보려고 한다!
React에서는 svg를 어떻게 사용할까?
일단 먼저 React에서는 svg를 알아보고 Next와의 차이점을 비교해보는게 좋을 것 같다.
1. image 태그 이용하기
제일 간단한 방법은 image처럼 사용하는 것이다. 하지만 이 방법은 커스텀하기가 어렵다.
import Like from 'icon/like.svg'
<img src={Like}>
2. svg를 컴포넌트처럼 이용하기
svg의 색을 바꾸거나 크기를 조절하는 등 내 맘대로 custom 하기 용이하도록 svg를 컴포넌트로 만들어보자.
먼저 svg 파일 내에서 props로 내려줘 변경하고 싶은 부분을 current로 변경해준다. 나는 색상을 변경하고 싶었기 때문에 fill 부분을 current로 변경해주었다.
//like.svg
<svg width="16" height="16" viewBox="0 0 16 16" fill="current">
<path d="M3.612~~~~aaaa" fill="current"/>
</svg>
이후 icon.ts 파일을 만들어 export만 모아 깔끔하게 보이도록 해준다. 개인적으로 이렇게 모아주면 한번에 어떤 svg를 쓰고 있는지, 변수명은 무엇인지 알 수 있어서 선호하는 편이다.
//icon.ts
export { ReactComponent as SearchIcon } from "./search.svg";
그리고 사용하고 싶은 컴포넌트로 가서 import 후 컴포넌트처럼 사용하면된다. 색상이나 길이 등을 변경하고 싶다면 props로 내려주면 된다. 이때 키 값은 아까 current를 지정했던 변수명을 가져오면 원하는 값으로 커스텀을 할 수 있다.
import { ReactComponent as HomeIcon } from "../../asset/icon/home.svg";
<HomeIcon fill={isMain ? "#FFFFFF" : "#6D6D6D"} />
만약 이렇게 적용을 해도 안된다 했을땐 ~~~.d.ts 파일에 아래 코드를 추가해주면 된다. 아래 코드는 Typescript에서 svg 파일을 모듈로 가져오고 해당 svg 파일을 리액트 컴포넌트로 사용하기 위해 타입 정의를 해주는 것이다. CRA로 리액트 프로젝트를 만든 경우에는 처리가 되어있어서 따로 해줄 필요가 없고 vite의 경우 아래 코드를 넣어줘야하는 것 같다.
//cusetom.d.ts
declare module "*.svg" { //SVG 파일이 모듈로 사용될 수 있음을 TypeScript에 선언
import * as React from "react";
export const ReactComponent: React.FunctionComponent<
React.ComponentProps<"svg"> & { title?: string } //SVG 파일을 React 컴포넌트로 사용할 수 있도록 내보냄
>;
}
Next에서 svg를 컴포넌트로 사용하기
@svgr/webpack 웹팩 플러그인을 설치
Next에서 svg를 컴포넌트로 사용하려면 앞서 리액트 방법으로 하면 svg를 읽을 수 없다는 에러가 뜬다. 그래서 @svgr/webpack 웹팩 플러그인을 설치해준다. @svgr/webpack은 svg 파일은 javascript 파일로 변환해 react에서 import해 사용할 수 있도록 컴포넌트 형태로 변환해주는 역할을 한다.
// npm
npm install -D @svgr/webpack
// yarn
yarn add -D @svgr/webpack
next.configue.js 파일 수정
그리고 next.configue.js 파일에 웹팩 설정을 수정해 SVG 파일을 처리하기 위해 @svgr/webpack로더를 추가하는 코드를 추가해준다.
//next.configue.js
webpack: (config) => { // config: 현재의 웹팩 설정 객체를 의미
config.module.rules.push({ // module.rules 배열에 새로운 규칙(rule)을 추가
test: /\.svg$/i, // test: 해당 규칙을 적용할 파일의 정규표현식을 지정
issuer: /\.[jt]sx?$/, // issuer: 해당 로더(loader)를 적용할 모듈을 지정하는 조건을 정의
// : .js, .jsx, .ts, .tsx 파일에 대해 규칙을 적용
use: ["@svgr/webpack"], // use: @svgr/webpack 로더를 사용하여 SVG 파일을 React 컴포넌트로 변환
});
return config; // 변경된 설정(config)을 반환
},
svg를 컴포넌트화
이제 설정은 다 해주었고 svg를 컴포넌트화해보자!! 리액트에서는 svg 파일 자체를 그대로 썼다면 이번에는 컴포넌트 내에 svg 파일 내용을 넣어준다.그리고 프롭스로 받아온 내용들을 변경하고 싶은 곳에 배치해주고 export 까지 해주면 svg를 컴포넌트로 사용할 수 있다.
//IC_Like.tsx
import * as React from "react";
type likeProps = {
width: string;
height: string;
isFill: boolean;
};
const IC_Like = ({ width, height, isFill }: likeProps) => (
<svg
width={width}
height={height}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_1_4868)">
<path
d="M13.926.58001 22 8.20.13Z"
fill={isFill ? "#fc8e8e" : "#dadada"}
/>
</g>
</svg>
);
export default IC_Like;
컴포넌트처럼 import를 해오기
svg를 컴포넌트로 만들어줬으니 이제 사용하고 싶은 곳에서 컴포넌트처럼 import를 해오면 된다. 만약 변경하고 싶은 값이 있다면 props로 값을 내려주면 쉽게 커스텀할 수 있다.
//사용하고 싶은 파일
import IC_Like from "../../../public/icon/Like";
<IC_Like width="2rem" height="2rem" isFill={false} />
만약 타입스크립트를 쓰고 있다면?
타입스크립트를 쓰는 이유는 자바스크립트가 컴파일이 없는 언어라서 실행되어야만 타입에러를 알 수 있는 불편한 점을 보완하기 위한거라고 생각한다. 그래서 any 타입은 최대한 지양해야한다고 생각한다. 근데! svg가 any 타입으로 선언이 되어있다. 아주 불편하기때문에 custom.d.ts 파일을 만들어 아래 코드를 넣어준다. svg의 타입을 지정해주는 코드이다.
// custom.d.ts
declare module "*.svg" {
import React from "react";
const svg: React.FC<React.SVGProps<SVGSVGElement>>;
export default svg;
}
그리고 tsconfig.json 파일에서 방금 만든 custom.d.ts 파일을 추가 해준다. 인덱스가 작을수록 우선순위가 높다.
//tsconfig.json
"include": ["custom.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx"],
글을 마무리하며
단순히 이미지로 사용하면 한번 설정한 속성값을 변경할 수 없고, 변경해야하는 경우에는 이미지 두개로 사용을 해왔었다. svg를 사용하니 text처럼 사용할 수 있어서 아주 편하기도 하고 기존의 방법보다 데이터도 덜 잡아먹는 것 같다. 앞으로도 자주 사용해야지~!
참고자료