import { apiClient } from "../api/client"; // 메뉴 관리 화면 다국어 키 상수 export const MENU_MANAGEMENT_KEYS = { // 기본 정보 TITLE: "menu.management.title", DESCRIPTION: "menu.management.description", MENU_TYPE_TITLE: "menu.type.title", MENU_TYPE_ADMIN: "menu.type.admin", MENU_TYPE_USER: "menu.type.user", ADMIN_MENU: "admin.menu", USER_MENU: "user.menu", ADMIN_DESCRIPTION: "admin.description", USER_DESCRIPTION: "user.description", LIST_TITLE: "list.title", LIST_TOTAL: "list.total", LIST_SEARCH_RESULT: "list.search.result", // 필터 관련 FILTER_COMPANY: "filter.company", FILTER_COMPANY_ALL: "filter.company.all", FILTER_COMPANY_COMMON: "filter.company.common", FILTER_COMPANY_SEARCH: "filter.company.search", FILTER_SEARCH: "filter.search", FILTER_SEARCH_PLACEHOLDER: "filter.search.placeholder", FILTER_RESET: "filter.reset", // 버튼 관련 BUTTON_ADD: "button.add", BUTTON_ADD_TOP_LEVEL: "button.add.top.level", BUTTON_ADD_SUB: "button.add.sub", BUTTON_EDIT: "button.edit", BUTTON_DELETE: "button.delete", BUTTON_DELETE_SELECTED: "button.delete.selected", BUTTON_DELETE_SELECTED_COUNT: "button.delete.selected.count", BUTTON_DELETE_PROCESSING: "button.delete.processing", BUTTON_CANCEL: "button.cancel", BUTTON_SAVE: "button.save", BUTTON_SAVE_PROCESSING: "button.save.processing", BUTTON_REGISTER: "button.register", BUTTON_MODIFY: "button.modify", // 폼 관련 FORM_MENU_TYPE: "form.menu.type", FORM_MENU_TYPE_ADMIN: "form.menu.type.admin", FORM_MENU_TYPE_USER: "form.menu.type.user", FORM_STATUS: "form.status", FORM_STATUS_ACTIVE: "form.status.active", FORM_STATUS_INACTIVE: "form.status.inactive", FORM_COMPANY: "form.company", FORM_COMPANY_SELECT: "form.company.select", FORM_COMPANY_COMMON: "form.company.common", FORM_COMPANY_SUBMENU_NOTE: "form.company.submenu.note", FORM_MENU_NAME: "form.menu.name", FORM_MENU_NAME_PLACEHOLDER: "form.menu.name.placeholder", FORM_MENU_URL: "form.menu.url", FORM_MENU_URL_PLACEHOLDER: "form.menu.url.placeholder", FORM_MENU_DESCRIPTION: "form.menu.description", FORM_MENU_DESCRIPTION_PLACEHOLDER: "form.menu.description.placeholder", FORM_MENU_SEQUENCE: "form.menu.sequence", FORM_LANG_KEY: "form.lang.key", FORM_LANG_KEY_SELECT: "form.lang.key.select", FORM_LANG_KEY_NONE: "form.lang.key.none", FORM_LANG_KEY_SEARCH: "form.lang.key.search", FORM_LANG_KEY_SELECTED: "form.lang.key.selected", // 모달 관련 MODAL_MENU_REGISTER_TITLE: "modal.menu.register.title", MODAL_MENU_MODIFY_TITLE: "modal.menu.modify.title", MODAL_DELETE_TITLE: "modal.delete.title", MODAL_DELETE_DESCRIPTION: "modal.delete.description", MODAL_DELETE_BATCH_DESCRIPTION: "modal.delete.batch.description", // 테이블 헤더 관련 TABLE_HEADER_SELECT: "table.header.select", TABLE_HEADER_MENU_NAME: "table.header.menu.name", TABLE_HEADER_MENU_URL: "table.header.menu.url", TABLE_HEADER_MENU_TYPE: "table.header.menu.type", TABLE_HEADER_STATUS: "table.header.status", TABLE_HEADER_COMPANY: "table.header.company", TABLE_HEADER_SEQUENCE: "table.header.sequence", TABLE_HEADER_ACTIONS: "table.header.actions", // 상태 관련 STATUS_ACTIVE: "status.active", STATUS_INACTIVE: "status.inactive", STATUS_UNSPECIFIED: "status.unspecified", // 메시지 관련 MESSAGE_LOADING: "message.loading", MESSAGE_MENU_DELETE_PROCESSING: "message.menu.delete.processing", MESSAGE_MENU_SAVE_SUCCESS: "message.menu.save.success", MESSAGE_MENU_SAVE_FAILED: "message.menu.save.failed", MESSAGE_MENU_DELETE_SUCCESS: "message.menu.delete.success", MESSAGE_MENU_DELETE_FAILED: "message.menu.delete.failed", MESSAGE_MENU_DELETE_BATCH_SUCCESS: "message.menu.delete.batch.success", MESSAGE_MENU_DELETE_BATCH_PARTIAL: "message.menu.delete.batch.partial", MESSAGE_MENU_STATUS_TOGGLE_SUCCESS: "message.menu.status.toggle.success", MESSAGE_MENU_STATUS_TOGGLE_FAILED: "message.menu.status.toggle.failed", MESSAGE_VALIDATION_MENU_NAME_REQUIRED: "message.validation.menu.name.required", MESSAGE_VALIDATION_COMPANY_REQUIRED: "message.validation.company.required", MESSAGE_VALIDATION_SELECT_MENU_DELETE: "message.validation.select.menu.delete", MESSAGE_ERROR_LOAD_MENU_LIST: "message.error.load.menu.list", MESSAGE_ERROR_LOAD_MENU_INFO: "message.error.load.menu.info", MESSAGE_ERROR_LOAD_COMPANY_LIST: "message.error.load.company.list", MESSAGE_ERROR_LOAD_LANG_KEY_LIST: "message.error.load.lang.key.list", // 기타 UI 요소 UI_EXPAND: "ui.expand", UI_COLLAPSE: "ui.collapse", UI_MENU_COLLAPSE: "ui.menu.collapse", UI_LANGUAGE: "ui.language", } as const; // 다국어 텍스트 캐시 (메모리 기반) const translationCache: Record> = {}; // 배치 조회를 위한 키 수집기 const pendingKeys: Set = new Set(); let batchTimeout: NodeJS.Timeout | null = null; const BATCH_DELAY = 50; // 50ms 지연으로 배치 처리 /** * 다국어 텍스트 배치 조회 * 여러 키를 한번에 조회하여 API 호출 횟수를 대폭 줄임 */ async function fetchBatchTranslations( keys: string[], companyCode: string = "*", menuCode: string = "MENU_MANAGEMENT", userLang: string = "KR", ): Promise> { try { console.log(`🚀 배치 조회 시작: ${keys.length}개 키`); const response = await apiClient.post( "/multilang/batch", { langKeys: keys, }, { params: { companyCode, menuCode, userLang, }, }, ); if (response.data.success) { console.log(`✅ 배치 조회 성공: ${keys.length}개 키`); return response.data.data || {}; } else { console.error("❌ 배치 조회 실패:", response.data.message); return {}; } } catch (error) { console.error("❌ 배치 조회 오류:", error); return {}; } } /** * 개별 다국어 텍스트 조회 (배치 처리) * 실제로는 배치로 처리되어 API 호출 횟수가 대폭 감소 */ export async function getMultilangText( key: string, companyCode: string = "*", menuCode: string = "MENU_MANAGEMENT", userLang: string = "KR", ): Promise { // 1. 캐시에서 먼저 확인 const cacheKey = `${userLang}_${companyCode}_${menuCode}`; if (translationCache[cacheKey]?.[key]) { return translationCache[cacheKey][key]; } // 2. 기본 텍스트에서 확인 const defaultText = getDefaultText(key); if (defaultText) { return defaultText; } // 3. 배치 처리에 추가 pendingKeys.add(key); // 4. 배치 타임아웃 설정 if (batchTimeout) { clearTimeout(batchTimeout); } return new Promise((resolve) => { batchTimeout = setTimeout(async () => { try { const keysToFetch = Array.from(pendingKeys); pendingKeys.clear(); if (keysToFetch.length > 0) { const translations = await fetchBatchTranslations(keysToFetch, companyCode, menuCode, userLang); // 캐시에 저장 if (!translationCache[cacheKey]) { translationCache[cacheKey] = {}; } Object.assign(translationCache[cacheKey], translations); // 요청된 키에 대한 번역 반환 if (translations[key]) { resolve(translations[key]); } else { resolve(defaultText || key); } } else { resolve(defaultText || key); } } catch (error) { console.error("❌ 배치 처리 오류:", error); resolve(defaultText || key); } }, BATCH_DELAY); }); } /** * 동기적 다국어 텍스트 조회 (캐시에서만) * UI 렌더링 시 즉시 사용 */ export function getMultilangTextSync( key: string, companyCode: string = "*", menuCode: string = "MENU_MANAGEMENT", userLang: string = "KR", ): string { // 1. 캐시에서 확인 const cacheKey = `${userLang}_${companyCode}_${menuCode}`; if (translationCache[cacheKey]?.[key]) { return translationCache[cacheKey][key]; } // 2. 기본 텍스트에서 확인 const defaultText = getDefaultText(key); if (defaultText) { return defaultText; } // 3. 캐시에 없으면 비동기적으로 로드 (백그라운드) if (typeof window !== "undefined") { getMultilangText(key, companyCode, menuCode, userLang).then((text) => { // 페이지 리렌더링을 위해 이벤트 발생 window.dispatchEvent( new CustomEvent("translation-loaded", { detail: { key, text, userLang }, }), ); }); } return defaultText || key; } /** * 메뉴 관리 관련 다국어 텍스트 조회 (배치 처리) */ export async function getMenuText(key: string, userLang: string = "KR"): Promise { return getMultilangText(key, "*", "MENU_MANAGEMENT", userLang); } /** * 메뉴 관리 관련 다국어 텍스트 동기 조회 */ export function getMenuTextSync(key: string, userLang: string = "KR"): string { return getMultilangTextSync(key, "*", "MENU_MANAGEMENT", userLang); } /** * 기본 텍스트 반환 (한국어) */ function getDefaultText(key: string): string { const defaultTexts: Record = { [MENU_MANAGEMENT_KEYS.TITLE]: "메뉴 관리", [MENU_MANAGEMENT_KEYS.DESCRIPTION]: "시스템의 메뉴 구조와 권한을 관리합니다.", [MENU_MANAGEMENT_KEYS.MENU_TYPE_TITLE]: "메뉴 타입", [MENU_MANAGEMENT_KEYS.MENU_TYPE_ADMIN]: "관리자", [MENU_MANAGEMENT_KEYS.MENU_TYPE_USER]: "사용자", [MENU_MANAGEMENT_KEYS.ADMIN_MENU]: "관리자 메뉴", [MENU_MANAGEMENT_KEYS.USER_MENU]: "사용자 메뉴", [MENU_MANAGEMENT_KEYS.ADMIN_DESCRIPTION]: "시스템 관리 및 설정 메뉴", [MENU_MANAGEMENT_KEYS.USER_DESCRIPTION]: "일반 사용자 업무 메뉴", [MENU_MANAGEMENT_KEYS.LIST_TITLE]: "메뉴 목록", [MENU_MANAGEMENT_KEYS.LIST_TOTAL]: "전체", [MENU_MANAGEMENT_KEYS.LIST_SEARCH_RESULT]: "검색 결과", [MENU_MANAGEMENT_KEYS.FILTER_COMPANY]: "회사 필터", [MENU_MANAGEMENT_KEYS.FILTER_COMPANY_ALL]: "전체", [MENU_MANAGEMENT_KEYS.FILTER_COMPANY_COMMON]: "공통", [MENU_MANAGEMENT_KEYS.FILTER_COMPANY_SEARCH]: "회사 검색...", [MENU_MANAGEMENT_KEYS.FILTER_SEARCH]: "검색", [MENU_MANAGEMENT_KEYS.FILTER_SEARCH_PLACEHOLDER]: "메뉴명 검색...", [MENU_MANAGEMENT_KEYS.FILTER_RESET]: "초기화", [MENU_MANAGEMENT_KEYS.BUTTON_ADD]: "추가", [MENU_MANAGEMENT_KEYS.BUTTON_ADD_TOP_LEVEL]: "최상위 메뉴 추가", [MENU_MANAGEMENT_KEYS.BUTTON_ADD_SUB]: "하위", [MENU_MANAGEMENT_KEYS.BUTTON_EDIT]: "수정", [MENU_MANAGEMENT_KEYS.BUTTON_DELETE]: "삭제", [MENU_MANAGEMENT_KEYS.BUTTON_DELETE_SELECTED]: "선택 삭제", [MENU_MANAGEMENT_KEYS.BUTTON_DELETE_SELECTED_COUNT]: "선택 삭제 ({count})", [MENU_MANAGEMENT_KEYS.BUTTON_DELETE_PROCESSING]: "삭제 중...", [MENU_MANAGEMENT_KEYS.BUTTON_CANCEL]: "취소", [MENU_MANAGEMENT_KEYS.BUTTON_SAVE]: "저장", [MENU_MANAGEMENT_KEYS.BUTTON_SAVE_PROCESSING]: "저장 중...", [MENU_MANAGEMENT_KEYS.BUTTON_REGISTER]: "등록", [MENU_MANAGEMENT_KEYS.BUTTON_MODIFY]: "수정", [MENU_MANAGEMENT_KEYS.FORM_MENU_TYPE]: "메뉴 타입", [MENU_MANAGEMENT_KEYS.FORM_MENU_TYPE_ADMIN]: "관리자", [MENU_MANAGEMENT_KEYS.FORM_MENU_TYPE_USER]: "사용자", [MENU_MANAGEMENT_KEYS.FORM_STATUS]: "상태", [MENU_MANAGEMENT_KEYS.FORM_STATUS_ACTIVE]: "활성화", [MENU_MANAGEMENT_KEYS.FORM_STATUS_INACTIVE]: "비활성화", [MENU_MANAGEMENT_KEYS.FORM_COMPANY]: "회사", [MENU_MANAGEMENT_KEYS.FORM_COMPANY_SELECT]: "회사를 선택하세요", [MENU_MANAGEMENT_KEYS.FORM_COMPANY_COMMON]: "공통", [MENU_MANAGEMENT_KEYS.FORM_COMPANY_SUBMENU_NOTE]: "하위 메뉴는 상위 메뉴와 동일한 회사를 가져야 합니다.", [MENU_MANAGEMENT_KEYS.FORM_MENU_NAME]: "메뉴명", [MENU_MANAGEMENT_KEYS.FORM_MENU_NAME_PLACEHOLDER]: "메뉴명을 입력하세요", [MENU_MANAGEMENT_KEYS.FORM_MENU_URL]: "URL", [MENU_MANAGEMENT_KEYS.FORM_MENU_URL_PLACEHOLDER]: "메뉴 URL을 입력하세요", [MENU_MANAGEMENT_KEYS.FORM_MENU_DESCRIPTION]: "설명", [MENU_MANAGEMENT_KEYS.FORM_MENU_DESCRIPTION_PLACEHOLDER]: "메뉴 설명을 입력하세요", [MENU_MANAGEMENT_KEYS.FORM_MENU_SEQUENCE]: "순서", [MENU_MANAGEMENT_KEYS.FORM_LANG_KEY]: "다국어 키", [MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_SELECT]: "다국어 키를 선택하세요", [MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_NONE]: "다국어 키 없음", [MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_SEARCH]: "다국어 키 검색...", [MENU_MANAGEMENT_KEYS.FORM_LANG_KEY_SELECTED]: "선택된 키: {key} - {description}", [MENU_MANAGEMENT_KEYS.MODAL_MENU_REGISTER_TITLE]: "메뉴 등록", [MENU_MANAGEMENT_KEYS.MODAL_MENU_MODIFY_TITLE]: "메뉴 수정", [MENU_MANAGEMENT_KEYS.MODAL_DELETE_TITLE]: "메뉴 삭제", [MENU_MANAGEMENT_KEYS.MODAL_DELETE_DESCRIPTION]: "해당 메뉴를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", [MENU_MANAGEMENT_KEYS.MODAL_DELETE_BATCH_DESCRIPTION]: "선택된 {count}개의 메뉴를 영구적으로 삭제하시겠습니까?\n\n⚠️ 주의: 상위 메뉴를 삭제하면 하위 메뉴들도 함께 삭제됩니다.\n이 작업은 되돌릴 수 없습니다.", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_SELECT]: "선택", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_MENU_NAME]: "메뉴명", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_MENU_URL]: "URL", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_MENU_TYPE]: "메뉴 타입", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_STATUS]: "상태", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_COMPANY]: "회사", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_SEQUENCE]: "순서", [MENU_MANAGEMENT_KEYS.TABLE_HEADER_ACTIONS]: "작업", [MENU_MANAGEMENT_KEYS.STATUS_ACTIVE]: "활성화", [MENU_MANAGEMENT_KEYS.STATUS_INACTIVE]: "비활성화", [MENU_MANAGEMENT_KEYS.STATUS_UNSPECIFIED]: "미지정", [MENU_MANAGEMENT_KEYS.MESSAGE_LOADING]: "로딩 중...", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_DELETE_PROCESSING]: "메뉴를 삭제하는 중...", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_SAVE_SUCCESS]: "메뉴가 성공적으로 저장되었습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_SAVE_FAILED]: "메뉴 저장에 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_DELETE_SUCCESS]: "메뉴가 성공적으로 삭제되었습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_DELETE_FAILED]: "메뉴 삭제에 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_DELETE_BATCH_SUCCESS]: "{count}개의 메뉴가 성공적으로 삭제되었습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_DELETE_BATCH_PARTIAL]: "{success}개 삭제됨, {failed}개 실패", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_STATUS_TOGGLE_SUCCESS]: "메뉴 상태가 변경되었습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_MENU_STATUS_TOGGLE_FAILED]: "메뉴 상태 변경에 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_VALIDATION_MENU_NAME_REQUIRED]: "메뉴명을 입력하세요.", [MENU_MANAGEMENT_KEYS.MESSAGE_VALIDATION_COMPANY_REQUIRED]: "회사를 선택하세요.", [MENU_MANAGEMENT_KEYS.MESSAGE_VALIDATION_SELECT_MENU_DELETE]: "삭제할 메뉴를 선택하세요.", [MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_MENU_LIST]: "메뉴 목록을 불러오는데 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_MENU_INFO]: "메뉴 정보를 불러오는데 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_COMPANY_LIST]: "회사 목록을 불러오는데 실패했습니다.", [MENU_MANAGEMENT_KEYS.MESSAGE_ERROR_LOAD_LANG_KEY_LIST]: "다국어 키 목록을 불러오는데 실패했습니다.", [MENU_MANAGEMENT_KEYS.UI_EXPAND]: "펼치기", [MENU_MANAGEMENT_KEYS.UI_COLLAPSE]: "접기", [MENU_MANAGEMENT_KEYS.UI_MENU_COLLAPSE]: "메뉴 접기", [MENU_MANAGEMENT_KEYS.UI_LANGUAGE]: "언어", // 추가 매핑: key 문자열 자체도 한글로 매핑 "menu.type.title": "메뉴 타입", "menu.management.admin": "관리자", "menu.management.admin.description": "시스템 관리 및 설정 메뉴", "menu.management.user": "사용자", "menu.management.user.description": "일반 사용자 업무 메뉴", "menu.list.title": "메뉴 목록", "filter.company.all": "전체", "filter.search.placeholder": "메뉴명 검색...", "filter.reset": "초기화", "button.add.top.level": "최상위 메뉴 추가", "button.delete.selected": "선택 삭제", "table.header.menu.name": "메뉴명", "table.header.sequence": "순서", "table.header.company": "회사", "table.header.menu.url": "URL", "table.header.status": "상태", "table.header.actions": "작업", }; return defaultTexts[key] || ""; } /** * 번역 캐시 설정 함수 */ export const setTranslationCache = (lang: string, translations: Record) => { translationCache[lang] = translations; }; /** * 번역 캐시 가져오기 함수 */ export const getTranslationCache = (lang: string): Record => { return translationCache[lang] || {}; }; /** * 메뉴 관리 다국어 텍스트 훅 (기존 코드와 호환) */ export const useMenuManagementText = () => { const getMenuText = async (key: string, params?: Record): Promise => { let text = await getMultilangText(key, "*", "MENU_MANAGEMENT", "KR"); // 파라미터 치환 if (params) { Object.entries(params).forEach(([paramKey, paramValue]) => { text = text.replace(new RegExp(`\\{${paramKey}\\}`, "g"), paramValue.toString()); }); } return text; }; return { getMenuText, keys: MENU_MANAGEMENT_KEYS, }; };