diff --git a/README.md b/README.md index 53c088d..6bcd746 100644 --- a/README.md +++ b/README.md @@ -1 +1,148 @@ -# mango-boss-fe \ No newline at end of file +# πŸ₯­ MangoBoss (망고보슀) + +> [image (10)](https://drive.google.com/file/d/1avQXxRjYqYMZdZu0MLEnuBWkBaB1wWlD/view) +> μ„œλΉ„μŠ€ μ‹œμ—° μ˜μƒ (Service Demo) + + +> **"λ³΅μž‘ν•œ λ§€μž₯ 관리, 이제 망고보슀 ν•˜λ‚˜λ‘œ 끝."** +> μ†Œμƒκ³΅μΈ 사μž₯λ‹˜λ“€μ„ μœ„ν•œ **μ˜¬μΈμ› λ§€μž₯ 관리 μ†”λ£¨μ…˜(SaaS)** ν”„λ‘ νŠΈμ—”λ“œ μ €μž₯μ†Œμž…λ‹ˆλ‹€. + +
+ +## πŸ”— 배포 및 λ¬Έμ„œ (Links) +* **Service URL**: [https://www.mangoboss.com](https://www.mangoboss.com) +* **Team Notion**: [MangoBoss Team Docs](https://www.notion.so/1af52a9c0c5380e6b73beb5fc5e64aea) +* **Personal Docs**: [Dev Progress](https://www.notion.so/MangoBoss-1aea0b0c7d4c805c8204eba5bb540b55) + +
+ +## πŸ“– ν”„λ‘œμ νŠΈ λ°°κ²½ (Background) + +**"사μž₯λ‹˜μ€ μž₯μ‚¬μ—λ§Œ μ§‘μ€‘ν•˜μ„Έμš”, κ΄€λ¦¬λŠ” λ§κ³ λ³΄μŠ€κ°€ ν• κ²Œμš”."** + +λ§Žμ€ μžμ˜μ—…μžλΆ„λ“€μ΄ 재고 관리, 직원 μŠ€μΌ€μ€„λ§, 맀좜 정산을 μœ„ν•΄ 엑셀을 μΌœκ±°λ‚˜ μ—¬λŸ¬ 개의 앱을 λ²ˆκ°ˆμ•„ μ‚¬μš©ν•˜λ©° μ‹œκ°„μ„ λ‚­λΉ„ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. +**MangoBoss**λŠ” 흩어져 μžˆλŠ” λ§€μž₯ 관리 κΈ°λŠ₯을 ν•˜λ‚˜μ˜ μ›Ή λŒ€μ‹œλ³΄λ“œλ‘œ ν†΅ν•©ν•˜μ—¬, IT에 μ΅μˆ™ν•˜μ§€ μ•Šμ€ 사μž₯λ‹˜λ“€λ„ μ§κ΄€μ μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ UXλ₯Ό μ„€κ³„ν–ˆμŠ΅λ‹ˆλ‹€. + +
+ +## ✨ 핡심 κΈ°λŠ₯ (Key Features) + +* **πŸ“Š μ‹€μ‹œκ°„ 맀좜 λŒ€μ‹œλ³΄λ“œ**: 차트 라이브러리λ₯Ό ν™œμš©ν•˜μ—¬ 일간/μ£Όκ°„/μ›”κ°„ 맀좜 좔이λ₯Ό μ‹œκ°ν™” +* **πŸ“¦ 슀마트 재고 관리**: 재고 λΆ€μ‘± μ‹œ μ•Œλ¦Ό 제곡 및 원클릭 발주 μ‹œμŠ€ν…œ +* **πŸ“… 직원 μŠ€μΌ€μ€„λ§**: λ“œλž˜κ·Έ μ•€ λ“œλ‘­(DnD)으둜 κ°„νŽΈν•˜κ²Œ μ•Œλ°”μƒ κ·Όλ¬΄ν‘œ μž‘μ„± +* **πŸ“± λ°˜μ‘ν˜• μ›Ή (Mobile First)**: PC ν¬μŠ€κΈ°λŠ” λ¬Όλ‘ , 이동 쀑인 사μž₯λ‹˜μ˜ μŠ€λ§ˆνŠΈν°μ—μ„œλ„ μ™„λ²½ ν˜Έν™˜ +* **πŸ”” μ‹€μ‹œκ°„ μ£Όλ¬Έ μ•Œλ¦Ό**: μ›Ή μ†ŒμΌ“(Web Socket)을 μ—°λ™ν•˜μ—¬ μ£Όλ¬Έ μ ‘μˆ˜ μ‹œ 즉각적인 μ•Œλ¦Ό 제곡 + +
+ +## πŸ› οΈ 기술 μŠ€νƒ (Tech Stack) + +| λΆ„λ₯˜ | 기술 | +| :-- | :-- | +| **Framework** | ![React](https://img.shields.io/badge/React-20232A?style=flat&logo=react&logoColor=61DAFB) ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=TypeScript&logoColor=white) | +| **State Mgt** | `Zustand` (μ „μ—­ μƒνƒœ) | +| **Styling** | ![TailwindCSS](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=flat&logo=tailwind-css&logoColor=white) | +| **Visualization** | `Recharts` (맀좜 데이터 μ‹œκ°ν™”) | +| **Build Tool** | ![Vite](https://img.shields.io/badge/Vite-646CFF?style=flat&logo=Vite&logoColor=white) | + +
+ +## πŸ“‚ 폴더 ꡬ쑰 (Directory Structure) + +μœ μ§€λ³΄μˆ˜μ„±κ³Ό ν™•μž₯성을 κ³ λ €ν•˜μ—¬ **κΈ°λŠ₯(Feature)** λ‹¨μœ„μ™€ **곡톡(Shared)** λ‹¨μœ„λ₯Ό λΆ„λ¦¬ν•˜μ—¬ μ„€κ³„ν–ˆμŠ΅λ‹ˆλ‹€. + +```bash +dev-fe/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ api/ # Axios μΈμŠ€ν„΄μŠ€ 및 API μš”μ²­ ν•¨μˆ˜ λͺ¨μŒ +β”‚ β”œβ”€β”€ assets/ # 이미지, 폰트, μ•„μ΄μ½˜ λ¦¬μ†ŒμŠ€ +β”‚ β”œβ”€β”€ components/ # μž¬μ‚¬μš© κ°€λŠ₯ν•œ 곡톡 UI (Button, Input λ“±) +β”‚ β”œβ”€β”€ constants/ # μƒμˆ˜ 데이터 (메뉴 리슀트, μ—λŸ¬ λ©”μ‹œμ§€ λ“±) +β”‚ β”œβ”€β”€ hooks/ # μ»€μŠ€ν…€ ν›… (useAuth, useSalesData λ“±) +β”‚ β”œβ”€β”€ layout/ # νŽ˜μ΄μ§€ λ ˆμ΄μ•„μ›ƒ (Header, Sidebar) +β”‚ β”œβ”€β”€ pages/ # λΌμš°νŒ… νŽ˜μ΄μ§€ (Dashboard, Inventory, Schedule) +β”‚ β”œβ”€β”€ store/ # Zustand μ „μ—­ μƒνƒœ 관리 +β”‚ β”œβ”€β”€ types/ # TypeScript νƒ€μž… μ •μ˜ +β”‚ β”œβ”€β”€ utils/ # λ‚ μ§œ λ³€ν™˜, 화폐 λ‹¨μœ„ ν¬λ§·νŒ… λ“± μœ ν‹Έ ν•¨μˆ˜ +β”‚ β”œβ”€β”€ App.tsx +β”‚ └── main.tsx +β”œβ”€β”€ .env +β”œβ”€β”€ package.json +└── README.md +``` + +
+ +## πŸš€ μ„€μΉ˜ 및 μ‹€ν–‰ (Getting Started) + +```bash +# 1. μ €μž₯μ†Œ 클둠 +git clone [https://github.com/Mango-Butter/dev-fe.git](https://github.com/Mango-Butter/dev-fe.git) + +# 2. νŒ¨ν‚€μ§€ μ„€μΉ˜ +yarn install + +# 3. 개발 μ„œλ²„ μ‹€ν–‰ +yarn dev +``` + +
+ +## πŸš€ μ„±λŠ₯ μ΅œμ ν™” (Performance Optimization) + +초기 λ‘œλ”© 속도 κ°œμ„ κ³Ό λ Œλ”λ§ μ΅œμ ν™”λ₯Ό μœ„ν•΄ Vite의 λΉŒλ“œ μ „λž΅μ„ μˆ˜μ •ν•˜κ³  μ½”λ“œ 뢄할을 적극적으둜 λ„μž…ν–ˆμŠ΅λ‹ˆλ‹€. + +* **λ²ˆλ“€ μ‚¬μ΄μ¦ˆ 96.8% κ°μ†Œ**: `React.lazy`, `Suspense` 및 `manualChunks` μ „λž΅μ„ μ μš©ν•˜μ—¬ λͺ¨λ†€λ¦¬μ‹ λ²ˆλ“€μ„ 뢄리. + * **Before**: 5MB (초기 단일 λ²ˆλ“€) + * **After**: 161KB (Core Chunk) + * **Result**: 전체 Chunk ꡬ쑰λ₯Ό 1개 β†’ 60개 μ΄μƒμœΌλ‘œ λΆ„μ‚°ν•˜μ—¬ TTI(Time to Interactive) λŒ€ν­ κ°œμ„ . +* **Lazy Loading 적용**: λžœλ”© νŽ˜μ΄μ§€ 및 μ£Όμš” μ„Ήμ…˜μ— `Intersection Observer`λ₯Ό λ„μž…ν•˜μ—¬ λ·°ν¬νŠΈμ— μ§„μž…ν•  λ•Œλ§Œ λ¦¬μ†ŒμŠ€λ₯Ό λ‘œλ“œν•˜λ„λ‘ κ°œμ„ . +* **비동기 UX κ°•ν™”**: 데이터 페칭 λŒ€κΈ° μ‹œκ°„μ— **Skeleton UI**와 **Lottie JSON μ• λ‹ˆλ©”μ΄μ…˜**을 λ…ΈμΆœν•˜μ—¬ 체감 λ‘œλ”© μ‹œκ°„μ„ λ‹¨μΆ•ν•˜κ³  λŠκΉ€ μ—†λŠ” μ‚¬μš©μž κ²½ν—˜ 제곡. + +
+ +## πŸ—οΈ μ•„ν‚€ν…μ²˜ 및 기술적 νŠΉμ§• (Architecture & Tech) + +### 1. PWA & FCM μ•Œλ¦Ό μ‹œμŠ€ν…œ +* **PWA (Progressive Web App)**: `vite-plugin-pwa`λ₯Ό ν™œμš©ν•˜μ—¬ λͺ¨λ°”일 ν™ˆ ν™”λ©΄ μ„€μΉ˜ 지원 및 μ„œλΉ„μŠ€ μ›Œμ»€(Service Worker) 기반의 μ˜€ν”„λΌμΈ μΊμ‹œ μ „λž΅ 적용. λ„€νŠΈμ›Œν¬κ°€ λΆˆμ•ˆμ •ν•œ ν™˜κ²½μ—μ„œλ„ 핡심 κΈ°λŠ₯ μ‚¬μš© κ°€λŠ₯. +* **Push Notification**: FCM(Firebase Cloud Messaging)을 μ—°λ™ν•˜μ—¬ λΈŒλΌμš°μ €μ˜ Foreground/Background μƒνƒœμ— 관계없이 μ‹€μ‹œκ°„ μ£Όλ¬Έ/μŠ€μΌ€μ€„ μ•Œλ¦Ό 전솑. + +### 2. μƒνƒœ 관리 및 λΌμš°νŒ… (State & Routing) +* **Zustand 기반 μƒνƒœ 뢄리**: 사μž₯λ‹˜(Admin)κ³Ό μ•Œλ°”μƒ(Staff)의 데이터 μ ‘κ·Ό λ²”μœ„κ°€ 닀름을 κ³ λ €ν•˜μ—¬, μ „μ—­ μƒνƒœ μŠ€ν† μ–΄λ₯Ό μ—­ν• λ³„λ‘œ 뢄리 및 κ²½λŸ‰ν™”. +* **Role-Based Access Control (RBAC)**: + * `PublicRoute`: λΉ„λ‘œκ·ΈμΈ μ‚¬μš©μž μ ‘κ·Ό + * `ProtectedRoute`: 둜그인 μ‚¬μš©μž μ „μš© + * `RoleRoute`: νŠΉμ • κΆŒν•œ(사μž₯λ‹˜/μ•Œλ°”μƒ)에 λ”°λ₯Έ νŽ˜μ΄μ§€ μ ‘κ·Ό μ œμ–΄ 및 λ¦¬λ””λ ‰μ…˜ 처리 + +### 3. λ””μžμΈ μ‹œμŠ€ν…œ (Design System) +* **Component-Driven Development**: **Tailwind CSS**와 **JSX in SVG**λ₯Ό 기반으둜 μœ μ—°ν•œ λ””μžμΈ μ‹œμŠ€ν…œ ꡬ좕. +* **Storybook λ„μž…**: 곡톡 UI μ»΄ν¬λ„ŒνŠΈ(Button, Input, Modal λ“±)λ₯Ό λ¬Έμ„œν™”ν•˜μ—¬ λ””μžμ΄λ„ˆ-개발자 κ°„ ν˜‘μ—… 효율 μ¦λŒ€ 및 UI 일관성 μœ μ§€. + +### 4. SEO & λ§ˆμΌ€νŒ… (SEO & Meta) +* μƒμš© μ„œλΉ„μŠ€ μˆ˜μ€€μ˜ **SEO λ©”νƒ€νƒœκ·Έ**, **OpenGraph**, **PWA Manifest**λ₯Ό μ™„λ²½ν•˜κ²Œ κ΅¬μ„±ν•˜μ—¬ SNS 곡유 μ‹œ 미리보기 μ΅œμ ν™” 및 검색 μ—”μ§„ λ…ΈμΆœ λŒ€μ‘. + +
+ +## πŸ”’ λ³΄μ•ˆ 및 배포 νŒŒμ΄ν”„λΌμΈ (Security & DevOps) + +### πŸ” ν΄λΌμ΄μ–ΈνŠΈ λ³΄μ•ˆ (Client Security) +* **AES 기반 데이터 μ•”ν˜Έν™”**: μ „μžκ·Όλ‘œκ³„μ•½μ„œ λ“± 민감 데이터 전솑 μ‹œ, ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ μƒμ„±ν•œ **Random Session Key**와 **Encryption Key**λ₯Ό μ‘°ν•©ν•˜μ—¬ AES μ•”ν˜Έν™” 적용. +* **μΌνšŒμ„± ν‚€ μ „λž΅**: 전솑 ν›„ ν‚€λ₯Ό νŒŒκΈ°ν•˜λŠ” λ°©μ‹μœΌλ‘œ 데이터 νƒˆμ·¨ 및 μ€‘κ°„μž 곡격(MITM) λ°©μ–΄. + +### ☁️ CI/CD & Infrastructure +* **Automated Deployment**: GitHub Actionsλ₯Ό ν™œμš©ν•˜μ—¬ `dev` (개발) β†’ `test` (ν…ŒμŠ€νŠΈ) β†’ `main` (운영) λΈŒλžœμΉ˜λ³„ μžλ™ 배포 νŒŒμ΄ν”„λΌμΈ ꡬ좕. +* **AWS Serverless**: **Amazon S3**(정적 ν˜ΈμŠ€νŒ…)와 **CloudFront**(CDN)λ₯Ό μ—°λ™ν•˜μ—¬ μ „ 세계 μ–΄λ””μ„œλ“  λΉ λ₯Έ μ†λ„λ‘œ μ½˜ν…μΈ λ₯Ό μ „μ†‘ν•˜κ³  SSL(HTTPS) λ³΄μ•ˆ 적용. + +
+ +## πŸ“¬ Team Mango-Butter + +| 이름 | μ—­ν•  | +| :-- | :-- | +| **[μœ€μ„μ°¬](https://github.com/PaleBlueNote)** | Frontend Lead +| **μ •ν˜„μ§€** | Backend Lead +| **μ‹¬μž¬μ—½** | Backend +| **이λͺ…건** | Designer + +--- +Β© 2026 Mango-Butter. All rights reserved. diff --git a/index.html b/index.html index 0d26d5d..7e6ccec 100644 --- a/index.html +++ b/index.html @@ -44,7 +44,7 @@ - +
diff --git a/src/App.tsx b/src/App.tsx index 7fbbbae..a40e65b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,9 +14,10 @@ import Signup from "./pages/signup/Signup.tsx"; import AddressSearchPopup from "./pages/store/boss/AddressSearchPopup.tsx"; import RoleRoute from "./routes/RoleRoute.tsx"; import FullScreenLoading from "./components/common/FullScreenLoading.tsx"; -import HomeBoss from "./pages/home/boss/HomeBoss.tsx"; -import HomeStaff from "./pages/home/staff/HomeStaff.tsx"; -import HomeAdmin from "./pages/home/admin/HomeAdmin.tsx"; +// Lazy-loaded home pages (role-based: no need to load all roles on initial bundle) +const HomeBoss = lazy(() => import("./pages/home/boss/HomeBoss.tsx")); +const HomeStaff = lazy(() => import("./pages/home/staff/HomeStaff.tsx")); +const HomeAdmin = lazy(() => import("./pages/home/admin/HomeAdmin.tsx")); import UnifiedPWAPrompt from "./libs/fcm/UnifiedPWAPrompt.tsx"; import PdfViewerPage from "./pages/PdfViewerPage.tsx"; import SubscribePage from "./pages/mypage/boss/SubscribePage.tsx"; diff --git a/src/api/boss/store.ts b/src/api/boss/store.ts index bee3ee0..0f8643b 100644 --- a/src/api/boss/store.ts +++ b/src/api/boss/store.ts @@ -11,9 +11,23 @@ import { } from "../../types/store.ts"; export const validateBusinessNumber = async (businessNumber: string) => { + // [μž„μ‹œ μˆ˜μ •] API 였λ₯˜λ‘œ 인해 λ°±μ—”λ“œ ν˜ΈμΆœμ„ 주석 μ²˜λ¦¬ν•˜κ³  무쑰건 μ„±κ³΅μ‹œν‚΄ + /* return await axiosAuth.post("/api/boss/stores/validations/business-number", { businessNumber, }); + */ + + // κ°€μ§œ 응닡 λ°˜ν™˜ (0.5초 λ”œλ ˆμ΄ ν›„ 성곡 처리) + return new Promise((resolve) => { + console.log(`[Mock API] μ‚¬μ—…μžλ²ˆν˜Έ(${businessNumber}) 인증 톡과 처리됨`); + setTimeout(() => { + resolve({ + status: 200, + data: { message: "μ‚¬μš© κ°€λŠ₯ν•œ μ‚¬μ—…μž λ²ˆν˜Έμž…λ‹ˆλ‹€." }, + }); + }, 500); + }); }; // λ§€μž₯ 등둝 diff --git a/src/pages/landing/FeatureCard.tsx b/src/pages/landing/FeatureCard.tsx index 61c6726..30719a9 100644 --- a/src/pages/landing/FeatureCard.tsx +++ b/src/pages/landing/FeatureCard.tsx @@ -1,4 +1,4 @@ -import { lazy } from "react"; +import { lazy, Suspense } from "react"; interface FeatureCardProps { title: string; @@ -22,7 +22,9 @@ export const FeatureCard = ({ title, description, icon }: FeatureCardProps) => {
{title}
{description}
- + }> + + ); }; diff --git a/src/pages/landing/Landing.tsx b/src/pages/landing/Landing.tsx index c68fc9c..803271b 100644 --- a/src/pages/landing/Landing.tsx +++ b/src/pages/landing/Landing.tsx @@ -6,9 +6,9 @@ import { FeatureCard } from "./FeatureCard.tsx"; import LandingIntroLazy from "./LandingIntroLazy.tsx"; import LandingPayLazy from "./LandingPayLazy.tsx"; import LandingDocLazy from "./LandingDocLazy.tsx"; +import LogoIcon from "../../components/icons/LogoIcon.tsx"; -// μ•„μ΄μ½˜ μ΅œμ†Œν™”: 큰 μ‚¬μ΄μ¦ˆ / ν•˜λ‹¨ λ Œλ”λ§λ§Œ lazy import -const LogoIcon = lazy(() => import("../../components/icons/LogoIcon.tsx")); +// νžˆμ–΄λ‘œ μ•„λž˜μͺ½ μš”μ†Œλ§Œ lazy: 초기 뷰포트 밖에 μœ„μΉ˜ const PartyIcon = lazy(() => import("../../components/icons/PartyIcon.tsx")); const Footer = lazy(() => import("../../components/layouts/Footer.tsx")); @@ -27,12 +27,10 @@ const Landing = () => {
- - - +
사μž₯λ‹˜μ˜ 뢀담을 μ€„μ—¬μ£ΌλŠ”
μ•Œλ°”μƒ κ΄€λ¦¬μ˜ μƒˆλ‘œμš΄ diff --git a/src/pages/landing/LandingDocLazy.tsx b/src/pages/landing/LandingDocLazy.tsx index 32222a7..e4fd4fd 100644 --- a/src/pages/landing/LandingDocLazy.tsx +++ b/src/pages/landing/LandingDocLazy.tsx @@ -8,16 +8,16 @@ const LandingDocLazy = () => { const { ref, inView } = useInView({ triggerOnce: true, threshold: 0.2 }); return ( -
- {inView && ( +
+ {inView ? ( λ¬Έμ„œ 정보 λΆˆλŸ¬μ˜€λŠ” 쀑...
+
λ¬Έμ„œ 정보 λΆˆλŸ¬μ˜€λŠ” 쀑...
} > - )} + ) : null}
); }; diff --git a/src/pages/landing/LandingDocSection.tsx b/src/pages/landing/LandingDocSection.tsx index 1681f8a..dd133c4 100644 --- a/src/pages/landing/LandingDocSection.tsx +++ b/src/pages/landing/LandingDocSection.tsx @@ -1,7 +1,13 @@ // src/pages/LandingDocSection.tsx -import BriefCaseIcon from "../../components/icons/BriefCaseIcon.tsx"; -import LandingDoc from "../../components/icons/LandingDoc.tsx"; -import LandingSign from "../../components/icons/LandingSign.tsx"; +import { lazy, Suspense } from "react"; + +const BriefCaseIcon = lazy( + () => import("../../components/icons/BriefCaseIcon.tsx"), +); +const LandingDoc = lazy(() => import("../../components/icons/LandingDoc.tsx")); +const LandingSign = lazy( + () => import("../../components/icons/LandingSign.tsx"), +); const LandingDocSection = () => { return ( @@ -9,7 +15,13 @@ const LandingDocSection = () => {
- + + } + > + +
@@ -26,8 +38,20 @@ const LandingDocSection = () => {
- - + + } + > + + + + } + > + +
); diff --git a/src/pages/landing/LandingIntroLazy.tsx b/src/pages/landing/LandingIntroLazy.tsx index 49e08f0..e8845f9 100644 --- a/src/pages/landing/LandingIntroLazy.tsx +++ b/src/pages/landing/LandingIntroLazy.tsx @@ -8,16 +8,16 @@ const LandingIntroLazy = () => { const { ref, inView } = useInView({ triggerOnce: true, threshold: 0.2 }); return ( -
- {inView && ( +
+ {inView ? ( μ†Œκ°œ μ„Ήμ…˜ λΆˆλŸ¬μ˜€λŠ” 쀑...
+
μ†Œκ°œ μ„Ήμ…˜ λΆˆλŸ¬μ˜€λŠ” 쀑...
} > - )} + ) : null}
); }; diff --git a/src/pages/landing/LandingIntroSection.tsx b/src/pages/landing/LandingIntroSection.tsx index b7c592b..c4f337d 100644 --- a/src/pages/landing/LandingIntroSection.tsx +++ b/src/pages/landing/LandingIntroSection.tsx @@ -1,7 +1,19 @@ // src/pages/LandingIntroSection.tsx -import LandingProblem from "../../components/icons/LandingProblem.tsx"; -import LandingArrow from "../../components/icons/LandingArrow.tsx"; -import LandingGroup from "../../components/icons/LandingGroup.tsx"; +import { lazy, Suspense } from "react"; + +const LandingProblem = lazy( + () => import("../../components/icons/LandingProblem.tsx"), +); +const LandingArrow = lazy( + () => import("../../components/icons/LandingArrow.tsx"), +); +const LandingGroup = lazy( + () => import("../../components/icons/LandingGroup.tsx"), +); + +const IconSkeleton = ({ height = "h-48" }: { height?: string }) => ( +
+); const LandingIntroSection = () => { return ( @@ -10,9 +22,15 @@ const LandingIntroSection = () => { λ§κ³ λ³΄μŠ€λŠ”
μ΄λ ‡κ²Œ λ§Œλ“€μ–΄μ‘Œμ–΄μš”!
- - - + }> + + + }> + + + }> + +
); }; diff --git a/src/pages/landing/LandingPayLazy.tsx b/src/pages/landing/LandingPayLazy.tsx index 7cc6948..b08d05f 100644 --- a/src/pages/landing/LandingPayLazy.tsx +++ b/src/pages/landing/LandingPayLazy.tsx @@ -8,16 +8,16 @@ const LandingPayLazy = () => { const { ref, inView } = useInView({ triggerOnce: true, threshold: 0.2 }); return ( -
- {inView && ( +
+ {inView ? ( κΈ‰μ—¬ 정보 λΆˆλŸ¬μ˜€λŠ” 쀑...
+
κΈ‰μ—¬ 정보 λΆˆλŸ¬μ˜€λŠ” 쀑...
} > - )} + ) : null}
); }; diff --git a/src/pages/landing/LandingPaySection.tsx b/src/pages/landing/LandingPaySection.tsx index 68af8aa..dd97b27 100644 --- a/src/pages/landing/LandingPaySection.tsx +++ b/src/pages/landing/LandingPaySection.tsx @@ -1,6 +1,10 @@ // src/pages/LandingPaySection.tsx -import MoneyIcon from "../../components/icons/MoneyIcon.tsx"; -import LandingCoin from "../../components/icons/LandingCoin.tsx"; +import { lazy, Suspense } from "react"; + +const MoneyIcon = lazy(() => import("../../components/icons/MoneyIcon.tsx")); +const LandingCoin = lazy( + () => import("../../components/icons/LandingCoin.tsx"), +); const LandingPaySection = () => { return ( @@ -8,7 +12,13 @@ const LandingPaySection = () => {
- + + } + > + +
@@ -25,7 +35,13 @@ const LandingPaySection = () => {
- + + } + > + +
); diff --git a/vite.config.ts b/vite.config.ts index 4cfbe78..ae90b52 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -50,6 +50,10 @@ export default defineConfig({ if (id.includes("react-hook-form")) return "vendor-form"; if (id.includes("zod")) return "vendor-zod"; if (id.includes("axios")) return "vendor-axios"; + // λŒ€ν˜• 라이브러리 별도 뢄리: vendor-others κ³ΌλΆ€ν•˜ λ°©μ§€ + if (id.includes("firebase")) return "vendor-firebase"; + if (id.includes("chart.js") || id.includes("recharts")) return "vendor-charts"; + if (id.includes("tosspayments")) return "vendor-toss"; return "vendor-others"; }