"use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { MenuItem, MenuState } from "@/types/menu"; import { apiClient } from "@/lib/api/client"; /** * 메뉴 관련 비즈니스 로직을 관리하는 커스텀 훅 * - API 호출은 apiClient를 통해 수행 (토큰 관리/401 처리 자동) * - 메뉴 로드 실패 시 토큰 삭제/리다이렉트하지 않음 (client.ts가 처리) */ export const useMenu = (user: any, authLoading: boolean) => { const router = useRouter(); const [menuState, setMenuState] = useState({ menuList: [], expandedMenus: new Set(), isLoading: true, }); /** * 데이터 키를 대문자로 변환하는 함수 */ const convertToUpperCaseKeys = useCallback((data: Record[]): MenuItem[] => { return data.map((item) => { const converted: Record = {}; Object.keys(item).forEach((key) => { const upperKey = key.toUpperCase(); converted[upperKey] = item[key]; }); return converted as unknown as MenuItem; }); }, []); /** * 메뉴 트리 구조 생성 */ const buildMenuTree = useCallback((menuItems: MenuItem[]): MenuItem[] => { const menuMap = new Map(); const rootMenus: MenuItem[] = []; menuItems.forEach((menu) => { const objId = String(menu.OBJID); const parentId = String(menu.PARENT_OBJ_ID); menuMap.set(objId, { ...menu, OBJID: objId, PARENT_OBJ_ID: parentId, children: [] }); }); menuItems.forEach((menu) => { const objId = String(menu.OBJID); const parentId = String(menu.PARENT_OBJ_ID); const menuItem = menuMap.get(objId)!; if (parentId !== "-395553955") { const parent = menuMap.get(parentId); if (parent) { parent.children = parent.children || []; parent.children.push(menuItem); } } else { rootMenus.push(menuItem); } }); return rootMenus.sort((a, b) => (a.SEQ || 0) - (b.SEQ || 0)); }, []); /** * 메뉴 데이터 로드 * - apiClient 사용으로 토큰/401 자동 처리 * - 실패 시 빈 메뉴 유지 (로그인 리다이렉트는 client.ts 인터셉터가 담당) */ const loadMenuData = useCallback(async () => { try { const response = await apiClient.get("/admin/user-menus"); if (response.data?.success && response.data?.data) { const convertedMenuData = convertToUpperCaseKeys(response.data.data || []); setMenuState((prev: MenuState) => ({ ...prev, menuList: buildMenuTree(convertedMenuData), isLoading: false, })); } else { setMenuState((prev: MenuState) => ({ ...prev, isLoading: false })); } } catch { // API 실패 시 빈 메뉴로 유지 (401은 client.ts 인터셉터가 리다이렉트 처리) setMenuState((prev: MenuState) => ({ ...prev, isLoading: false })); } }, [convertToUpperCaseKeys, buildMenuTree]); /** * 메뉴 토글 */ const toggleMenu = useCallback((menuId: string) => { setMenuState((prev: MenuState) => { const newExpanded = new Set(prev.expandedMenus); if (newExpanded.has(menuId)) { newExpanded.delete(menuId); } else { newExpanded.add(menuId); } return { ...prev, expandedMenus: newExpanded, }; }); }, []); /** * 메뉴 클릭 처리 */ const handleMenuClick = useCallback( async (menu: MenuItem) => { if (menu.children && menu.children.length > 0) { toggleMenu(String(menu.OBJID)); } else { const menuName = menu.MENU_NAME_KOR || menu.menuNameKor || menu.TRANSLATED_NAME || "메뉴"; if (typeof window !== "undefined") { localStorage.setItem("currentMenuName", menuName); } try { const menuObjid = menu.OBJID || menu.objid; if (menuObjid) { const { menuScreenApi } = await import("@/lib/api/screen"); const assignedScreens = await menuScreenApi.getScreensByMenu(parseInt(menuObjid.toString())); if (assignedScreens.length > 0) { const firstScreen = assignedScreens[0]; router.push(`/screens/${firstScreen.screenId}?menuObjid=${menuObjid}`); return; } } } catch (error) { console.warn("할당된 화면 조회 실패:", error); } if (menu.MENU_URL) { router.push(menu.MENU_URL); } else { console.warn("메뉴에 URL이나 할당된 화면이 없습니다:", menu); const { toast } = await import("sonner"); toast.warning("이 메뉴에는 연결된 페이지나 화면이 없습니다."); } } }, [toggleMenu, router], ); useEffect(() => { if (user && !authLoading) { loadMenuData(); } }, [user, authLoading, loadMenuData]); return { menuList: menuState.menuList, expandedMenus: menuState.expandedMenus, isMenuLoading: menuState.isLoading, handleMenuClick, toggleMenu, refreshMenus: loadMenuData, }; };