-
vite 플러그인 만들기 (with 간단한 예제 & vite-plugin-pages 분석) 😊웹 개발 2025. 3. 18. 21:17
vite는 rollup을 기반으로 만들어졌고, 빌드시 rollup이 사용된다.
또한 vite의 플러그인은 rollup을 기반으로 vite의 특정 옵션이 추가된 형태로 개발되었기에,
vite에서 제작한 플러그인은 기본적으로 개발 환경과 빌드 환경에서 모두 호환이 된다. (물론, 각 환경에서만 실행되는 특화된 기능을 제공하는 플러그인도 있다)
실제로 vite 플러그인을 만들어보는 것도 재밌는 경험이 될 것 같아서, 어떻게 만들 수 있는지 간단한 예제를 만들어 알아보기로 했다 😙
🌟 일단 가장 간단한 예제로 일단 플러그인을 만들어보자.
// vite-plugin-hello.ts import { Plugin } from 'vite'; export default function vitePluginHello(): Plugin { return { name: "vite-plugin-hello", // 플러그인 이름 buildStart() { console.log("Hello from Vite Plugin!"); }, }; }이렇게 플러그인 함수를 만든뒤, 연결한다.
// vite.config.ts import { defineConfig } from "vite"; import vitePluginHello from "./vite-plugin-hello"; export default defineConfig({ plugins: [vitePluginHello()], });이렇게 단순하게 로그를 남기는 플러그인을 만들 수 있지만, 좀 더 vite의 기능을 활용한 플러그인을 만들 수도 있다.
🌠 vite는 transform, resolveId, load 같은 여러 훅(hook)을 제공하여 빌드 과정에서 다양한 동작을 수행할 수 있다.
import { Plugin } from 'vite'; export default function vitePluginVirtualModule(): Plugin { return { name: "vite-plugin-virtual-module", resolveId(id: string) { if (id === "virtual:hello") { return id; // 가상 모듈로 처리 } // return이 null 이나 void면 가상모듈 X }, load(id: string) { if (id === "virtual:hello") { return `export default 'Hello from virtual module!';`; } }, }; }🥖 load와 resolveId 훅을 사용하면 특정 모듈의 경로를 변경할 수 있다.
🥖 load 훅은 resolveId에서 처리한 가상의 모듈에 대해 실제 내용을 제공한다.
위 예제에서 load는 virtual:hello 모듈이 요청될 때, export default 'Hello from virtual module!';을 반환하도록 하였다.
이렇게 하면 Vite는 해당 모듈이 실제 파일이 아니라도, 우리가 정의한 내용을 사용하여 코드를 빌드할 수 있다.
예를 들면, API 호출을 가상으로 처리하거나, 동적 코드 생성을 할 수도 있다.
위의 예제 플러그인을 사용하면 virtual:hello를 import 할때, 실제 파일이 없어도 해당 코드가 반환된다 🫢
import message from 'virtual:hello'; console.log(message); // "Hello from virtual module!"+ 근데, 타입스크립트라면 해당 모듈에 대한 타이핑이 필요하다
// global.d.ts declare module 'virtual:hello' { const value: string; export default value; }// tsconfig.app.json { "compilerOptions": { ... "types": ["./global.d.ts"], }🍠 transform 훅을 사용하면 빌드 과정에서 특정 파일의 내용을 변경할 수 있다.
예를 들어, .env 변수를 자동으로 주입하는 기능을 플러그인으로 제공할 수 있다.
import { Plugin } from 'vite'; export default function vitePluginTransform(): Plugin { return { name: "vite-plugin-transform", transform(code: string, id: string) { if (id.endsWith(".ts")) { return code.replace("process.env.HELLO", "'Hello from transform' "); } }, }; }위의 플러그인 예제를 직접 사용하면,
console.log(process.env.HELLO); // "Hello from transform"플러그인을 통해서 env.HELLO를 주입해줄 수 있다.
+ 기본적으로 transform의 인자로 code가 주어지기에, 코드가 처리되기 전에 `process.env.HELLO`라는 텍스트를 다른 문자열로 바꿔치기를 함으로써 마치 env를 주입하는 것 처럼 동작할 수 있게 한다.
+ 추가로 작성해본 vite plugins들 ( vite-plugin-trolling & vite-plugin-code-stats.ts ) 🌺
https://github.com/citron03/practice-trpc/commit/4c2570f987d0b91eca47030ac5e799abf4dd381c
feat(vite): add custom vite-plugins · citron03/practice-trpc@4c2570f
+ plugins: [react(), vitePluginTrolling(), vitePluginCodeStats()],
github.com
vite 플러그인은 다양한 시점에서 코드를 변환할 수 있도록 훅을 제공하므로, 이를 참고하여 원하는 시점에서 원하는대로 코드를 변형하거나 추가로 기능을 제공하도록 할 수 있다 🫰

티스토리 표가 자꾸 깨지네요 vite가 export하는 Plugin 타입을 확인하면, 더 많은 vite 플러그인의 기능을 확인할 수 있다. (물론, 타입도 함께)
+ 옵션이 있는 경우, ssr을 설정하는 boolean 값이 들어갈 수 있는데 vite에서 ssr에 대한 설정을 플러그인을 통해서도 제공할 수 있는 듯 하다 🌺
+ build 관련 훅 (buildStart, buildEnd 등)은 개발 서버에서 실행되지 않는다.
실제 사용하는 vite-plugin 간단 분석 (✨ vite-plugin-pages)
간단하게 vite 플러그인에 대해서 알아보고 예제도 작성해봤지만, 그래도 실제로 많이 사용하는 vite 플러그인을 분석해보면 해본 내용들이 와닿지 않을까, 싶어서 플러그인 하나의 코드를 열어보기로 했다.
열어볼 코드는 vite-plugin-pages로, 간단히 말하자면 next의 page router같은 page 폴더 기반 라우팅을 자동으로 vite 환경에서 구현해주는 착한 플러그인이다 ✈️
https://github.com/hannoeru/vite-plugin-pages
GitHub - hannoeru/vite-plugin-pages: File system based route generator for ⚡️Vite
File system based route generator for ⚡️Vite. Contribute to hannoeru/vite-plugin-pages development by creating an account on GitHub.
github.com
깃헙 레포지토리를 찾아 들어가서, 플러그인이 어떻게 구성되어 있는지 코드를 살펴보도록 했다.
packages.json의 main을 보고 index.ts를 찾아가면 익숙한 구조를 만날 수 있다.
간단하게 주석을 남긴 코드인데, 구조는 그렇게 어렵지 않다 😃
import type { Plugin } from 'vite' import type { UserOptions } from './types' import { MODULE_ID_VIRTUAL, ROUTE_BLOCK_ID_VIRTUAL, routeBlockQueryRE } from './constants' import { PageContext } from './context' import { parsePageRequest } from './utils' function pagesPlugin(userOptions: UserOptions = {}): Plugin { let ctx: PageContext return { name: 'vite-plugin-pages', enforce: 'pre', // 플러그인이 다른 transform 훅보다 먼저 실행되도록 설정 async configResolved(config) { // Vite 설정이 확정된 후 실행됨 // 프로젝트가 React 또는 Solid인지 자동으로 감지하여 resolver 설정 // auto set resolver for react project if ( !userOptions.resolver && config.plugins.find(i => i.name.includes('vite:react')) ) { userOptions.resolver = 'react' } // auto set resolver for solid project if ( !userOptions.resolver && config.plugins.find(i => i.name.includes('solid')) ) { userOptions.resolver = 'solid' } ctx = new PageContext(userOptions, config.root) ctx.setLogger(config.logger) await ctx.searchGlob() // 페이지 파일을 검색하여 라우트 구성 ? }, api: { getResolvedRoutes() { // 플러그인이 해석한 라우트 데이터를 제공하는 API ? return ctx.options.resolver.getComputedRoutes(ctx) }, }, configureServer(server) { // 개발 서버 설정을 위한 훅 ctx.setupViteServer(server) // 핫 리로드 및 파일 감지 설정 }, resolveId(id) { // 특정 가상 모듈 ID를 처리하여 변환 if (ctx.options.moduleIds.includes(id)) return `${MODULE_ID_VIRTUAL}?id=${id}` if (routeBlockQueryRE.test(id)) return ROUTE_BLOCK_ID_VIRTUAL return null }, async load(id) { // resolveId에서 처리한 가상 모듈의 실제 내용을 반환 const { moduleId, pageId, } = parsePageRequest(id) if (moduleId === MODULE_ID_VIRTUAL && pageId && ctx.options.moduleIds.includes(pageId)) return ctx.resolveRoutes() // 라우트 정보를 변환하여 제공 ? if (id === ROUTE_BLOCK_ID_VIRTUAL) { return { code: 'export default {};', map: null, } } return null }, } } export { syncIndexResolver } from './options' export type { ReactRoute, SolidRoute, VueRoute, } from './resolvers' export { reactResolver, solidResolver, vueResolver, } from './resolvers' export * from './types' export { PageContext } export default pagesPlugin물론, 훌륭한 라이브러리인만큼 기능이 분리되어 있고, 더 자세한 동작을 알기 위해서는 더 깊이 파고들어야 하겠지만.
일단, 우리가 vite 플러그인에 대해서 작성해보고 공부한 내용이 vite-plugin-pages에 포함되어 있음을 알 수 있다.
load, resolveId, configResolved(vite 전용 훅), configureServer(vite 전용 훅) 등이 vite에서 사용할 수 있는 훅임을 알고 이들이 어느때에 어떻게 동작하는지 파악할 수 있다. (이를 알기에, vite 공식문서를 참고할 수 있다)
그렇기 때문에, 어느 순서대로 이 플러그인을 분석해야될지 접근할 수 있으며, 크게 어떤 구조로 움직이는지 대강 상상할 수 있다 😎
😊 참고 자료
https://ko.vite.dev/guide/api-plugin.html#authoring-a-plugin
Vite
Vite, 프런트엔드 개발의 새로운 기준
ko.vite.dev
https://www.npmjs.com/package/vite-plugin-pages
vite-plugin-pages
File system base vue-router plugin for Vite. Latest version: 0.32.5, last published: a month ago. Start using vite-plugin-pages in your project by running `npm i vite-plugin-pages`. There are 38 other projects in the npm registry using vite-plugin-pages.
www.npmjs.com
'웹 개발' 카테고리의 다른 글
리스트 가상화(Virtualization) - 성능 최적화를 위한 필수 기술 (많은 데이터 표현 시 최적화) (0) 2025.07.04 Next.js 15 App Router에서 PWA 설정하기 🍍 (0) 2025.04.11 stylelint 🎨 스타일에도 린트 적용하기 (0) 2025.03.01 Next에서 더 효과적인 modal 개발하기 (Parallel Routes & Intercepting Routes) 😎 (0) 2025.02.21 pnpm patch를 사용해서 노드 모듈 수정하기 (patch-package 에러 발생 😭) (0) 2025.02.05