Files
sam-react/MIGRATION_PLAN.md
hskwon a524e3c9e2 feat: 역할별 메뉴 시스템 및 비즈니스 컴포넌트 라우팅 통합
- 5가지 역할(CEO, ProductionManager, Worker, SystemAdmin, Sales) 지원
- 역할별 메뉴 구성 및 동적 전환 기능
- 사이드바 컴포넌트 분리 (Sidebar.tsx)
- sam_prototype 디자인 완전 통합 (네이티브 select 사용)
- 비즈니스 컴포넌트 라우팅 연결 (lazy loading)
- PlaceholderPage 추가 (미구현 페이지용)
- BOMManagement Figma 에셋 제거 및 플레이스홀더 적용
2025-10-20 16:49:12 +09:00

20 KiB

sam_prototype → react 마이그레이션 실행 계획

작성일: 2025-10-17 (목) 예상 소요 시간: 4시간 완료 목표: 오늘 오후 (로컬 및 개발 서버 실행 완료)


📊 현황 분석 요약

sam_prototype (원본)

  • 위치: /Users/hskwon/Works/@KD_SAM/SAM/sam_prototype/
  • 기술 스택: React 18.3.1, Vite 6.3.5
  • UI 라이브러리: Radix UI 23개 패키지 (shadcn/ui 완벽 구현)
  • 컴포넌트: 90개 (UI 40개 + 비즈니스 50개)
  • 디자인 시스템: Clean Glass Design + 3가지 테마 (light/dark/senior)
  • 폰트: Pretendard 한글 폰트
  • 상태 관리: 없음 (로컬 useState만)
  • 라우팅: 없음 (조건부 렌더링)

react (타겟)

  • 위치: /Users/hskwon/Works/@KD_SAM/SAM/react/
  • 기술 스택: React 19.1.1, Vite 7.x, TypeScript 5.x
  • 준비된 것: Zustand, React Query v5, React Router v7, Tailwind CSS 4.x
  • 현재 상태: 거의 빈 프로젝트 (DemoPage만 존재)

핵심 의사결정 (완료됨)

1. React 버전: 19.1.1

  • 근거: Radix UI 공식 React 19 지원 확인 (Context7 MCP로 검증)
  • 리스크: 낮음 (모든 라이브러리 호환 확인)

2. 컴포넌트 이전 방식: 하이브리드 (Copy + Refactor)

  • UI 컴포넌트 40개: 복사 + TypeScript 타입만 추가
  • 비즈니스 컴포넌트 50개: 복사 + 구조 개선

3. 테마 시스템: Tailwind CSS 4.x Native

  • next-themes 제거 (Vite에서 불필요)
  • CSS 변수 + Zustand themeStore 사용

4. 상태 관리: Zustand (4개) + React Query

  • themeStore: light/dark/senior
  • authStore: 인증/사용자
  • menuStore: 메뉴/사이드바
  • demoStore: 데모 모드

5. API 통합: 하이브리드 (Mock → 점진적 통합)

  • 초기에는 Mock 데이터로 작동
  • 나중에 SAM API 연동

🚀 5단계 실행 계획 (총 4시간)

Phase 1: 환경 구축 (30분)

1.1 의존성 설치

cd /Users/hskwon/Works/@KD_SAM/SAM/react

# React 19 (이미 설치되어 있음 - 확인만)
# npm install react@19.1.1 react-dom@19.1.1

# Radix UI 전체 패키지 설치 (23개)
npm install \
  @radix-ui/react-accordion@^1.2.3 \
  @radix-ui/react-alert-dialog@^1.1.6 \
  @radix-ui/react-aspect-ratio@^1.1.2 \
  @radix-ui/react-avatar@^1.1.3 \
  @radix-ui/react-checkbox@^1.1.4 \
  @radix-ui/react-collapsible@^1.1.3 \
  @radix-ui/react-context-menu@^2.2.6 \
  @radix-ui/react-dialog@^1.1.6 \
  @radix-ui/react-dropdown-menu@^2.1.6 \
  @radix-ui/react-hover-card@^1.1.6 \
  @radix-ui/react-label@^2.1.2 \
  @radix-ui/react-menubar@^1.1.6 \
  @radix-ui/react-navigation-menu@^1.2.5 \
  @radix-ui/react-popover@^1.1.6 \
  @radix-ui/react-progress@^1.1.2 \
  @radix-ui/react-radio-group@^1.2.3 \
  @radix-ui/react-scroll-area@^1.2.3 \
  @radix-ui/react-select@^2.1.6 \
  @radix-ui/react-separator@^1.1.2 \
  @radix-ui/react-slider@^1.2.3 \
  @radix-ui/react-slot@^1.1.2 \
  @radix-ui/react-switch@^1.1.3 \
  @radix-ui/react-tabs@^1.1.3 \
  @radix-ui/react-toggle@^1.1.2 \
  @radix-ui/react-toggle-group@^1.1.2 \
  @radix-ui/react-tooltip@^1.1.8

