Files
vexplor/docs/kjs/SAVED_DATA_NESTED_STRUCTURE_BUG_FIX.md
DDD1542 f3a0c92564 feat: EditModal 및 ButtonActionExecutor에서 데이터 흐름 제어 로직 개선
- EditModal 컴포넌트에서 executionTiming 체크 로직을 추가하여 데이터 흐름 제어를 보다 유연하게 처리하도록 개선하였습니다.
- ButtonActionExecutor에서 저장된 데이터 구조를 명확히 하여, API 응답에서 실제 폼 데이터를 올바르게 추출하도록 수정하였습니다.
- 디버깅 로그를 추가하여 데이터 흐름 및 상태를 추적할 수 있도록 하여 개발 편의성을 높였습니다.
2026-02-05 10:08:26 +09:00

5.0 KiB

저장 후 플로우 실행 시 폼 데이터 전달 오류 수정

오류 현상

사용자가 폼에서 데이터를 저장한 후, 연결된 노드 플로우(예: 비밀번호 자동 설정)가 실행될 때 sabun 값이 undefined로 전달되어 UPDATE 쿼리의 WHERE 조건이 작동하지 않는 문제.

증상

  • 저장 버튼 클릭 시 INSERT는 정상 작동
  • 저장 후 실행되는 노드 플로우에서 user_password UPDATE가 실패 (0건 업데이트)
  • 콘솔 로그에서 savedData.sabun: undefined 출력
📦 [executeAfterSaveControl] savedData 필드: ['id', 'screenId', 'tableName', 'data', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']
📦 [executeAfterSaveControl] savedData.sabun: undefined

원인 분석

API 응답 구조의 3단계 중첩

저장 API(DynamicFormApi.saveFormData)의 응답이 3단계로 중첩되어 있었음:

// 1단계: Axios 응답
saveResult = {
  data: { ... }  // API 응답
}

// 2단계: API 응답 래핑 (ApiResponse 인터페이스)
saveResult.data = {
  success: true,
  data: { ... },  // 저장된 레코드
  message: "저장 완료"
}

// 3단계: 저장된 레코드 (dynamic_form_data 테이블 구조)
saveResult.data.data = {
  id: 123,
  screenId: 106,
  tableName: "user_info",
  data: { sabun: "20260205-087", user_name: "TEST", ... },  // ← 실제 폼 데이터
  createdAt: "2026-02-05T...",
  updatedAt: "2026-02-05T...",
  createdBy: "admin",
  updatedBy: "admin"
}

// 4단계: 실제 폼 데이터 (우리가 필요한 데이터)
saveResult.data.data.data = {
  sabun: "20260205-087",
  user_name: "TEST",
  user_id: "Kim1542",
  ...
}

기존 코드의 문제점

// 기존 코드 (buttonActions.ts:1619-1621)
const savedData = saveResult?.data?.data || saveResult?.data || {};
const formData = savedData;  // ← 2단계까지만 추출

// savedData = { id, screenId, tableName, data: {...}, createdAt, ... }
// savedData.sabun = undefined  ← 문제 발생!

기존 코드는 2단계(saveResult.data.data)까지만 추출했기 때문에, savedData가 저장된 레코드 메타데이터를 가리키고 있었음. 실제 폼 데이터는 savedData.data 안에 있었음.


해결 방법

수정된 코드

// 수정된 코드 (buttonActions.ts:1619-1628)
// 🔧 수정: saveResult.data가 3단계로 중첩된 경우 실제 폼 데이터 추출
// saveResult.data = API 응답 { success, data, message }
// saveResult.data.data = 저장된 레코드 { id, screenId, tableName, data, createdAt... }
// saveResult.data.data.data = 실제 폼 데이터 { sabun, user_name... }
const savedRecord = saveResult?.data?.data || saveResult?.data || {};
const actualFormData = savedRecord?.data || savedRecord;  // ← 3단계까지 추출
const formData = (Object.keys(actualFormData).length > 0 ? actualFormData : context.formData || {});

수정 핵심

  1. savedRecord: 저장된 레코드 메타데이터 ({ id, screenId, tableName, data, ... })
  2. actualFormData: savedRecord.data가 있으면 그것을 사용, 없으면 savedRecord 자체 사용
  3. 폴백: actualFormData가 비어있으면 context.formData 사용

수정된 파일

파일 수정 내용
frontend/lib/utils/buttonActions.ts 3단계 중첩 데이터 구조에서 실제 폼 데이터 추출 로직 수정 (라인 1619-1628)

검증 결과

수정 전

📦 [executeAfterSaveControl] savedData 필드: ['id', 'screenId', 'tableName', 'data', ...]
📦 [executeAfterSaveControl] savedData.sabun: undefined

수정 후

📦 [executeAfterSaveControl] savedRecord 구조: ['id', 'screenId', 'tableName', 'data', ...]
📦 [executeAfterSaveControl] actualFormData 추출: ['sabun', 'user_id', 'user_password', ...]
📦 [executeAfterSaveControl] formData.sabun: 20260205-087

DB 확인

SELECT sabun, user_name, user_password FROM user_info WHERE sabun = '20260205-087';
-- 결과: sabun: "20260205-087", user_name: "TEST", user_password: "1e538e2abdd9663437343212a4853591"

교훈

  1. API 응답 구조 확인: API 응답이 여러 단계로 래핑될 수 있음. 프론트엔드에서 apiClient가 한 번, ApiResponse 인터페이스가 한 번, 그리고 실제 데이터 구조가 또 다른 레벨을 가질 수 있음.

  2. 로그 추가의 중요성: 중간 단계마다 로그를 찍어 데이터 구조를 확인하는 것이 디버깅에 필수적.

  3. 폴백 처리: 데이터 추출 시 여러 단계의 폴백을 두어 다양한 응답 구조에 대응.


관련 이슈

  • 비밀번호 자동 설정 노드 플로우가 저장 후 실행되지 않는 문제
  • 저장 후 연결된 UPDATE 플로우에서 WHERE 조건이 작동하지 않는 문제

작성 정보

  • 작성일: 2026-02-05
  • 작성자: AI Assistant
  • 관련 화면: 부서관리 > 사용자 등록 모달
  • 관련 플로우: flowId: 120 (부서관리 비밀번호 자동세팅)