diff --git a/frontend/lib/registry/pop-components/pop-field/PopFieldComponent.tsx b/frontend/lib/registry/pop-components/pop-field/PopFieldComponent.tsx index 0438df90..9a65e0fb 100644 --- a/frontend/lib/registry/pop-components/pop-field/PopFieldComponent.tsx +++ b/frontend/lib/registry/pop-components/pop-field/PopFieldComponent.tsx @@ -95,6 +95,7 @@ export function PopFieldComponent({ const row = res.data[0] as Record; const extracted: Record = {}; + const lookupQueue: { fieldId: string; codeValue: string; table: string; codeCol: string; displayCol: string }[] = []; for (const mapping of readSource.fieldMappings || []) { if (mapping.valueSource === "json_extract" && mapping.columnName && mapping.jsonKey) { const raw = row[mapping.columnName]; @@ -105,11 +106,48 @@ export function PopFieldComponent({ parsed = raw as Record; } extracted[mapping.fieldId] = parsed[mapping.jsonKey] ?? ""; + } else if (mapping.valueSource === "json_extract_lookup" && mapping.columnName && mapping.jsonKey && mapping.lookupTable && mapping.lookupCodeColumn && mapping.lookupDisplayColumn) { + const raw = row[mapping.columnName]; + let parsed: Record = {}; + if (typeof raw === "string") { + try { parsed = JSON.parse(raw); } catch { /* ignore */ } + } else if (typeof raw === "object" && raw !== null) { + parsed = raw as Record; + } + const codeValue = String(parsed[mapping.jsonKey] ?? ""); + if (codeValue) { + lookupQueue.push({ fieldId: mapping.fieldId, codeValue, table: mapping.lookupTable, codeCol: mapping.lookupCodeColumn, displayCol: mapping.lookupDisplayColumn }); + } else { + extracted[mapping.fieldId] = ""; + } } else if (mapping.valueSource === "db_column" && mapping.columnName) { extracted[mapping.fieldId] = row[mapping.columnName] ?? ""; } } + // json_extract_lookup: 코드 값으로 참조 테이블 조회하여 표시명 획득 + if (lookupQueue.length > 0) { + const lookupResults = await Promise.allSettled( + lookupQueue.map(async (lq) => { + const lookupRes = await dataApi.getTableData(lq.table, { + page: 1, + size: 1, + filters: { [lq.codeCol]: lq.codeValue }, + }); + if (Array.isArray(lookupRes.data) && lookupRes.data.length > 0) { + const lookupRow = lookupRes.data[0] as Record; + return { fieldId: lq.fieldId, value: lookupRow[lq.displayCol] ?? lq.codeValue }; + } + return { fieldId: lq.fieldId, value: lq.codeValue }; + }), + ); + for (const result of lookupResults) { + if (result.status === "fulfilled") { + extracted[result.value.fieldId] = result.value.value; + } + } + } + const allFieldsInConfig = cfg.sections.flatMap((s) => s.fields || []); const valuesUpdate: Record = {}; for (const [fieldId, val] of Object.entries(extracted)) { diff --git a/frontend/lib/registry/pop-components/pop-field/types.ts b/frontend/lib/registry/pop-components/pop-field/types.ts index f0813e6c..a2ddae0c 100644 --- a/frontend/lib/registry/pop-components/pop-field/types.ts +++ b/frontend/lib/registry/pop-components/pop-field/types.ts @@ -110,12 +110,13 @@ export interface PopFieldSection { // ===== 저장 설정: 값 소스 타입 ===== -export type FieldValueSource = "direct" | "json_extract" | "db_column"; +export type FieldValueSource = "direct" | "json_extract" | "db_column" | "json_extract_lookup"; export const FIELD_VALUE_SOURCE_LABELS: Record = { direct: "직접 입력", json_extract: "JSON 추출", db_column: "DB 컬럼", + json_extract_lookup: "JSON 추출 + 조회", }; // ===== 저장 설정: 필드-컬럼 매핑 ===== @@ -170,6 +171,12 @@ export interface PopFieldReadMapping { valueSource: FieldValueSource; columnName: string; jsonKey?: string; + /** json_extract_lookup 전용: 조회할 테이블명 */ + lookupTable?: string; + /** json_extract_lookup 전용: JSON에서 추출한 코드 값과 매칭할 컬럼 */ + lookupCodeColumn?: string; + /** json_extract_lookup 전용: 화면에 표시할 컬럼 */ + lookupDisplayColumn?: string; } export interface PopFieldReadSource {