6 min read

Next.js 16 Turbopack์—์„œ SVG ์‚ฌ์šฉํ•˜๊ธฐ

Table of Contents

Next.js 16 Turbopack์—์„œ SVG๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ

Next.js 16์—์„œ๋Š” experimental.turbo ์˜ต์…˜์ด ์ œ๊ฑฐ๋˜์–ด ๊ธฐ๋ณธ๊ฐ’์ด ๋์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ SVG๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next.js ํ”„๋กœ์ ํŠธ์—์„œ SVG ์•„์ด์ฝ˜์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ img ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ react-icons, lucide ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ, ์ด ๋ฐฉ๋ฒ•๋“ค์€ ๊ฐ๊ฐ์˜ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • img ํƒœ๊ทธ: CSS๋กœ ์ƒ‰์ƒ ๋ณ€๊ฒฝ์ด ์–ด๋ ต๊ณ , ๋งค๋ฒˆ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์•„์ด์ฝ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ: ์ปค์Šคํ…€ ๋””์ž์ธ ์‹œ์Šคํ…œ์˜ ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Next.js ์˜ Turbopack๊ณผ @svgr/webpack์„ ์‚ฌ์šฉํ•˜์—ฌ SVG ํŒŒ์ผ์„ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , TypeScript์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•œ ํƒ€์ž… ์•ˆ์ „ํ•œ ์•„์ด์ฝ˜ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.


๋ฌธ์ œ ์ƒํ™ฉ

Next.js 16๋ถ€ํ„ฐ ๊ธฐ๋ณธ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ Turbopack์œผ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Turbopack์—์„œ SVG๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Failed to parse svg source code for image dimensions

Caused by:
- Source code does not contain a <svg> root element

์ด๋Š” Turbopack์ด SVG ํŒŒ์ผ์„ ์ œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•ด์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.


Step 1: next.config.ts ์„ค์ •

@svgr/webpack ์„ค์น˜

๋จผ์ € ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค:

npm install -D @svgr/webpack

Turbopack ์„ค์ •

Next.js ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, Turbopack์—์„œ Webpack ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด as: '*.js' ์˜ต์…˜์„ ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  reactCompiler: true,

  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',  // ์ด ์˜ต์…˜์ด ์—†์œผ๋ฉด ์—๋Ÿฌ ๋ฐœ์ƒ!
      },
    },
  },
};

export default nextConfig;

์„ค์ • ์ƒ์„ธ ์„ค๋ช…

  1. turbopack.rules: Turbopack์˜ ํŒŒ์ผ ๋ณ€ํ™˜ ๊ทœ์น™์„ ์ •์˜ํ•˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. Webpack์˜ module.rules์™€ ์œ ์‚ฌํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  2. '*.svg': ๋ชจ๋“  .svg ํ™•์žฅ์ž๋ฅผ ๊ฐ€์ง„ ํŒŒ์ผ์— ๋Œ€ํ•ด ์ด ๊ทœ์น™์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. loaders: ['@svgr/webpack']: SVG ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•  ๋กœ๋”๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. @svgr/webpack์€ SVG๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋กœ๋”์ž…๋‹ˆ๋‹ค.
  4. as: '*.js' (์ค‘์š”!): ์ด ์˜ต์…˜์ด ๊ฐ€์žฅ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ณ€ํ™˜๋œ SVG๋ฅผ JavaScript ๋ชจ๋“ˆ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก Turbopack์— ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.
// as: '*.js'๊ฐ€ ์—†์œผ๋ฉด
// Turbopack์ด ๋ณ€ํ™˜๋œ ๊ฒฐ๊ณผ๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ• ์ง€ ๋ชฐ๋ผ์„œ ์—๋Ÿฌ ๋ฐœ์ƒ

// as: '*.js'๊ฐ€ ์žˆ์œผ๋ฉด
// SVG โ†’ React Component โ†’ JavaScript Module๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ณ€ํ™˜

๊ฒฐ๊ณผ์ ์œผ๋กœ ์—ฌ๊ธฐ๊นŒ์ง€ ์„ค์ •ํ•˜๋ฉด Next.js 16 Tuebopack์—์„œ SVGR์„ ์ด์šฉํ•ด SVG ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Step 2: ์•„์ด์ฝ˜ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

๋‹จ์ˆœํžˆ SVG๋ฅผ importํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ๋” ๋‚˜์€ ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜์„ ์œ„ํ•ด ์•„์ด์ฝ˜ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

์•„๋ž˜์™€ ๊ฐ™์€ ํŒŒ์ผ๊ตฌ์กฐ๋กœ svg๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