# 기타 필수 패키지
npm install \
  class-variance-authority \
  cmdk@^1.1.1 \
  embla-carousel-react@^8.6.0 \
  input-otp@^1.4.2 \
  react-day-picker@^8.10.1 \
  react-resizable-panels@^2.1.7 \
  recharts@^2.15.2 \
  sonner@^2.0.3 \
  vaul@^1.1.2

# react-hook-form 버전 업그레이드 (현재 ^7.65.0 → ^7.55.0 이상)
# 이미 설치되어 있으므로 스킵

1.2 디렉토리 구조 생성

mkdir -p src/components/ui
mkdir -p src/components/business/{dashboard,production,sales,material,quality,master,system,hr,accounting,common}
mkdir -p src/components/landing
mkdir -p src/components/shared
mkdir -p src/app/layouts
mkdir -p src/styles/themes
mkdir -p public/fonts/pretendard

1.3 설정 파일 작성

tailwind.config.js 확장:

export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      colors: {
        border: 'var(--color-border)',
        input: 'var(--color-input)',
        ring: 'var(--color-ring)',
        background: 'var(--color-background)',
        foreground: 'var(--color-foreground)',
        primary: {
          DEFAULT: 'var(--color-primary)',
          foreground: 'var(--color-primary-foreground)',
        },
        secondary: {
          DEFAULT: 'var(--color-secondary)',
          foreground: 'var(--color-secondary-foreground)',
        },
        muted: {
          DEFAULT: 'var(--color-muted)',
          foreground: 'var(--color-muted-foreground)',
        },
        accent: {
          DEFAULT: 'var(--color-accent)',
          foreground: 'var(--color-accent-foreground)',
        },
        destructive: {
          DEFAULT: 'var(--color-destructive)',
          foreground: 'var(--color-destructive-foreground)',
        },
        card: {
          DEFAULT: 'var(--color-card)',
          foreground: 'var(--color-card-foreground)',
        },
        popover: {
          DEFAULT: 'var(--color-popover)',
          foreground: 'var(--color-popover-foreground)',
        },
        'sidebar': {
          DEFAULT: 'var(--color-sidebar)',
          foreground: 'var(--color-sidebar-foreground)',
          primary: 'var(--color-sidebar-primary)',
          'primary-foreground': 'var(--color-sidebar-primary-foreground)',
          accent: 'var(--color-sidebar-accent)',
          'accent-foreground': 'var(--color-sidebar-accent-foreground)',
          border: 'var(--color-sidebar-border)',
          ring: 'var(--color-sidebar-ring)',
        },
        chart: {
          1: 'var(--color-chart-1)',
          2: 'var(--color-chart-2)',
          3: 'var(--color-chart-3)',
          4: 'var(--color-chart-4)',
          5: 'var(--color-chart-5)',
        },
      },
      borderRadius: {
        lg: 'var(--radius)',
        md: 'calc(var(--radius) - 2px)',
        sm: 'calc(var(--radius) - 4px)',
      },
      fontFamily: {
        sans: ['Pretendard Variable', 'ui-sans-serif', 'system-ui', 'sans-serif'],
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
}

사용자 컨펌 포인트 #1: npm install 및 디렉토리 생성 완료 확인


Phase 2: 디자인 시스템 이전 (1시간)

2.1 Pretendard 폰트 설정

# public/fonts/pretendard/ 에 폰트 파일이 있는지 확인
# 없으면 sam_prototype에서 복사하거나 CDN 사용

2.2 Clean Glass CSS 이전

src/index.css 전체 교체:

  • sam_prototype/src/styles/globals.css 내용 복사
  • Tailwind v4 @theme 문법으로 변환
  • 3가지 테마 CSS 변수 정의

주요 내용:

@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css');
@import "tailwindcss";

@theme {
  /* Light Mode 변수 */
  --color-background: #FAFAFA;
  --color-foreground: #1A1A1A;
  --color-primary: #3B82F6;
  /* ... 모든 색상 변수 */

  --radius: 0.75rem;
  --clean-blur: blur(8px);
  /* ... 기타 변수 */
}

