보통 next에서는 svg 파일을 사용하기 위해서는 @svgr/webpack 을 설치한다.
import Component from './star.svg' -> svg를 컴포넌트로 import하기
import url from './star.svg?url' -> svg를 url로 import하기
1. svg를 컴포넌트로 import 하기
webpack.config.js 혹은 next.config.js 에 @svgr/webpack 을 사용한다고 명시한다.
/**next.config.ts*/
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
},
],
},
}
사용하고 싶은 컴포넌트에서 svg 파일을 import해서 사용한다.
import Star from './star.svg'
const Example = () => (
<div>
<Star />
</div>
)
2. svg를 url로 import하기
만약 svg를 url로써 활용하고 싶다면(컴포넌트가 아님), 가장 쉬운 방법은 resourceQuery를 활용하는것이다.
이전에 작성한 next.config.js 파일에 resourceQuery를 활용해서 “.svg?url”로 끝날 경우에는 문자열로 변환하도록 한다.
/**next.config.ts*/
module.exports = {
module: {
rules: [
{
test: /\.svg$/i,
type: 'asset',
resourceQuery: /url/,// *.svg?url
},
{
test: /\.svg$/i,
issuer: /\.[jt]sx?$/,
resourceQuery: { not: [/url/] },// exclude react component if *.svg?url
use: ['@svgr/webpack'],
},
],
},
}
이걸 다음과 같이 활용할 수 있다.
import svg from './assets/file.svg?url'
import Svg from './assets/file.svg'
const App = () => {
return (
<div>
<img src={svg} width="200" height="200" />
<Svg width="200" height="200" viewBox="0 0 3500 3500" />
</div>
)
}
3. 프로젝트에 적용하기
이걸 프로젝트에 적용하게 된 계기이다.
하단 네비게이션바(GNB)를 구현했다.
여기서 NAV_ITEMS에 4가지(홈, 모임, 랭킹, 프로필)을 저장하고, 배열을 순회하며 4가지 아이템이 고르게 배치되도록 했다.
하지만 마음에 들지 않는 부분이 있었다.
NAV_ITEMS를 별도 constants 파일로 깔끔하게 분리하고 싶었다.
하지만 import 방식, 즉 JSX 구문을 활용하고 있어 constants 확장자가 constants.tsx로 규칙(.tsx) 에 깨지는 게 마음에 들지 않았다.
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import GatheringIcon from "/public/nav/group02.svg";
import HomeIcon from "/public/nav/home.svg";
import RankingIcon from "/public/nav/medal02.svg";
import ProfileIcon from "/public/nav/profile.svg";
const NAV_ITEMS = [
{
label: "홈",
icon: <HomeIcon />,
href: `/home`,
},
{
label: "모임",
icon: <GatheringIcon />,
href: `/gatherings`,
},
{
label: "랭킹",
icon: <RankingIcon />,
href: `/ranking`,
},
{
label: "프로필",
icon: <ProfileIcon />,
href: `/profile`,
},
];
const NAV_ITEM_STYLE = {
default: "text-grey-400",
active: "text-green",
};
export default function Nav() {
const pathname = usePathname();
const pathnameFirst = pathname.split("/")[1];
return (
<div className="shadow-[0px_4px_24px_0px_rgba(170,170,170,0.30)]justify-center fixed bottom-0 mx-auto flex w-96 w-full max-w-[500px] items-end gap-16 bg-white px-10 pt-2.5 pb-5">
{NAV_ITEMS.map((item, index) => (
<Link
key={index}
href={item.href}
className={`flex flex-1 flex-col items-center justify-center gap-0.5 ${item.href === `/${pathnameFirst}` ? NAV_ITEM_STYLE.active : NAV_ITEM_STYLE.default}`}
>
{item.icon}
<div className="text-body3-medium">{item.label}</div>
</Link>
))}
</div>
);
}
따라서 다음과 같이 수정했다.
이전에 설명한 "svg를 url로 import하기" 처럼 next.config.ts를 수정했다.
그다음 상수 파일로 분리하였다.
/** constants/nav.ts */
import homeUrl from "@/assets/icons/home.svg?url";
import gatherUrl from "/public/nav/group02.svg?url";
import rankingUrl from "/public/nav/medal02.svg?url";
import profileUrl from "/public/nav/profile.svg?url";
export const NAV_ITEMS = [
{
label: "홈",
icon: homeUrl,
href: `/home`,
},
{
label: "모임",
icon: gatherUrl,
href: `/gatherings`,
},
{
label: "랭킹",
icon: rankingUrl,
href: `/ranking`,
},
{
label: "프로필",
icon: profileUrl,
href: `/profile`,
},
];
svg 타입 선업하기
typescript에서 svg 파일을 url 형태로 import 시키기 위해선 타입 선언이 필요하므로, 이 부분도 추가했다.
/** types/svg.d.ts */
declare module "*.svg?url" {
const content: string;
export default content;
}
declare module "*.svg" {
import { FC, SVGProps } from "react";
const content: FC<SVGProps<SVGElement>>;
export default content;
}
이로써 관심사별로 분리하여 코드는 깔끔해졌다.
하지만 어이없게도 기존 코드로 돌아갈 수 밖에 없었다.
SVG 색상 변경이 어려웠기 때문이였다.
변경 이후(URL import)
// src/constants/nav.ts
import homeUrl from "@/assets/icons/home.svg?url";
export const NAV_ITEMS = [
{
label: "홈",
icon: homeUrl, // URL string
href: `/home`,
},
// ...
];
// src/components/organisms/Nav.tsx
<Image src={item.icon} /> // SVG를 이미지로 표시
👉 장점: 상수 파일을 .ts로 유지 가능
👉 단점: SVG 색상 변경이 어려움
변경 이전(컴포넌트 import 방식)
import GatheringIcon from "@/assets/icons/group02.svg";
import HomeIcon from "@/assets/icons/home.svg";
import RankingIcon from "@/assets/icons/medal02.svg";
import ProfileIcon from "@/assets/icons/profile.svg";
import Link from "next/link";
import { usePathname } from "next/navigation";
const NAV_ITEMS = [
{
label: "홈",
icon: <HomeIcon />,
href: `/home`,
},
{
label: "모임",
icon: <GatheringIcon />,
href: `/gatherings`,
},
{
label: "랭킹",
icon: <RankingIcon />,
href: `/ranking`,
},
{
label: "프로필",
icon: <ProfileIcon />,
href: `/profile`,
},
];
👉 장점: SVG 색상을 className으로 쉽게 변경 가능
👉 단점: 확장자가 .tsx 여야함
결론
svg 파일을 import 해오는 방법 2가지
- 컴포넌트 형태로 가져오기 => .svg
- url 형태로 가져오기 -> .svg?url
그렇다면 어떤 기준으로 나누어 작성할까?
.svg?url
- 만약 svg 파일 여러개를 constants.ts에 저장하고 싶을 때 활용
- 단 동적으로 색상, 크기 변경해야할 때는 사용하지 않는다.
예) 프로필 이미지가 없을 때, "N개 중 랜덤한 이미지를 보여주기 위해"
- svg
- 한개만 사용하는 경우
- 동적으로 색상, 크기 변경하는 경우