'use client'; import { createContext, useContext, useState, useEffect, useRef, ReactNode } from 'react'; import { performFullLogout } from '@/lib/auth/logout'; // ===== 타입 정의 ===== // ✅ 추가: 테넌트 타입 (실제 서버 응답 구조) export interface Tenant { id: number; // 테넌트 고유 ID (number) company_name: string; // 회사명 business_num: string; // 사업자번호 tenant_st_code: string; // 테넌트 상태 코드 (trial, active 등) options?: { // 테넌트 옵션 (선택) company_scale?: string; // 회사 규모 industry?: string; // 업종 }; } // ✅ 추가: 권한 타입 export interface Role { id: number; name: string; description: string; } // ✅ 추가: 메뉴 아이템 타입 export interface MenuItem { id: string; label: string; iconName: string; path: string; } // ✅ 수정: User 타입을 실제 서버 응답에 맞게 변경 export interface User { userId: string; // 사용자 ID (username 아님) name: string; // 사용자 이름 position: string; // 직책 roles: Role[]; // 권한 목록 (배열) tenant: Tenant; // ✅ 테넌트 정보 (필수!) menu: MenuItem[]; // 메뉴 목록 } // ❌ 삭제 예정: 기존 UserRole (더 이상 사용하지 않음) export type UserRole = 'CEO' | 'ProductionManager' | 'Worker' | 'SystemAdmin' | 'Sales'; // ===== Context 타입 ===== interface AuthContextType { users: User[]; currentUser: User | null; setCurrentUser: (user: User | null) => void; addUser: (user: User) => void; updateUser: (userId: string, updates: Partial) => void; deleteUser: (userId: string) => void; getUserByUserId: (userId: string) => User | undefined; logout: () => Promise; // ✅ 추가: 로그아웃 (완전한 캐시 정리) clearTenantCache: (tenantId: number) => void; // ✅ 추가: 테넌트 캐시 삭제 resetAllData: () => void; } // ===== 초기 데이터 ===== const initialUsers: User[] = [ { userId: "TestUser1", name: "김대표", position: "대표이사", roles: [ { id: 1, name: "ceo", description: "최고경영자" } ], tenant: { id: 282, company_name: "(주)테크컴퍼니", business_num: "123-45-67890", tenant_st_code: "trial" }, menu: [ { id: "13664", label: "시스템 대시보드", iconName: "layout-dashboard", path: "/dashboard" } ] }, { userId: "TestUser2", name: "박관리", position: "생산관리자", roles: [ { id: 2, name: "production_manager", description: "생산관리자" } ], tenant: { id: 282, company_name: "(주)테크컴퍼니", business_num: "123-45-67890", tenant_st_code: "trial" }, menu: [ { id: "13664", label: "시스템 대시보드", iconName: "layout-dashboard", path: "/dashboard" } ] }, { userId: "TestUser3", name: "드미트리", position: "시스템 관리자", roles: [ { id: 19, name: "system_manager", description: "시스템 관리자" } ], tenant: { id: 282, company_name: "(주)테크컴퍼니", business_num: "123-45-67890", tenant_st_code: "trial" }, menu: [ { id: "13664", label: "시스템 대시보드", iconName: "layout-dashboard", path: "/dashboard" } ] } ]; // ===== Context 생성 ===== const AuthContext = createContext(undefined); // ===== Provider 컴포넌트 ===== export function AuthProvider({ children }: { children: ReactNode }) { // 상태 관리 (SSR-safe: 항상 초기값으로 시작) const [users, setUsers] = useState(initialUsers); const [currentUser, setCurrentUser] = useState(null); // ✅ 추가: 이전 tenant.id 추적 (테넌트 전환 감지용) const previousTenantIdRef = useRef(null); // localStorage에서 초기 데이터 로드 (클라이언트에서만 실행) useEffect(() => { try { const savedUsers = localStorage.getItem('mes-users'); if (savedUsers) { setUsers(JSON.parse(savedUsers)); } const savedCurrentUser = localStorage.getItem('mes-currentUser'); if (savedCurrentUser) { setCurrentUser(JSON.parse(savedCurrentUser)); } } catch (error) { console.error('Failed to load auth data from localStorage:', error); // 손상된 데이터 제거 localStorage.removeItem('mes-users'); localStorage.removeItem('mes-currentUser'); } }, []); // localStorage 동기화 (상태 변경 시 자동 저장) useEffect(() => { localStorage.setItem('mes-users', JSON.stringify(users)); }, [users]); useEffect(() => { if (currentUser) { localStorage.setItem('mes-currentUser', JSON.stringify(currentUser)); } }, [currentUser]); // ✅ 추가: 테넌트 전환 감지 useEffect(() => { const prevTenantId = previousTenantIdRef.current; const currentTenantId = currentUser?.tenant?.id; if (prevTenantId && currentTenantId && prevTenantId !== currentTenantId) { console.log(`[Auth] Tenant changed: ${prevTenantId} → ${currentTenantId}`); clearTenantCache(prevTenantId); } previousTenantIdRef.current = currentTenantId || null; }, [currentUser?.tenant?.id]); // ✅ 추가: 테넌트별 캐시 삭제 함수 (SSR-safe) const clearTenantCache = (tenantId: number) => { // 서버 환경에서는 실행 안함 if (typeof window === 'undefined') return; const prefix = `mes-${tenantId}-`; // localStorage 캐시 삭제 Object.keys(localStorage).forEach(key => { if (key.startsWith(prefix)) { localStorage.removeItem(key); console.log(`[Cache] Cleared localStorage: ${key}`); } }); // sessionStorage 캐시 삭제 Object.keys(sessionStorage).forEach(key => { if (key.startsWith(prefix)) { sessionStorage.removeItem(key); console.log(`[Cache] Cleared sessionStorage: ${key}`); } }); }; // ✅ 추가: 로그아웃 함수 (완전한 캐시 정리) const logout = async () => { console.log('[Auth] Starting logout...'); // 1. React 상태 초기화 (UI 즉시 반영) setCurrentUser(null); // 2. 완전한 로그아웃 수행 (Zustand, sessionStorage, localStorage, 서버 API) await performFullLogout({ skipServerLogout: false, // 서버 API 호출 (HttpOnly 쿠키 삭제) redirectTo: null, // 리다이렉트는 호출하는 곳에서 처리 }); console.log('[Auth] Logout completed'); }; // Context value const value: AuthContextType = { users, currentUser, setCurrentUser, addUser: (user) => setUsers(prev => [...prev, user]), updateUser: (userId, updates) => setUsers(prev => prev.map(user => user.userId === userId ? { ...user, ...updates } : user) ), deleteUser: (userId) => setUsers(prev => prev.filter(user => user.userId !== userId)), getUserByUserId: (userId) => users.find(user => user.userId === userId), logout, clearTenantCache, resetAllData: () => { setUsers(initialUsers); setCurrentUser(null); } }; return {children}; } // ===== Custom Hook ===== export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } return context; }