.dark {
  /* Dark Mode 변수 */
}

.senior {
  /* Senior Mode 변수 */
  --font-size-base: 1.125rem;
  --button-min-height: 3.5rem;
  /* ... 고령자 접근성 변수 */
}

/* Clean Glass 스타일 */
.clean-glass {
  backdrop-filter: var(--clean-blur);
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid var(--border);
}

/* ... 기타 Clean Design 스타일 */

2.3 Zustand themeStore 구현

src/stores/themeStore.ts:

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface ThemeState {
  theme: 'light' | 'dark' | 'senior';
  setTheme: (theme: 'light' | 'dark' | 'senior') => void;
  toggleTheme: () => void;
}

export const useThemeStore = create<ThemeState>()(
  persist(
    (set, get) => ({
      theme: 'light',

      setTheme: (theme) => {
        // DOM 클래스 업데이트
        document.documentElement.className = theme === 'light' ? '' : theme;
        set({ theme });
      },

      toggleTheme: () => {
        const themes = ['light', 'dark', 'senior'] as const;
        const currentIndex = themes.indexOf(get().theme);
        const nextTheme = themes[(currentIndex + 1) % 3];
        get().setTheme(nextTheme);
      },
    }),
    {
      name: 'sam-theme',
      onRehydrateStorage: () => (state) => {
        // 초기 로드 시 테마 적용
        if (state?.theme) {
          document.documentElement.className = state.theme === 'light' ? '' : state.theme;
        }
      },
    }
  )
);

src/hooks/useTheme.ts:

import { useEffect } from 'react';
import { useThemeStore } from '@/stores/themeStore';

export const useTheme = () => {
  const { theme, setTheme } = useThemeStore();

  useEffect(() => {
    // 초기 로드 시 테마 적용
    document.documentElement.className = theme === 'light' ? '' : theme;
  }, [theme]);

  return { theme, setTheme };
};

사용자 컨펌 포인트 #2: CSS 및 테마 시스템 작동 확인 (light/dark/senior 전환)


Phase 3: 컴포넌트 대량 이전 (1.5시간)

3.1 UI 컴포넌트 40개 이전

복사할 파일 목록:

