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
'웹 개발' 카테고리의 다른 글
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 |
JS 개발 환경에서 커밋 컨벤션 강제하기 (Conventional Commits with commitlint 👻) (0) | 2025.01.31 |