리엑티 컴포넌트를 패키지 빌드 시 CSS 같이 적용시키기

Vite를 사용해 React 컴포넌트를 라이브러리로 패키징하면, dist 디렉토리에 style.css 파일이 생성되지만, 실제로 해당 컴포넌트를 사용하는 쪽에서 CSS가 적용되지 않는 문제가 발생할 수 있다. 이 글에서는 왜 이런 문제가 발생하는지, 그리고 어떻게 해결할 수 있는지를 실전 예제와 함께 자세히 살펴보자.


문제 상황

Vite로 React 컴포넌트를 라이브러리 형태로 빌드할 경우, vite.config.ts를 다음과 같이 설정하는 경우가 많다.

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'MyComponentLib',
      fileName: 'my-component-lib',
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
});

예제 컴포넌트

// src/components/MyButton.tsx
import React from 'react';
import './MyButton.css';

export const MyButton = () => {
  return <button className="my-button">Click me</button>;
};
/* src/components/MyButton.css */
.my-button {
  padding: 10px 20px;
  background-color: #007bff;
  color: white;
}

빌드를 수행하면 다음과 같은 구조가 생성된다:

dist/
├── style.css                ← 여기에 CSS는 존재한다
├── my-component-lib.es.js
├── my-component-lib.umd.js

하지만, 이 패키지를 외부 프로젝트에서 사용할 경우 다음과 같은 코드로 불러오게 된다:

import { MyButton } from 'my-component-lib';

export default function App() {
  return <MyButton />;
}

결과는?

❌ 버튼은 HTML 구조는 보이지만 스타일이 적용되지 않는다.


원인 분석

이는 Vite의 라이브러리 모드(build.lib)가 CSS를 JS 번들에서 분리하기 때문이다. 라이브러리 모드에서는 Vite가 CSS 파일을 별도로 추출하고, 이를 JS 파일에 자동으로 포함하거나 주입하지 않는다.

이 디자인은 일반적으로 라이브러리 소비자가 스타일 적용 여부를 스스로 결정할 수 있도록 하기 위한 의도이다. 하지만 대부분의 사용자는 라이브러리를 사용할 때 CSS가 자동으로 적용되기를 기대하기 때문에, 실제 사용에서 불편함을 유발한다.


해결책 1: vite-plugin-lib-inject-css로 자동 주입하기

보다 강력하고 사용자 친화적인 방법은 CSS를 라이브러리 JS 파일 내부에 자동으로 주입해주는 방식이다. 이를 도와주는 플러그인이 vite-plugin-lib-inject-css이다.

설치

npm install vite-plugin-lib-inject-css --save-dev

설정

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import libInjectCss from 'vite-plugin-lib-inject-css';

export default defineConfig({
  plugins: [react(), libInjectCss()],
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'MyComponentLib',
      fileName: 'my-component-lib',
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
});

이렇게 설정하면 빌드된 JS 파일 내부에 다음과 같은 코드가 자동으로 삽입된다:

const style = document.createElement('style');
style.textContent = '...빌드된 CSS 내용...';
document.head.appendChild(style);

결과

  • 사용자는 더 이상 CSS를 수동으로 import 할 필요가 없다.
  • import { MyButton } from 'my-component-lib' 한 줄이면, 스타일까지 함께 적용된다.
  • 내부적으로 <style> 태그가 생성되어 head에 삽입되므로 브라우저에서 동작도 보장된다.

결론

Vite의 라이브러리 빌드 모드에서는 기본적으로 CSS가 JS와 분리되어 출력되며, 자동으로 로드되지 않는다. 이를 방치하면 라이브러리를 사용하는 쪽에서 예상치 못한 스타일 누락 문제가 발생한다.

✔ 해결 방법 요약

방법장점단점
vite-plugin-lib-inject-css 사용자동 스타일 적용, UX 향상빌드 크기 소폭 증가

라이브러리를 보다 완성도 있게 만들고 싶다면, vite-plugin-lib-inject-css를 적용하여 개발자 경험을 개선하자.