diff --git a/backend-node/src/controllers/multilangController.ts b/backend-node/src/controllers/multilangController.ts index 738d486d..ec5fa949 100644 --- a/backend-node/src/controllers/multilangController.ts +++ b/backend-node/src/controllers/multilangController.ts @@ -763,12 +763,22 @@ export const getBatchTranslations = async ( ): Promise => { try { const { companyCode, menuCode, userLang } = req.query; - const { langKeys } = req.body; + const { + langKeys, + companyCode: bodyCompanyCode, + menuCode: bodyMenuCode, + userLang: bodyUserLang, + } = req.body; + + // query params에서 읽지 못한 경우 body에서 읽기 + const finalCompanyCode = companyCode || bodyCompanyCode; + const finalMenuCode = menuCode || bodyMenuCode; + const finalUserLang = userLang || bodyUserLang; logger.info("다국어 텍스트 배치 조회 요청", { - companyCode, - menuCode, - userLang, + companyCode: finalCompanyCode, + menuCode: finalMenuCode, + userLang: finalUserLang, keyCount: langKeys?.length || 0, user: req.user, }); @@ -785,7 +795,7 @@ export const getBatchTranslations = async ( return; } - if (!companyCode || !userLang) { + if (!finalCompanyCode || !finalUserLang) { res.status(400).json({ success: false, message: "companyCode와 userLang은 필수입니다.", @@ -809,9 +819,9 @@ export const getBatchTranslations = async ( try { const multiLangService = new MultiLangService(client); const translations = await multiLangService.getBatchTranslations({ - companyCode: companyCode as string, - menuCode: menuCode as string, - userLang: userLang as string, + companyCode: finalCompanyCode as string, + menuCode: finalMenuCode as string, + userLang: finalUserLang as string, langKeys, }); diff --git a/frontend/app/(main)/admin/layout.tsx b/frontend/app/(main)/admin/layout.tsx index 89738fb2..84882137 100644 --- a/frontend/app/(main)/admin/layout.tsx +++ b/frontend/app/(main)/admin/layout.tsx @@ -313,17 +313,13 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) } }, [userLang]); - // 컴포넌트 마운트 시 강제로 번역 로드 (userLang이 아직 설정되지 않았을 수 있음) + // 컴포넌트 마운트 시 userLang이 설정될 때까지 대기 useEffect(() => { - const timer = setTimeout(() => { - if (!userLang) { - console.log("🔄 Admin Layout 마운트 후 강제 번역 로드 (userLang 없음)"); - loadTranslations(); - } - }, 100); // 100ms 후 실행 - - return () => clearTimeout(timer); - }, []); // 컴포넌트 마운트 시 한 번만 실행 + if (userLang) { + console.log("🔄 userLang 설정됨, 번역 로드 시작:", userLang); + loadTranslations(); + } + }, [userLang]); // userLang이 설정될 때마다 실행 // 키보드 단축키로 사이드바 토글 useEffect(() => { @@ -359,11 +355,14 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) const loadTranslations = async () => { try { - // 현재 사용자 언어 사용 - const currentUserLang = userLang || "en"; + // userLang이 설정되지 않았으면 번역 로드하지 않음 + if (!userLang) { + console.log("⏳ userLang이 설정되지 않음, 번역 로드 대기"); + return; + } + console.log("🌐 Admin Layout 번역 로드 시작", { userLang, - currentUserLang, }); // API 직접 호출로 현재 언어 사용 (배치 조회 방식) @@ -380,7 +379,7 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) params: { companyCode, menuCode: "MENU_MANAGEMENT", - userLang: currentUserLang, + userLang: userLang, }, }, ); @@ -392,24 +391,45 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) translations[MENU_MANAGEMENT_KEYS.DESCRIPTION] || "시스템의 메뉴 구조와 권한을 관리합니다."; // 번역 캐시에 저장 - setTranslationCache(currentUserLang, translations); + setTranslationCache(userLang, translations); // 상태 업데이트 setMenuTranslations({ title, description }); - console.log("🌐 Admin Layout 번역 로드 완료 (배치)", { title, description, userLang: currentUserLang }); + console.log("🌐 Admin Layout 번역 로드 완료 (배치)", { title, description, userLang }); } else { - // 기본값 사용 - const title = "메뉴 관리"; - const description = "시스템의 메뉴 구조와 권한을 관리합니다."; + // 전역 사용자 로케일 확인하여 기본값 설정 + const globalUserLang = (window as any).__GLOBAL_USER_LANG || localStorage.getItem("userLocale") || "KR"; + console.log("🌐 전역 사용자 로케일 확인:", globalUserLang); + + // 사용자 로케일에 따른 기본값 설정 + let title, description; + if (globalUserLang === "US") { + title = "Menu Management"; + description = "Manage system menu structure and permissions"; + } else { + title = "메뉴 관리"; + description = "시스템의 메뉴 구조와 권한을 관리합니다."; + } + setMenuTranslations({ title, description }); - console.log("🌐 Admin Layout 기본값 사용", { title, description, userLang: currentUserLang }); + console.log("🌐 Admin Layout 기본값 사용", { title, description, userLang: globalUserLang }); } } catch (error) { console.error("❌ Admin Layout 배치 번역 로드 실패:", error); - // 오류 시 기본값 사용 - const title = "메뉴 관리"; - const description = "시스템의 메뉴 구조와 권한을 관리합니다."; + // 오류 시에도 전역 사용자 로케일 확인하여 기본값 설정 + const globalUserLang = (window as any).__GLOBAL_USER_LANG || localStorage.getItem("userLocale") || "KR"; + console.log("🌐 오류 시 전역 사용자 로케일 확인:", globalUserLang); + + let title, description; + if (globalUserLang === "US") { + title = "Menu Management"; + description = "Manage system menu structure and permissions"; + } else { + title = "메뉴 관리"; + description = "시스템의 메뉴 구조와 권한을 관리합니다."; + } + setMenuTranslations({ title, description }); } } catch (error) { @@ -510,11 +530,6 @@ export default function AdminLayout({ children }: { children: React.ReactNode })

인증 실패

토큰이 없습니다. 3초 후 로그인 페이지로 이동합니다.

-
-

디버깅 정보

-

현재 경로: {pathname}

-

토큰: {localStorage.getItem("authToken") ? "존재" : "없음"}

-
)} diff --git a/frontend/components/admin/MenuFormModal.tsx b/frontend/components/admin/MenuFormModal.tsx index 3770a095..a2eb5ceb 100644 --- a/frontend/components/admin/MenuFormModal.tsx +++ b/frontend/components/admin/MenuFormModal.tsx @@ -345,58 +345,79 @@ export const MenuFormModal: React.FC = ({ const selectedLangKeyInfo = getSelectedLangKeyInfo(); + // 전역 사용자 로케일 가져오기 + const getCurrentUserLang = () => { + return (window as any).__GLOBAL_USER_LANG || localStorage.getItem("userLocale") || "KR"; + }; + return ( {isEdit - ? getMenuTextSync(MENU_MANAGEMENT_KEYS.MODAL_MENU_MODIFY_TITLE) - : getMenuTextSync(MENU_MANAGEMENT_KEYS.MODAL_MENU_REGISTER_TITLE)} + ? getMenuTextSync(MENU_MANAGEMENT_KEYS.MODAL_MENU_MODIFY_TITLE, getCurrentUserLang()) + : getMenuTextSync(MENU_MANAGEMENT_KEYS.MODAL_MENU_REGISTER_TITLE, getCurrentUserLang())}
- +
- +
- + {!isEdit && level !== 1 && ( -

{getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_COMPANY_SUBMENU_NOTE)}

+

+ {getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_COMPANY_SUBMENU_NOTE, getCurrentUserLang())} +

)}
- +
- + handleInputChange("menuNameKor", e.target.value)} - placeholder={getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_MENU_NAME_PLACEHOLDER)} + placeholder={getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_MENU_NAME_PLACEHOLDER, getCurrentUserLang())} required />
- + handleInputChange("menuUrl", e.target.value)} - placeholder={getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_MENU_URL_PLACEHOLDER)} + placeholder={getMenuTextSync(MENU_MANAGEMENT_KEYS.FORM_MENU_URL_PLACEHOLDER, getCurrentUserLang())} />
- +