이번 글에서는 지금까지 만든 프로젝트로 배포 테스트를 진행해보겠습니다. "이제 프로젝트 세팅 끝났는 데 왜 배포를 하냐?" 라는 질문이 나올 수 있을 거 같습니다. 어차피 해당 디자인 시스템을 배포할 예정이기도 하고 프로젝트가 방대해지기 전에 배포 테스트를 하는 것이 에러가 발생하는 원인을 쉽게 찾을 수 있다고 생각하기 때문에 지금 진행하려고 합니다.
제 코드를 그대로 복붙하는 것 보다는 아래에 명시된 Reference 링크를 통해 옵션이 어떤 기능을 하는지 한번은 보시는 걸 추천드립니다.
import { defineConfig } from "vite";
import { resolve } from "path";
import react from "@vitejs/plugin-react-swc";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import dts from "vite-plugin-dts";
export default defineConfig({
plugins: [
vanillaExtractPlugin(),
react(),
// 타입스크립트 컴파일을 도와줌
dts({
insertTypesEntry: true, // 컴포넌트 타입 생성
}),
],
build: {
// 라이브러리 모드로 빌드하는 데 필요한 설정
lib: {
// 진입점
entry: resolve(__dirname, "src/components/index.ts"),
// 라이브러리 이름
name: "@black-ui/react",
// 파일 이름
fileName: "index",
// 모듈형태
formats: ["es", "umd", "cjs"],
},
// rollup에 전달될 추가 옵션 정의
rollupOptions: {
// 빌드 과정에서 외부로 처리될 패키지 명시
external: ["react", "react-dom"],
// 빌드 결과물의 출력 옵션을 설정
output: {
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
},
},
});
vite에서 라이브러리를 빌드하기 위해서는 build.lib 옵션을 사용해야합니다.
{
"compilerOptions": {
// JavaScript 코드의 ECMAScript 버전을 설정하는 옵션 (가장 최신)
"target": "ESNext",
// TypeScript가 어떤 내장 API(예: Array.map, Promise 등)와 전역 객체(예: document, console 등)를 인식해야 하는지를 결정
"lib": ["ESNext", "DOM", "DOM.Iterable"],
//JavaScript 모듈을 생성
"module": "ESNext",
// Node.js의 모듈 해석 알고리즘 사용
"moduleResolution": "node",
// 컴파일된 JavaScript 파일들이 저장될 디렉토리를 설정
"outDir": "dist",
// 모든 파일을 분리된 모듈로서 처리하게 함
"isolatedModules": true,
// 컴파일을 실행하지만 JavaScript 출력을 제거함
"noEmit": true,
// : JSX 코드의 컴파일 방식을 설정합니다
"jsx": "react-jsx",
// 모든 엄격한 타입 검사 옵션을 활성화함
"strict": true,
// 사용되지 않는 변수에 대해 에러를 발생 시킴
"noUnusedLocals": true,
// 사용되지 않는 함수 매개변수에 대해 에러를 발생
"noUnusedParameters": true,
// switch 문의 case가 break나 return 등으로 종료되지 않으면 에러를 발생 시킴
"noFallthroughCasesInSwitch": true,
// TypeScript가 .d.ts 타입 선언 파일을 생성하도록 함
"declaration": true,
// .d.ts 파일에 대한 소스 맵을 생성하도록 함
"declarationMap": true,
// 컴파일된 JavaScript 파일에 대한 소스 맵을 생성하도록 함
"sourceMap": true,
// CommonJS와 ES 모듈 사이의 상호 운용성을 위한 코드를 생성하도록 함
"esModuleInterop": true,
// 대소문자가 다르게 사용된 파일 이름에 대해 에러를 발생 시킴
"forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true,
"skipLibCheck": true
},
// 컴파일 대상 파일들을 설정함
"include": ["src/components"],
// 프로젝트 참조 설정을 위한 경로를 설정함
"references": [{ "path": "./tsconfig.node.json" }]
}
{
// 라이브러리 이름
"name": "@black-ui/react",
// 라이브러리 버전
"version": "0.0.1",
// 'commonjs', 'module'
"type": "module",
// 작성자 정보
"author": {
"name": "hyeon9782"
},
// git 정보
"repository": {
"type": "git",
"url": "git://github.com/hyeon9782/black-ui.git"
},
// 라이브러리 설명
"description": "A presonalised UI Library.",
// 라이센스
"license": "MIT",
// build하고 어떤 파일을 생성할 것인지
"files": [
"dist"
],
// 진입점
"main": "./dist/index.js",
// 타입 파일
"types": "./dist/index.d.ts",
"scripts": {
"dev": "vite",
"build": "tsc && vite build --base=./",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"@vanilla-extract/css": "^1.14.0",
"@vanilla-extract/dynamic": "^2.1.0",
"@vanilla-extract/recipes": "^0.5.1",
"@vanilla-extract/sprinkles": "^1.6.1",
"react": "^18.0",
"react-dom": "^18.0",
"vite-plugin-commonjs": "^0.10.1",
"vite-plugin-dts": "^3.7.0",
"vite-plugin-libcss": "^1.1.1"
},
// npm에서 검색되는 데 필요한 keywords 정보
"keywords": [
"design system",
"button",
"input"
],
"devDependencies": {
"@storybook/addon-essentials": "^7.6.6",
"@storybook/addon-interactions": "^7.6.6",
"@storybook/addon-links": "^7.6.6",
"@storybook/addon-onboarding": "^1.0.10",
"@storybook/blocks": "^7.6.6",
"@storybook/react": "^7.6.6",
"@storybook/react-vite": "^7.6.6",
"@storybook/test": "^7.6.6",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vanilla-extract/vite-plugin": "^3.9.3",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"eslint-plugin-storybook": "^0.6.15",
"storybook": "^7.6.6",
"typescript": "^5.2.2",
"vite": "^5.0.8"
},
// 리액트에서만 돌아가는 프로젝트임을 명시
"peerDependencies": {
"react": "^18.0",
"react-dom": "^18.0"
}
}
해당 라이브러리의 라이센스 문서입니다. 원하시는 라이센스를 선택하셔서 작성하시면 됩니다.
해당 라이브러리가 어떤 라이브러리인지 설명해주는 문서입니다. README.md 파일을 최대한 상세하게 작성하시는 것이 좋습니다.
npm에 배포할 때 포함시키지 않을 폴더 또는 파일을 지정하는 파일입니다.
기본적인 설정은 끝났습니다. 이제 라이브러리를 배포하기 위해서 NPM 공식 문서에서 계정을 만들어봅시다.
오래된 라이브러리가 아니라면 보통 라이브러리 명은 @[조직명]/[패키지명] 이런식으로 되어있을 겁니다. 꼭 이런식으로 하실 필요는 없지만 저는 추후에 vue 버전도 만들 예정이고, 라이브러리를 쪼개서 배포할 수도 있기 때문에 @black-ui라는 Organization을 생성하겠습니다.
npm login
명령어를 통해 npm에 로그인해줍니다. 다른 분들은 username과 password를 입력하던데 저는 npm 홈페이지로 넘어가서 이메일로 2차 인증을 진행한 후에 로그인이 되었습니다.
npm publish
명령어를 통해 npm에 배포를 해줍니다.
npm install @black-ui/react
명령어를 통해 제가 만든 라이브러리를 다운 받아서 사용해봅시다!
문제가 발생했습니다. 배포한 라이브러리를 사용해보니 스타일이 적용되지 않습니다. 배포된 결과물을 보면 style.css가 정상적으로 생성되는데요.
style.css를 컴포넌트를 사용하는 곳에서 직접 import하면 스타일이 적용되지만 이건 제가 원하는 작동 방식이 아닙니다.
컴포넌트를 import하는 것만으로도 스타일이 적용이 되어야하죠. 이 부분에서 구글링을 정말 많이 한 거 같습니다.
결론만 말씀드리면 빌드 타임에 css를 js에 포함시켜야 하는 데 해당 과정이 제대로 이루어지지 않아 생기는 문제였습니다.
vite-plugin-libcss 패키지를 사용하여 해당 과정을 진행하여 문제를 해결했습니다.
import { defineConfig } from "vite";
import { resolve } from "path";
import react from "@vitejs/plugin-react-swc";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import libCss from "vite-plugin-libcss";
import dts from "vite-plugin-dts";
export default defineConfig({
plugins: [
vanillaExtractPlugin(),
react(),
dts({
insertTypesEntry: true, // 컴포넌트 타입 생성
}),
libCss(),
],
build: {
lib: {
entry: resolve(__dirname, "src/components/index.ts"),
name: "@black-ui/react",
fileName: "index",
// 어떤 모듈형태로 빌드할건지 : es, umd, cjs
formats: ["es", "umd", "cjs"],
},
rollupOptions: {
external: ["react", "react-dom"],
output: {
globals: {
react: "React",
"react-dom": "ReactDOM",
},
},
},
},
});