sam_prototype/src/components/ui/*.tsx → react/src/components/ui/

accordion.tsx
alert.tsx
alert-dialog.tsx
aspect-ratio.tsx
avatar.tsx
badge.tsx
breadcrumb.tsx
button.tsx
calendar.tsx
card.tsx
carousel.tsx
chart.tsx
checkbox.tsx
collapsible.tsx
command.tsx
context-menu.tsx
dialog.tsx
drawer.tsx
dropdown-menu.tsx
form.tsx
hover-card.tsx
input.tsx
input-otp.tsx
label.tsx
menubar.tsx
navigation-menu.tsx
pagination.tsx
popover.tsx
progress.tsx
radio-group.tsx
resizable.tsx
scroll-area.tsx
select.tsx
separator.tsx
sheet.tsx
sidebar.tsx
skeleton.tsx
slider.tsx
sonner.tsx
switch.tsx
table.tsx
tabs.tsx
textarea.tsx
toggle.tsx
toggle-group.tsx
tooltip.tsx

자동 처리 작업:

  1. 파일 복사
  2. Import 경로 수정 (../lib/utils@/lib/utils)
  3. TypeScript 타입 추가 (Props interface 정의)

3.2 비즈니스 컴포넌트 50개 이전

복사할 파일 목록 (도메인별):

dashboard/

  • Dashboard.tsx
  • SalesLeadDashboard.tsx
  • SystemAdminDashboard.tsx

landing/

  • LandingPage.tsx
  • LoginPage.tsx
  • SignupPage.tsx
  • DemoRequestPage.tsx

production/

  • ProductionManagement.tsx
  • WorkerPerformance.tsx

sales/

  • SalesManagement.tsx
  • SalesManagement-clean.tsx
  • QuoteCreation.tsx
  • QuoteSimulation.tsx

material/

  • MaterialManagement.tsx
  • LotManagement.tsx
  • ReceivingWrite.tsx
  • ShippingManagement.tsx

quality/

  • QualityManagement.tsx

master/

  • MasterData.tsx
  • BOMManagement.tsx
  • ItemManagement.tsx
  • ProductManagement.tsx
  • PricingManagement.tsx
  • CodeManagement.tsx

system/

  • SystemManagement.tsx
  • UserManagement.tsx
  • MenuCustomization.tsx
  • MenuCustomizationGuide.tsx

hr/

  • HRManagement.tsx

accounting/

  • AccountingManagement.tsx
  • ApprovalManagement.tsx

common/

  • Board.tsx
  • Reports.tsx
  • EquipmentManagement.tsx
  • OrderManagement.tsx
  • DrawingCanvas.tsx
  • ContactModal.tsx

figma/

  • ImageWithFallback.tsx

자동 처리 작업:

  1. 파일 복사 및 도메인별 폴더 배치
  2. Import 경로 수정 (./components/ui/@/components/ui/)
  3. TypeScript Props 타입 추가
  4. useState → 유지 (나중에 Zustand 전환)

사용자 컨펌 포인트 #3: 주요 컴포넌트 렌더링 확인 (Dashboard, LoginPage 등)


Phase 4: 상태 관리 & 라우팅 (1시간)

4.1 Zustand Stores 구현

src/stores/menuStore.ts:

import { create } from 'zustand';

interface MenuState {
  activeMenu: string;
  expandedMenus: string[];
  isSidebarCollapsed: boolean;
  isMobileSidebarOpen: boolean;

  setActiveMenu: (menuId: string) => void;
  toggleMenu: (menuId: string) => void;
  toggleSidebar: () => void;
  closeMobileSidebar: () => void;
}

export const useMenuStore = create<MenuState>((set) => ({
  activeMenu: 'dashboard',
  expandedMenus: [],
  isSidebarCollapsed: false,
  isMobileSidebarOpen: false,

  setActiveMenu: (menuId) => set({ activeMenu: menuId }),

  toggleMenu: (menuId) => set((state) => ({
    expandedMenus: state.expandedMenus.includes(menuId)
      ? state.expandedMenus.filter((id) => id !== menuId)
      : [...state.expandedMenus, menuId],
  })),

  toggleSidebar: () => set((state) => ({
    isSidebarCollapsed: !state.isSidebarCollapsed,
  })),

  closeMobileSidebar: () => set({ isMobileSidebarOpen: false }),
}));

src/stores/demoStore.ts:

import { create } from 'zustand';

interface DemoConfig {
  token: string;
  expiresAt: Date;
  userName?: string;
  companyName?: string;
}

interface DemoState {
  isDemoMode: boolean;
  demoConfig: DemoConfig | null;

  activateDemo: (config: DemoConfig) => void;
  deactivateDemo: () => void;
  checkDemoExpiry: () => boolean;
}

export const useDemoStore = create<DemoState>((set, get) => ({
  isDemoMode: false,
  demoConfig: null,

  activateDemo: (config) => set({
    isDemoMode: true,
    demoConfig: config,
  }),

  deactivateDemo: () => set({
    isDemoMode: false,
    demoConfig: null,
  }),

  checkDemoExpiry: () => {
    const { demoConfig } = get();
    if (!demoConfig) return false;

    const isExpired = new Date() > new Date(demoConfig.expiresAt);
    if (isExpired) {
      get().deactivateDemo();
    }
    return !isExpired;
  },
}));

4.2 레이아웃 컴포넌트 작성

src/app/layouts/RootLayout.tsx:

import { Outlet } from 'react-router-dom';
import { useTheme } from '@/hooks/useTheme';

export function RootLayout() {
  const { theme } = useTheme();

  return (
    <div className={theme}>
      <Outlet />
    </div>
  );
}

src/app/layouts/DashboardLayout.tsx:

import { Outlet } from 'react-router-dom';
import { Sidebar } from '@/components/shared/Sidebar';
import { useMenuStore } from '@/stores/menuStore';

export function DashboardLayout() {
  const { isSidebarCollapsed } = useMenuStore();

  return (
    <div className="flex min-h-screen bg-background">
      <Sidebar />
      <main className={`flex-1 ${isSidebarCollapsed ? 'ml-20' : 'ml-64'} transition-all`}>
        <Outlet />
      </main>
    </div>
  );
}

4.3 React Router 설정

src/App.tsx 수정:

import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { queryClient } from '@/lib/query-client';
import { useAuthStore } from '@/stores/auth';

// Layouts
import { RootLayout } from '@/app/layouts/RootLayout';
import { DashboardLayout } from '@/app/layouts/DashboardLayout';
import { AuthLayout } from '@/app/layouts/AuthLayout';

// Pages - Landing
import { LandingPage } from '@/components/landing/LandingPage';
import { LoginPage } from '@/components/landing/LoginPage';
import { SignupPage } from '@/components/landing/SignupPage';
import { DemoRequestPage } from '@/components/landing/DemoRequestPage';

// Pages - Dashboard
import { Dashboard } from '@/components/business/dashboard/Dashboard';
import { SalesLeadDashboard } from '@/components/business/dashboard/SalesLeadDashboard';

// Pages - Production
import { ProductionManagement } from '@/components/business/production/ProductionManagement';

// ... 기타 import

function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated } = useAuthStore();
  return isAuthenticated ? <>{children}</> : <Navigate to="/login" />;
}

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <BrowserRouter>
        <Routes>
          <Route element={<RootLayout />}>
            {/* Public Routes */}
            <Route path="/" element={<LandingPage />} />
            <Route path="/login" element={<LoginPage />} />
            <Route path="/signup" element={<SignupPage />} />
            <Route path="/demo-request" element={<DemoRequestPage />} />
            <Route path="/d/:token" element={<DemoPage />} />

            {/* Protected Routes */}
            <Route path="/dashboard" element={<ProtectedRoute><DashboardLayout /></ProtectedRoute>}>
              <Route index element={<Dashboard />} />
              <Route path="sales-leads" element={<SalesLeadDashboard />} />
            </Route>

            <Route path="/production" element={<ProtectedRoute><DashboardLayout /></ProtectedRoute>}>
              <Route index element={<ProductionManagement />} />
            </Route>

            {/* ... 기타 라우트 */}
          </Route>
        </Routes>
      </BrowserRouter>
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

