# 다국어 지원 컴포넌트 개발 가이드 새로운 화면 컴포넌트를 개발할 때 반드시 다국어 시스템을 고려해야 합니다. 이 가이드는 컴포넌트가 다국어 자동 생성 및 매핑 시스템과 호환되도록 하는 방법을 설명합니다. --- ## 1. 타입 정의 시 다국어 필드 추가 ### 기본 원칙 텍스트가 표시되는 **모든 속성**에 `langKeyId`와 `langKey` 필드를 함께 정의해야 합니다. ### 단일 텍스트 속성 ```typescript interface MyComponentConfig { // 기본 텍스트 title?: string; // 다국어 키 (필수 추가) titleLangKeyId?: number; titleLangKey?: string; // 라벨 label?: string; labelLangKeyId?: number; labelLangKey?: string; // 플레이스홀더 placeholder?: string; placeholderLangKeyId?: number; placeholderLangKey?: string; } ``` ### 배열/목록 속성 (컬럼, 탭 등) ```typescript interface ColumnConfig { name: string; label: string; // 다국어 키 (필수 추가) langKeyId?: number; langKey?: string; // 기타 속성 width?: number; align?: "left" | "center" | "right"; } interface TabConfig { id: string; label: string; // 다국어 키 (필수 추가) langKeyId?: number; langKey?: string; // 탭 제목도 별도로 title?: string; titleLangKeyId?: number; titleLangKey?: string; } interface MyComponentConfig { columns?: ColumnConfig[]; tabs?: TabConfig[]; } ``` ### 버튼 컴포넌트 ```typescript interface ButtonComponentConfig { text?: string; // 다국어 키 (필수 추가) langKeyId?: number; langKey?: string; } ``` ### 실제 예시: 분할 패널 ```typescript interface SplitPanelLayoutConfig { leftPanel?: { title?: string; langKeyId?: number; // 좌측 패널 제목 다국어 langKey?: string; columns?: Array<{ name: string; label: string; langKeyId?: number; // 각 컬럼 다국어 langKey?: string; }>; }; rightPanel?: { title?: string; langKeyId?: number; // 우측 패널 제목 다국어 langKey?: string; columns?: Array<{ name: string; label: string; langKeyId?: number; langKey?: string; }>; additionalTabs?: Array<{ label: string; langKeyId?: number; // 탭 라벨 다국어 langKey?: string; title?: string; titleLangKeyId?: number; // 탭 제목 다국어 titleLangKey?: string; columns?: Array<{ name: string; label: string; langKeyId?: number; langKey?: string; }>; }>; }; } ``` --- ## 2. 라벨 추출 로직 등록 ### 파일 위치 `frontend/lib/utils/multilangLabelExtractor.ts` ### `extractMultilangLabels` 함수에 추가 새 컴포넌트의 라벨을 추출하는 로직을 추가해야 합니다. ```typescript // 새 컴포넌트 타입 체크 if (comp.componentType === "my-new-component") { const config = comp.componentConfig as MyComponentConfig; // 1. 제목 추출 if (config?.title) { addLabel({ id: `${comp.id}_title`, componentId: `${comp.id}_title`, label: config.title, type: "title", parentType: "my-new-component", parentLabel: config.title, langKeyId: config.titleLangKeyId, langKey: config.titleLangKey, }); } // 2. 컬럼 추출 if (config?.columns && Array.isArray(config.columns)) { config.columns.forEach((col, index) => { const colLabel = col.label || col.name; addLabel({ id: `${comp.id}_col_${index}`, componentId: `${comp.id}_col_${index}`, label: colLabel, type: "column", parentType: "my-new-component", parentLabel: config.title || "새 컴포넌트", langKeyId: col.langKeyId, langKey: col.langKey, }); }); } // 3. 버튼 텍스트 추출 (버튼 컴포넌트인 경우) if (config?.text) { addLabel({ id: `${comp.id}_button`, componentId: `${comp.id}_button`, label: config.text, type: "button", parentType: "my-new-component", parentLabel: config.text, langKeyId: config.langKeyId, langKey: config.langKey, }); } } ``` ### 추출해야 할 라벨 타입 | 타입 | 설명 | 예시 | | ------------- | ------------------ | ------------------------ | | `title` | 컴포넌트/패널 제목 | 분할패널 제목, 카드 제목 | | `label` | 입력 필드 라벨 | 텍스트 입력 라벨 | | `button` | 버튼 텍스트 | 저장, 취소, 삭제 | | `column` | 테이블 컬럼 헤더 | 품목명, 수량, 금액 | | `tab` | 탭 라벨 | 기본정보, 상세정보 | | `filter` | 검색 필터 라벨 | 검색어, 기간 | | `placeholder` | 플레이스홀더 | "검색어를 입력하세요" | | `action` | 액션 버튼/링크 | 수정, 삭제, 상세보기 | --- ## 3. 매핑 적용 로직 등록 ### 파일 위치 `frontend/lib/utils/multilangLabelExtractor.ts` ### `applyMultilangMappings` 함수에 추가 다국어 키가 선택되면 컴포넌트에 `langKeyId`와 `langKey`를 저장하는 로직을 추가합니다. ```typescript // 새 컴포넌트 매핑 적용 if (comp.componentType === "my-new-component") { const config = comp.componentConfig as MyComponentConfig; // 1. 제목 매핑 const titleMapping = mappingMap.get(`${comp.id}_title`); if (titleMapping) { updated.componentConfig = { ...updated.componentConfig, titleLangKeyId: titleMapping.keyId, titleLangKey: titleMapping.langKey, }; } // 2. 컬럼 매핑 if (config?.columns && Array.isArray(config.columns)) { const updatedColumns = config.columns.map((col, index) => { const colMapping = mappingMap.get(`${comp.id}_col_${index}`); if (colMapping) { return { ...col, langKeyId: colMapping.keyId, langKey: colMapping.langKey, }; } return col; }); updated.componentConfig = { ...updated.componentConfig, columns: updatedColumns, }; } // 3. 버튼 매핑 (버튼 컴포넌트인 경우) const buttonMapping = mappingMap.get(`${comp.id}_button`); if (buttonMapping) { updated.componentConfig = { ...updated.componentConfig, langKeyId: buttonMapping.keyId, langKey: buttonMapping.langKey, }; } } ``` ### 주의사항 - **객체 참조 유지**: 매핑 시 기존 `updated.componentConfig`를 기반으로 업데이트해야 합니다. - **중첩 구조**: 중첩된 객체(예: `leftPanel.columns`)는 상위 객체부터 순서대로 업데이트합니다. ```typescript // 잘못된 방법 - 이전 업데이트 덮어쓰기 updated.componentConfig = { ...config, langKeyId: mapping.keyId }; // ❌ updated.componentConfig = { ...config, columns: updatedColumns }; // langKeyId 사라짐! // 올바른 방법 - 이전 업데이트 유지 updated.componentConfig = { ...updated.componentConfig, langKeyId: mapping.keyId, }; // ✅ updated.componentConfig = { ...updated.componentConfig, columns: updatedColumns, }; // ✅ ``` --- ## 4. 번역 표시 로직 구현 ### 파일 위치 새 컴포넌트 파일 (예: `frontend/lib/registry/components/my-component/MyComponent.tsx`) ### Context 사용 ```typescript import { useScreenMultiLang } from "@/contexts/ScreenMultiLangContext"; const MyComponent = ({ component }: Props) => { const { getTranslatedText } = useScreenMultiLang(); const config = component.componentConfig; // 제목 번역 const displayTitle = config?.titleLangKey ? getTranslatedText(config.titleLangKey, config.title || "") : config?.title || ""; // 컬럼 헤더 번역 const translatedColumns = config?.columns?.map((col) => ({ ...col, displayLabel: col.langKey ? getTranslatedText(col.langKey, col.label) : col.label, })); // 버튼 텍스트 번역 const buttonText = config?.langKey ? getTranslatedText(config.langKey, config.text || "") : config?.text || ""; return (
| {col.displayLabel} | ))}
|---|