diff --git a/index.html b/index.html
index d4b7ac8..89a6e13 100644
--- a/index.html
+++ b/index.html
@@ -33,6 +33,21 @@
href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap"
rel="stylesheet"
/>
+
Pick-Px
diff --git a/package-lock.json b/package-lock.json
index 6c0c654..68dfbdd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
"socket.io-client": "^4.8.1",
"tailwindcss": "^4.1.11",
"use-sound": "^5.0.0",
+ "web-vitals": "^5.1.0",
"zustand": "^5.0.6"
},
"devDependencies": {
@@ -46,7 +47,9 @@
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
"vite": "^7.0.0",
- "vitest": "^3.2.4"
+ "vite-bundle-analyzer": "^1.2.3",
+ "vitest": "^3.2.4",
+ "webpack-bundle-analyzer": "^4.10.2"
}
},
"node_modules/@adobe/css-tools": {
@@ -497,6 +500,16 @@
"node": ">=18"
}
},
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
@@ -2432,6 +2445,19 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
@@ -2748,6 +2774,16 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2833,6 +2869,13 @@
"node": ">=18"
}
},
+ "node_modules/debounce": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -2925,6 +2968,13 @@
"node": ">= 0.4"
}
},
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.173",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.173.tgz",
@@ -3621,6 +3671,22 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/gzip-size": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
+ "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "^0.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3689,6 +3755,13 @@
"node": ">=18"
}
},
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
@@ -4515,6 +4588,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/opener": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
+ "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+ "dev": true,
+ "license": "(WTFPL OR MIT)",
+ "bin": {
+ "opener": "bin/opener-bin.js"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -5745,6 +5828,16 @@
}
}
},
+ "node_modules/vite-bundle-analyzer": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/vite-bundle-analyzer/-/vite-bundle-analyzer-1.2.3.tgz",
+ "integrity": "sha512-8nhwDGHWMKKgg6oegAOpDgTT7/yzTVzeYzLF4y8WBJoYu9gO7h29UpHiQnXD2rAvfQzDy5Wqe/Za5cgqhnxi5g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "analyze": "dist/bin.js"
+ }
+ },
"node_modules/vite-node": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
@@ -5893,6 +5986,12 @@
"node": ">=18"
}
},
+ "node_modules/web-vitals": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.1.0.tgz",
+ "integrity": "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==",
+ "license": "Apache-2.0"
+ },
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -5903,6 +6002,70 @@
"node": ">=12"
}
},
+ "node_modules/webpack-bundle-analyzer": {
+ "version": "4.10.2",
+ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz",
+ "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@discoveryjs/json-ext": "0.5.7",
+ "acorn": "^8.0.4",
+ "acorn-walk": "^8.0.0",
+ "commander": "^7.2.0",
+ "debounce": "^1.2.1",
+ "escape-string-regexp": "^4.0.0",
+ "gzip-size": "^6.0.0",
+ "html-escaper": "^2.0.2",
+ "opener": "^1.5.2",
+ "picocolors": "^1.0.0",
+ "sirv": "^2.0.3",
+ "ws": "^7.3.1"
+ },
+ "bin": {
+ "webpack-bundle-analyzer": "lib/bin/analyzer.js"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/sirv": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz",
+ "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-bundle-analyzer/node_modules/ws": {
+ "version": "7.5.10",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+ "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
diff --git a/package.json b/package.json
index 0f8a8c8..6b2accd 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"socket.io-client": "^4.8.1",
"tailwindcss": "^4.1.11",
"use-sound": "^5.0.0",
+ "web-vitals": "^5.1.0",
"zustand": "^5.0.6"
},
"devDependencies": {
@@ -49,6 +50,8 @@
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
"vite": "^7.0.0",
- "vitest": "^3.2.4"
+ "vite-bundle-analyzer": "^1.2.3",
+ "vitest": "^3.2.4",
+ "webpack-bundle-analyzer": "^4.10.2"
}
}
diff --git a/src/index.css b/src/index.css
index e0b2431..d2c1fbd 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,4 +1,18 @@
@import 'tailwindcss';
+
+/* 웹폰트 최적화 */
+@font-face {
+ font-family: 'Press Start 2P';
+ font-display: swap;
+ font-weight: 400;
+ font-style: normal;
+}
+
+/* 폰트 로딩 중 텍스트 표시 최적화 */
+.font-press-start {
+ font-family: 'Press Start 2P', 'Courier New', monospace;
+ font-display: swap;
+}
html,
body {
overflow: hidden; /* 스크롤바를 숨기고 스크롤 동작을 비활성화 */
diff --git a/src/main.tsx b/src/main.tsx
index 364db39..6d6a9a4 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client';
import './index.css';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
+import { onCLS, onFCP, onLCP, onTTFB } from 'web-vitals';
import Router from './router/router.tsx';
@@ -16,4 +17,16 @@ document.addEventListener(
{ passive: false }
);
+// Web Vitals 측정
+function sendToAnalytics(metric: any) {
+ console.log('Web Vitals:', metric);
+ // 여기에 분석 도구로 전송하는 코드를 추가할 수 있습니다
+ // 예: Google Analytics, Mixpanel 등
+}
+
+onCLS(sendToAnalytics);
+onFCP(sendToAnalytics);
+onLCP(sendToAnalytics);
+onTTFB(sendToAnalytics);
+
createRoot(document.getElementById('root')!).render();
diff --git a/vite.config.ts b/vite.config.ts
index 6082eec..0f94233 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -6,4 +6,29 @@ import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
base: '/', // ✨ 루트 경로 명시
plugins: [react(), tailwindcss()],
+ build: {
+ rollupOptions: {
+ output: {
+ manualChunks: {
+ // 핵심 React 라이브러리
+ 'react-vendor': ['react', 'react-dom'],
+
+ // 라우팅
+ router: ['react-router-dom'],
+
+ // UI/스타일링
+ ui: ['react-slick', 'slick-carousel', 'react-toastify'],
+
+ // 상태 관리
+ state: ['zustand'],
+
+ // 네트워크/통신
+ network: ['axios', 'socket.io-client'],
+
+ // 유틸리티
+ utils: ['jwt-decode', 'react-responsive', 'use-sound'],
+ },
+ },
+ },
+ },
});