export default App;

사용자 컨펌 포인트 #4: 라우팅 작동 확인 (/, /login, /dashboard 등 페이지 전환)


Phase 5: 통합 & 테스트 (30분)

5.1 빌드 테스트

npm run build

# 예상 결과:
# ✓ built in XXXms
# dist/index.html
# dist/assets/*.css
# dist/assets/*.js

# 번들 크기 확인 (목표: < 500KB gzip)

5.2 개발 서버 실행

# 로컬
npm run dev

# Docker
cd ../docker
docker-compose restart react

5.3 주요 페이지 확인

테스트 체크리스트:

사용자 컨펌 포인트 #5: 최종 통합 테스트 완료 확인


📊 작업 완료 기준

필수 완료 항목

  • React 19 + Radix UI 전체 패키지 설치
  • 디렉토리 구조 생성 완료
  • Clean Glass CSS 이전 완료
  • 3가지 테마 작동 확인
  • UI 컴포넌트 40개 이전 완료
  • 비즈니스 컴포넌트 50개 이전 완료
  • 4개 Zustand stores 구현 완료
  • React Router 설정 완료
  • 레이아웃 컴포넌트 작성 완료
  • npm run build 성공
  • 로컬 개발 서버 실행 성공
  • dev.sam.kr 접속 성공

성공 지표

  • TypeScript 타입 에러 없음
  • 빌드 성공
  • 주요 페이지 렌더링 확인
  • 테마 전환 작동
  • 라우팅 작동

🚧 알려진 제한사항 (향후 작업)

즉시 작동하지 않을 기능

  1. API 연동: 모든 컴포넌트는 Mock 데이터로 작동 (향후 React Query 훅으로 전환)
  2. 폼 검증: 일부 폼은 react-hook-form 미적용 상태
  3. 에러 처리: 전역 에러 핸들링 미구현
  4. 테스트: 단위 테스트 미작성
  5. 성능 최적화: React.lazy() 코드 스플리팅 미적용
  6. 접근성 검증: WCAG 2.1 AA 미검증

향후 개선 작업

  • SAM API 연동 (React Query 훅 작성)
  • 코드 스플리팅 (React.lazy + Suspense)
  • E2E 테스트 작성
  • 성능 최적화 (Lighthouse > 90)
  • 접근성 검증 및 개선

📝 컨펌 포인트 요약

각 Phase 완료 후 사용자 컨펌:

  1. Phase 1 완료: npm install 성공 확인
  2. Phase 2 완료: 테마 전환 작동 확인
  3. Phase 3 완료: 주요 컴포넌트 렌더링 확인
  4. Phase 4 완료: 라우팅 작동 확인
  5. Phase 5 완료: 최종 통합 테스트 완료

🔧 사용 도구

  • SuperClaude 페르소나: system-architect
  • MCP 서버: Sequential (의사결정), Context7 (Radix UI 문서)
  • 네이티브 도구: Read, Write, Edit, Bash, Glob

📚 참고 문서


작성자: Claude (AI Assistant) 검증자: 사용자 컨펌 필요 최종 업데이트: 2025-10-17 (목)