src/
  shared/
    assets/
      icons/
        โ”œโ”€โ”€ arrow_left.svg
        โ”œโ”€โ”€ chart.svg
        โ”œโ”€โ”€ comment.svg
        โ”œโ”€โ”€ dashboard.svg
        โ””โ”€โ”€ index.ts  // ํ•ต์‹ฌ ํŒŒ์ผ

icons/index.ts ๊ตฌํ˜„

// src/shared/assets/icons/index.ts

// 1. ๋ชจ๋“  ์•„์ด์ฝ˜์„ import
import ArrowLeft from './arrow_left.svg';
import Chart from './chart.svg';
import Comment from './comment.svg';
import Dashboard from './dashboard.svg';

// 2. ์•„์ด์ฝ˜ ๊ฐ์ฒด๋กœ export (const assertion ์‚ฌ์šฉ)
export const Icons = {
  ArrowLeft,
  Chart,
  Comment,
  Dashboard,
} as const;

// 3. ์•„์ด์ฝ˜ ์ด๋ฆ„ ํƒ€์ž… ์ž๋™ ์ถ”๋ก 
export type IconName = keyof typeof Icons;

์ฝ”๋“œ ์ƒ์„ธ ์„ค๋ช…

1. ๊ฐœ๋ณ„ import

๊ฐ SVG ํŒŒ์ผ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ importํ•ฉ๋‹ˆ๋‹ค. @svgr/webpack์ด ์ด๋“ค์„ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

import ArrowLeft from './arrow_left.svg';

2. Icons ๊ฐ์ฒด with as const

export const Icons = {
  ArrowLeft,
  Chart,
  // ...
} as const;

as const์˜ ์—ญํ• :

  • TypeScript์—๊ฒŒ ์ด ๊ฐ์ฒด์˜ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž„์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค
  • ๊ฐ ํ”„๋กœํผํ‹ฐ์˜ ํƒ€์ž…์ด ๋” ์ •ํ™•ํ•˜๊ฒŒ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค
  • IconName ํƒ€์ž…์ด ์ •ํ™•ํ•œ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์œผ๋กœ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค
// as const ์—†์œผ๋ฉด
type IconName = string  // ๋„ˆ๋ฌด ๊ด‘๋ฒ”์œ„

// as const ์žˆ์œผ๋ฉด
type IconName = "ArrowLeft" | "Chart" | "Comment" | "Dashboard" | ... // ์ •ํ™•

3. IconName ํƒ€์ž…

export type IconName = keyof typeof Icons;

์ด๋Š” TypeScript์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์„ ํ™œ์šฉํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค:

  • typeof Icons: Icons ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค
  • keyof: ๊ฐ์ฒด์˜ ๋ชจ๋“  ํ‚ค๋ฅผ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค

๊ฒฐ๊ณผ:

type IconName = "ArrowLeft" | "Chart" | "Comment" | "Dashboard" | ...

ํƒ€์ž… ์•ˆ์ „์„ฑ์˜ ์žฅ์ 

1. ์ž๋™ ์™„์„ฑ ์ง€์›

// IDE๊ฐ€ ์ž๋™์œผ๋กœ ์•„์ด์ฝ˜ ๋ชฉ๋ก์„ ๋ณด์—ฌ์คŒ
const icon: IconName = 'ArrowLeft';  // ์ž๋™์™„์„ฑ!

2. ์ปดํŒŒ์ผ ํƒ€์ž„ ์—๋Ÿฌ ๊ฐ์ง€

// ํƒ€์ž… ์—๋Ÿฌ: 'InvalidIcon'๋Š” IconName์ด ์•„๋‹˜
const icon: IconName = 'InvalidIcon';  

// ํƒ€์ž… ์—๋Ÿฌ: ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ‚ค
const Icon = Icons['WrongIcon'];

3. ๋ฆฌํŒฉํ† ๋ง ์•ˆ์ „์„ฑ

์•„์ด์ฝ˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๊ณณ์—์„œ TypeScript ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ, ๋†“์น˜๋Š” ๋ถ€๋ถ„ ์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ ๋ฆฌํŒฉํ† ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ

๋ฐฉ๋ฒ• 1: ์ง์ ‘ ์‚ฌ์šฉ

import { Icons } from '@/shared/assets/icons';

function MyComponent() {
  return (
    <div>
      <Icons.ArrowLeft 
        width={20} 
        height={20} 
        fill="currentColor" 
      />
      <span>๋’ค๋กœ๊ฐ€๊ธฐ</span>
    </div>
  );
}

๋ฐฉ๋ฒ• 2: ๋™์  ์‚ฌ์šฉ (ํƒ€์ž… ์•ˆ์ „)

import { Icons, type IconName } from '@/shared/assets/icons';

