troubleshooting2025-02-14·9 min·206/348

Rollup 번들러 설정 에러: 빌드 최적화 및 문제 해결

Rollup 번들러 설정 시 발생하는 에러와 빌드 최적화 방법을 설명합니다.

Rollup 번들러 설정 에러

Introduction

Rollup은 JavaScript 모듈 번들링을 위한 강력한 도구로, tree shaking과 ES 모듈 지원에 특화되어 있습니다. 라이브러리 개발 시 많이 사용되지만, 설정 과정에서 여러 에러에 직면할 수 있습니다. 이 글에서는 Rollup 설정 에러 해결 방법과 빌드 최적화 기법을 다루겠습니다.

Environment

// package.json
{
  "name": "my-library",
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.cjs.js",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "rollup -c",
    "watch": "rollup -c --watch"
  },
  "devDependencies": {
    "rollup": "^4.9.0",
    "@rollup/plugin-typescript": "^11.1.0",
    "@rollup/plugin-node-resolve": "^15.2.0",
    "@rollup/plugin-commonjs": "^25.0.0",
    "@rollup/plugin-terser": "^0.4.0",
    "tslib": "^2.6.0",
    "typescript": "^5.3.0"
  }
}
npx rollup --version
# rollup v4.9.1

Problem

Rollup 설정 시 발생하는 에러들:

// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
  input: 'src/index.ts',
  output: {
    file: 'dist/index.js',
    format: 'cjs'
  },
  plugins: [
    typescript(),
    resolve(),
    commonjs()
  ]
};

// 에러: Cannot use import statement outside a module
// TypeError: plugins.map is not a function
# 빌드 실행 시 에러
npm run build

# 에러:
# [!] (plugin typescript) Error: tslib does not exist
# src/index.ts:1:0
# import { Button } from './Button';

Analysis

Rollup 설정 에러의 원인을 분석했습니다:

// Rollup 설정 파일 구조
// rollup.config.js (ES 모듈 방식)
export default {
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/index.cjs.js',
      format: 'cjs',
      sourcemap: true
    },
    {
      file: 'dist/index.esm.js',
      format: 'es',
      sourcemap: true
    }
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript()
  ]
};

// 플러그인 순서的重要性:
// 1. resolve(): node_modules에서 모듈 해결
// 2. commonjs(): CommonJS 모듈을 ES 모듈로 변환
// 3. typescript(): TypeScript 컴파일
# TypeScript 컴파일 옵션 확인
cat tsconfig.json
# {
#   "compilerOptions": {
#     "target": "ES2020",
#     "module": "ESNext",
#     "declaration": true,
#     "declarationDir": "./dist"
#   }
# }

Solution

1단계: 기본 설정 구성

// rollup.config.mjs (ES 모듈 확장자 사용)
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import { readFileSync } from 'fs';

const pkg = JSON.parse(readFileSync('./package.json', 'utf8'));

export default {
  input: 'src/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true,
      exports: 'named'
    },
    {
      file: pkg.module,
      format: 'es',
      sourcemap: true,
      exports: 'named'
    }
  ],
  external: [
    ...Object.keys(pkg.dependencies || {}),
    ...Object.keys(pkg.peerDependencies || {})
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript({
      tsconfig: './tsconfig.json',
      declaration: true,
      declarationDir: './dist/types'
    }),
    terser({
      compress: {
        drop_console: true
      }
    })
  ],
  onwarn: (warning, warn) => {
    if (warning.code === 'CircularDependency') {
      return;
    }
    warn(warning);
  }
};

2단계: TypeScript 설정 최적화

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "declaration": true,
    "declarationDir": "./dist/types",
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.stories.tsx"]
}

3단계: 코드 분할 설정

// rollup.config.mjs (코드 분할)
export default {
  input: {
    index: 'src/index.ts',
    utils: 'src/utils/index.ts',
    hooks: 'src/hooks/index.ts'
  },
  output: [
    {
      dir: 'dist',
      format: 'cjs',
      entryFileNames: '[name].cjs.js',
      chunkFileNames: 'chunks/[name]-[hash].js',
      sourcemap: true
    },
    {
      dir: 'dist',
      format: 'es',
      entryFileNames: '[name].esm.js',
      chunkFileNames: 'chunks/[name]-[hash].js',
      sourcemap: true
    }
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript()
  ],
  manualChunks: {
    vendor: ['react', 'react-dom'],
    utils: ['lodash', 'date-fns']
  }
};

4단계: 빌드 최적화

// rollup.config.mjs (최적화된 설정)
import copy from 'rollup-plugin-copy';
import postcss from 'rollup-plugin-postcss';
import image from '@rollup/plugin-image';

export default {
  input: 'src/index.ts',
  output: {
    dir: 'dist',
    format: 'es',
    sourcemap: true,
    preserveModules: true,
    preserveModulesRoot: 'src'
  },
  plugins: [
    resolve({
      browser: true,
      dedupe: ['react', 'react-dom']
    }),
    commonjs(),
    typescript({
      tsconfig: './tsconfig.build.json'
    }),
    postcss({
      extract: 'styles.css',
      minimize: true
    }),
    image(),
    copy({
      targets: [
        { src: 'src/assets/fonts', dest: 'dist' },
        { src: 'README.md', dest: 'dist' }
      ]
    })
  ],
  treeshake: {
    moduleSideEffects: false,
    propertyReadSideEffects: false,
    tryCatchDeoptimization: false
  }
};

Lessons Learned

  1. 플러그인 순서: resolve, commonjs, typescript 순서로 플러그인을 정렬해야 합니다
  2. ESM 설정: rollup.config.mjs 파일을 사용하여 ES 모듈 문법을 지원해야 합니다
  3. external 설정: 외부 의존성을 명시하여 번들 크기를 줄여야 합니다
  4. tree shaking: treeshake 옵션을 최적화하여 불필요한 코드를 제거해야 합니다
  5. 다중 출력: CJS와 ESM 형식으로 동시에 출력하여 호환성을 확보해야 합니다

이 블로그는 외부 스폰서십, 제휴 마케팅 또는 광고 수익을 받지 않습니다.