alpaka206

Vite + React 빌드 산출물을 단일 HTML로 전달하기

date
Aug 24, 2025
slug
vite-plugin-singlefile-one-html-build
author
status
Public
tags
펑타이그레이터차이나
React
summary
Vite + React 빌드 결과를 vite-plugin-singlefile단일 HTML에 인라인하여 file://, 내부망, 메일 첨부 환경에서도 바로 열 수 있도록 하는 방법을 정리합니다.
type
Post
thumbnail
category
✍️ Study
updatedAt
Jan 22, 2026 03:32 PM
일반적으로 빌드하면 dist/ 폴더에 index.html, assets/*.js, *.css가 나뉘어 생성됩니다. 그런데 메일로 HTML 하나만 보내야 하거나, 내부망 환경에서 웹서버 없이 열어야 하는 상황이면 문제가 생깁니다. 그냥 index.html을 더블클릭(file://)으로 열면 빈 화면이거나 콘솔에 MIME 타입 에러가 발생하기 쉽습니다.
퍼블리셔가 정적인 HTML만으로 디자인을 전달할 수도 있지만, 동적인 react 초안을 한 장으로 공유하면서 이후 정식 웹페이지로 고도화하고 싶은 경우가 있습니다. 이 글은 그런 상황을 해결하려고 빌드 결과를 “한 장짜리 HTML”로 만드는 방법을 공유합니다.

file://로 그냥 열면 잘 안 돌아갈까?

  • 경로 해석 문제
    • 빌드된 HTML에는 보통 <script type="module" src="/assets/…"> 같은 절대경로가 들어갑니다. file://로 열면 이 경로가 로컬 파일 시스템의 루트로 해석되거나 아예 찾지 못해 리소스 로드 실패가 납니다.
  • MIME 타입/모듈 스크립트 문제
    • 브라우저는 모듈 스크립트를 로드할 때 올바른 MIME 타입과 응답 헤더를 기대합니다. 하지만 file://에는 서버가 없어 타입이 보장되지 않으며, 잘못된 타입/경로로 인해 “Expected a JavaScript module, got …” 류의 오류가 발생하기 쉽습니다.
  • 히스토리 라우팅 문제
    • BrowserRouter는 경로별로 항상 index.html을 돌려주는 서버 리라이트가 전제입니다. 서버가 없는 file://이나 리라이트를 바꿀 수 없는 정적 호스팅에서는 새로고침/직접 접근 시 404가 나거나 라우팅이 깨집니다. 이런 환경에서는 HashRouter가 안전합니다.

해결책: vite-plugin-singlefile로 “한 파일” 만들기

vite-plugin-singlefile번들된 JS/CSS를 전부 index.html 안으로 인라인합니다. 결과적으로 HTML 파일 하나만 전달해도 되고, 사용자는 그 파일을 더블클릭해 바로 실행할 수 있습니다. 도구 데모, 내부망 전달, 오프라인 검토 상황에 특히 유용합니다.

동작 방식

  • 빌드 결과의 JavaScript/CSS를 <script>/<style>HTML에 삽입합니다.
  • 동적 임포트 및 모듈 로더를 단일 파일 시나리오에 맞게 정리합니다.
  • 대부분의 에셋을 인라인 처리하지만, 일부 public 리소스는 별도 취급이 될 수 있습니다.

빠른 시작(React 기준)

1) 설치

npm i -D vite-plugin-singlefile # 또는 yarn add -D vite-plugin-singlefile

2) vite.config.ts

import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { viteSingleFile } from 'vite-plugin-singlefile' export default defineConfig({ plugins: [ react(), viteSingleFile(), ], })

3) 빌드 & 전달

npm run build
참고: public/폴더의 파비콘·이미지 등은 인라인되지 않을 수 있습니다. 완전한 “한 장”이 필요하다면
1. 해당 리소스를 src에서 import하여 번들 파이프라인에 태우거나,
2. Data URL/SVG 인라인 등으로 대체하는 방법을 고려합니다.

라우팅 전략: 왜 HashRouter인가?

  • file://이나 단순 정적 호스팅에서는 BrowserRouter새로고침/직접 접근 시 쉽게 깨집니다.
  • HashRouter은 서버 개입이 없어도 동작합니다. /#/about처럼 # 뒤 경로는 클라이언트만 해석하므로 404 위험이 없습니다.
import { HashRouter, Routes, Route } from 'react-router-dom' export default function App() { return ( <HashRouter> <Routes> <Route path="/" element={<Home/>} /> <Route path="/about" element={<About/>} /> </Routes> </HashRouter> ) }

한계와 주의사항

  • 성능/크기
    • 모든 JS/CSS가 한 HTML에 인라인되므로 파일이 커지고 캐시 분할이 불가합니다. 일반 서비스 배포에는 적합하지 않으며, 데모/오프라인/내부망 전달용으로 사용하는 것이 좋습니다.
  • 에셋 제약
    • 일부 리소스와 소스맵은 인라인/활용에 제약이 있습니다. 필요 시 src에서 import해 번들에 포함시키거나 Data URL 등으로 우회합니다.

결론

  • “빌드한 결과를 HTML 하나로 보내야 한다”, “서버 없이 바로 열어야 한다”는 요구에는 vite-plugin-singlefile이 가장 간단하고 확실한 선택입니다.
  • 라우팅은 HashRouter를 사용하면 file:// , 정적 호스팅에서도 안전하게 동작합니다.
  • 정식 배포에서는 캐시/성능을 위해 일반 번들 방식을, 공유/검토/내부망 시연에는 단일 파일 방식을 택하는 이원화 전략이 현실적입니다.