interface NavItem {
  icon: IconName;  // ํƒ€์ž… ์•ˆ์ „!
  title: string;
  url: string;
}

const navItems: NavItem[] = [
  { icon: 'Dashboard', title: '๋Œ€์‹œ๋ณด๋“œ', url: '/dashboard' },
  { icon: 'Chart', title: 'ํ†ต๊ณ„', url: '/statistics' },
  // { icon: 'Wrong', ... }  // ํƒ€์ž… ์—๋Ÿฌ
];

function Navigation() {
  return (
    <nav>
      {navItems.map((item) => {
        const IconComponent = Icons[item.icon];  // ํƒ€์ž… ์•ˆ์ „
        
        return (
          <a key={item.url} href={item.url}>
            <IconComponent width={20} height={20} />
            <span>{item.title}</span>
          </a>
        );
      })}
    </nav>
  );
}

์•„์ด์ฝ˜ ์ถ”๊ฐ€ํ•˜๊ธฐ

1๋‹จ๊ณ„: SVG ํŒŒ์ผ ์ถ”๊ฐ€

src/shared/assets/icons/new-icon.svg

2๋‹จ๊ณ„: index.ts์— ์ถ”๊ฐ€

// 1. import ์ถ”๊ฐ€
import NewIcon from './new-icon.svg';

// 2. Icons ๊ฐ์ฒด์— ์ถ”๊ฐ€
export const Icons = {
  // ... ๊ธฐ์กด ์•„์ด์ฝ˜๋“ค
  NewIcon,  // ์—ฌ๊ธฐ์— ์ถ”๊ฐ€
} as const;

3๋‹จ๊ณ„: ๋ฐ”๋กœ ์‚ฌ์šฉ

<Icons.NewIcon width={20} height={20} />

// ๋˜๋Š”
const item: NavItem = {
  icon: 'NewIcon',  // ํƒ€์ž… ์ฒดํฌ
  title: '์ƒˆ ๊ธฐ๋Šฅ',
  url: '/new',
};

์žฅ๋‹จ์  ๋น„๊ต

์žฅ์ 

  1. ํƒ€์ž… ์•ˆ์ „์„ฑ: ์ปดํŒŒ์ผ ํƒ€์ž„์— ์˜คํƒ€ ๋ฐฉ์ง€
  2. ์ž๋™ ์™„์„ฑ: IDE ์ง€์›์œผ๋กœ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ ํ–ฅ์ƒ
  3. ๋ฒˆ๋“ค ์ตœ์ ํ™”: ์‚ฌ์šฉํ•˜๋Š” ์•„์ด์ฝ˜๋งŒ ๋ฒˆ๋“ค์— ํฌํ•จ
  4. ์Šคํƒ€์ผ ์ œ์–ด: CSS๋กœ ์ƒ‰์ƒ, ํฌ๊ธฐ ์ž์œ ๋กญ๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
  5. ๊ด€๋ฆฌ ์šฉ์ด: ์ค‘์•™ ์ง‘์ค‘์‹ ์•„์ด์ฝ˜ ๊ด€๋ฆฌ
  6. ๋ฆฌํŒฉํ† ๋ง ์•ˆ์ „: ์ด๋ฆ„ ๋ณ€๊ฒฝ ์‹œ ๋ชจ๋“  ์‚ฌ์šฉ์ฒ˜ ์ถ”์  ๊ฐ€๋Šฅ

๋‹จ์ 

  1. ์ดˆ๊ธฐ ์„ค์ •: ์„ค์ •์ด ๋‹ค์†Œ ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Œ
  2. ๋นŒ๋“œ ์‹œ๊ฐ„: SVG ๋ณ€ํ™˜์œผ๋กœ ๋นŒ๋“œ ์‹œ๊ฐ„ ์ฆ๊ฐ€

๋งˆ์น˜๋ฉฐ

Next.js 16์˜ Turbopack์—์„œ SVG๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ, TypeScript๋ฅผ ํ™œ์šฉํ•œ ํƒ€์ž… ์•ˆ์ „ํ•œ ์•„์ด์ฝ˜ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ํฌ์ธํŠธ๋ฅผ ์ •๋ฆฌํ•˜๋ฉด

  1. as: '*.js' ์˜ต์…˜์€ Turbopack์—์„œ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค
  2. **as const**์™€ **keyof typeof**๋กœ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค
  3. ์ค‘์•™ ์ง‘์ค‘์‹ ๊ด€๋ฆฌ๋กœ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค

์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„์ด์ฝ˜ ๊ด€๋ฆฌ์˜ ์ƒ์‚ฐ์„ฑ๊ณผ ์•ˆ์ „์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ˜‘์—…ํ•  ๋•Œ ๋”์šฑ ๋น›์„ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