Files
vexplor/PLAN.MD
SeongHyun Kim 7a97603106 feat(pop-card-list): 3섹션 분리 + 포장 2단계 계산기 + 설정 패널 개편
- 입력 필드/포장등록/담기 버튼 독립 ON/OFF 분리
- NumberInputModal을 4단계 상태 머신으로 재작성
  (수량 -> 포장 수 -> 개당 수량 -> summary)
- 포장 단위 커스텀 지원 (기본 6종 + 디자이너 추가)
- 본문 필드에 계산식 통합 (3-드롭다운 수식 빌더)
- 입력 필드: limitColumn(동적 상한), saveTable/saveColumn(저장 대상)
- 저장 대상 테이블 선택을 TableCombobox로 교체 (검색 가능)
- 다중 정렬 지원 + 하위 호환 (sorts.map 에러 수정)
- GroupedColumnSelect 항상 테이블명 헤더 표시
- 반응형 표시 우선순위 (required/shrink/hidden) 설정
- PackageEntry/CartItem 타입 확장, CardPackageConfig 신규

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-25 17:03:47 +09:00

13 KiB

현재 구현 계획: pop-card-list 입력 필드/계산 필드 구조 개편

작성일: 2026-02-24 상태: 계획 완료, 코딩 대기 목적: 입력 필드 설정 단순화 + 본문 필드에 계산식 통합 + 기존 계산 필드 섹션 제거


1. 변경 개요

배경

  • 기존: "입력 필드", "계산 필드", "담기 버튼" 3개가 별도 섹션으로 분리
  • 문제: 계산 필드가 본문 필드와 동일한 위치에 표시되어야 하는데 별도 영역에 있음
  • 문제: 입력 필드의 min/max 고정값은 비실용적 (실제로는 DB 컬럼 기준 제한이 필요)
  • 문제: step, columnName, sourceColumns, resultColumn 등 죽은 코드 존재

목표

  1. 본문 필드에 계산식 지원 추가 - 필드별로 "DB 컬럼" 또는 "계산식" 선택
  2. 입력 필드 설정 단순화 - 고정 min/max 제거, 제한 기준 컬럼 방식으로 변경
  3. 기존 "계산 필드" 섹션 제거 - 본문 필드에 통합되므로 불필요
  4. 죽은 코드 정리

2. 수정 대상 파일 (3개)

파일 A: frontend/lib/registry/pop-components/types.ts

변경 A-1: CardFieldBinding 타입 확장

현재 코드 (라인 367~372):

export interface CardFieldBinding {
  id: string;
  columnName: string;
  label: string;
  textColor?: string;
}

변경 코드:

export interface CardFieldBinding {
  id: string;
  label: string;
  textColor?: string;
  valueType: "column" | "formula";  // 값 유형: DB 컬럼 또는 계산식
  columnName?: string;              // valueType === "column"일 때 사용
  formula?: string;                 // valueType === "formula"일 때 사용 (예: "$input - received_qty")
  unit?: string;                    // 계산식일 때 단위 표시 (예: "EA")
}

주의: columnName이 required에서 optional로 변경됨. 기존 저장 데이터와의 하위 호환 필요.

변경 A-2: CardInputFieldConfig 단순화

현재 코드 (라인 443~453):

export interface CardInputFieldConfig {
  enabled: boolean;
  columnName?: string;
  label?: string;
  unit?: string;
  defaultValue?: number;
  min?: number;
  max?: number;
  maxColumn?: string;
  step?: number;
}

변경 코드:

export interface CardInputFieldConfig {
  enabled: boolean;
  label?: string;
  unit?: string;
  limitColumn?: string;           // 제한 기준 컬럼 (해당 행의 이 컬럼 값이 최대값)
  saveTable?: string;             // 저장 대상 테이블
  saveColumn?: string;            // 저장 대상 컬럼
  showPackageUnit?: boolean;      // 포장등록 버튼 표시 여부
}

제거 항목:

  • columnName -> saveTable + saveColumn으로 대체 (명확한 네이밍)
  • defaultValue -> 제거 (제한 기준 컬럼 값으로 대체)
  • min -> 제거 (항상 0)
  • max -> 제거 (limitColumn으로 대체)
  • maxColumn -> limitColumn으로 이름 변경
  • step -> 제거 (키패드 방식에서 미사용)

변경 A-3: CardCalculatedFieldConfig 제거

삭제: CardCalculatedFieldConfig 인터페이스 전체 (라인 457~464) 삭제: PopCardListConfig에서 calculatedField?: CardCalculatedFieldConfig; 제거


파일 B: frontend/lib/registry/pop-components/pop-card-list/PopCardListConfig.tsx

변경 B-1: 본문 필드 편집기(FieldEditor)에 값 유형 선택 추가

현재: 필드 편집 시 라벨, 컬럼, 텍스트색상만 설정 가능

변경: 값 유형 라디오("DB 컬럼" / "계산식") 추가

  • "DB 컬럼" 선택 시: 기존 컬럼 Select 표시
  • "계산식" 선택 시: 수식 입력란 + 사용 가능한 컬럼/변수 칩 목록 표시
  • 사용 가능한 변수: DB 컬럼명들 + $input (입력 필드 활성화 시)

하위 호환: 기존 저장 데이터에 valueType이 없으면 "column"으로 기본 처리

변경 B-2: 입력 필드 설정 섹션 개편

현재 설정 항목: 라벨, 단위, 기본값, 최소/최대, 최대값 컬럼, 저장 컬럼

변경 설정 항목:

라벨           [입고 수량       ]
단위           [EA             ]
제한 기준 컬럼  [ order_qty     v ]
저장 대상 테이블 [ 선택          v ]
저장 대상 컬럼   [ 선택          v ]
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
포장등록 버튼                [on/off]

변경 B-3: "계산 필드" 섹션 제거

삭제: CalculatedFieldSettingsSection 함수 전체 삭제: 카드 템플릿 탭에서 "계산 필드" CollapsibleSection 제거

변경 B-4: import 정리

삭제: CardCalculatedFieldConfig import 추가: 없음 (기존 import 재사용)


파일 C: frontend/lib/registry/pop-components/pop-card-list/PopCardListComponent.tsx

변경 C-1: FieldRow에서 계산식 필드 지원

현재: const value = row[field.columnName] 로 DB 값만 표시

변경:

function FieldRow({ field, row, scaled, inputValue }: {
  field: CardFieldBinding;
  row: RowData;
  scaled: ScaledConfig;
  inputValue?: number;  // 입력 필드 값 (계산식에서 $input으로 참조)
}) {
  const value = field.valueType === "formula" && field.formula
    ? evaluateFormula(field.formula, row, inputValue ?? 0)
    : row[field.columnName ?? ""];
  // ...
}

주의: inputValue를 FieldRow까지 전달해야 하므로 CardItem -> FieldRow 경로에 prop 추가 필요

변경 C-2: 계산식 필드 실시간 갱신

현재: 별도 calculatedValue useMemo가 [calculatedField, row, inputValue]에 반응

변경: FieldRow가 inputValue prop을 받으므로, inputValue가 변경될 때 계산식 필드가 자동으로 리렌더링됨. 별도 useMemo 불필요.

변경 C-3: 기존 calculatedField 관련 코드 제거

삭제 대상:

  • calculatedField prop 전달 (CardItem)
  • calculatedValue useMemo
  • 계산 필드 렌더링 블록 ({calculatedField?.enabled && calculatedValue !== null && (...)}

변경 C-4: 입력 필드 로직 단순화

변경 대상:

  • effectiveMax: limitColumn 사용, 미설정 시 999999 폴백
  • defaultValue 자동 초기화 로직 제거 (불필요)
  • NumberInputModal에 포장등록 on/off 전달

변경 C-5: NumberInputModal에 포장등록 on/off 전달

현재: 포장등록 버튼 항상 표시 변경: showPackageUnit prop 추가, false이면 포장등록 버튼 숨김


파일 D: frontend/lib/registry/pop-components/pop-card-list/NumberInputModal.tsx

변경 D-1: showPackageUnit prop 추가

현재 props: open, onOpenChange, unit, initialValue, initialPackageUnit, min, maxValue, onConfirm

추가 prop: showPackageUnit?: boolean (기본값 true)

변경: showPackageUnit === false이면 포장등록 버튼 숨김


3. 구현 순서 (의존성 기반)

순서 작업 파일 의존성 상태
1 A-1: CardFieldBinding 타입 확장 types.ts 없음 [ ]
2 A-2: CardInputFieldConfig 단순화 types.ts 없음 [ ]
3 A-3: CardCalculatedFieldConfig 제거 types.ts 없음 [ ]
4 B-1: FieldEditor에 값 유형 선택 추가 PopCardListConfig.tsx 순서 1 [ ]
5 B-2: 입력 필드 설정 섹션 개편 PopCardListConfig.tsx 순서 2 [ ]
6 B-3: 계산 필드 섹션 제거 PopCardListConfig.tsx 순서 3 [ ]
7 B-4: import 정리 PopCardListConfig.tsx 순서 6 [ ]
8 D-1: NumberInputModal showPackageUnit 추가 NumberInputModal.tsx 없음 [ ]
9 C-1: FieldRow 계산식 지원 PopCardListComponent.tsx 순서 1 [ ]
10 C-3: calculatedField 관련 코드 제거 PopCardListComponent.tsx 순서 9 [ ]
11 C-4: 입력 필드 로직 단순화 PopCardListComponent.tsx 순서 2, 8 [ ]
12 린트 검사 전체 순서 1~11 [ ]

순서 1, 2, 3은 독립이므로 병렬 가능. 순서 8은 독립이므로 병렬 가능.


4. 사전 충돌 검사 결과

새로 추가할 식별자 목록

식별자 타입 정의 파일 사용 파일 충돌 여부
valueType CardFieldBinding 속성 types.ts PopCardListConfig.tsx, PopCardListComponent.tsx 충돌 없음
formula CardFieldBinding 속성 types.ts PopCardListConfig.tsx, PopCardListComponent.tsx 충돌 없음 (기존 CardCalculatedFieldConfig.formula와 다른 인터페이스)
limitColumn CardInputFieldConfig 속성 types.ts PopCardListConfig.tsx, PopCardListComponent.tsx 충돌 없음
saveTable CardInputFieldConfig 속성 types.ts PopCardListConfig.tsx 충돌 없음
saveColumn CardInputFieldConfig 속성 types.ts PopCardListConfig.tsx 충돌 없음
showPackageUnit CardInputFieldConfig 속성 / NumberInputModal prop types.ts, NumberInputModal.tsx PopCardListComponent.tsx 충돌 없음

기존 타입/함수 재사용 목록

기존 식별자 정의 위치 이번 수정에서 사용하는 곳
evaluateFormula() PopCardListComponent.tsx 라인 1026 C-1: FieldRow에서 호출 (기존 함수 그대로 재사용)
CardFieldBinding types.ts 라인 367 A-1에서 수정, B-1/C-1에서 사용
CardInputFieldConfig types.ts 라인 443 A-2에서 수정, B-2/C-4에서 사용
GroupedColumnSelect PopCardListConfig.tsx B-1: 계산식 모드에서 컬럼 칩 표시에 재사용 가능

사용처 있는데 정의 누락된 항목: 없음


5. 에러 함정 경고

함정 1: 기존 저장 데이터 하위 호환

기존에 저장된 CardFieldBinding에는 valueType이 없고 columnName이 필수였음. 반드시 런타임에서 field.valueType || "column" 폴백 처리해야 함. Config UI에서도 valueType 미존재 시 "column" 기본값 적용 필요.

함정 2: CardInputFieldConfig 하위 호환

기존 maxColumnlimitColumn으로 이름 변경됨. 기존 저장 데이터의 maxColumnlimitColumn으로 읽어야 함. 런타임: inputField?.limitColumn || (inputField as any)?.maxColumn 폴백 필요.

함정 3: evaluateFormula의 inputValue 전달

FieldRow에 inputValue를 전달하려면, CardItem -> body.fields.map -> FieldRow 경로에서 inputValue prop을 추가해야 함. 입력 필드가 비활성화된 경우 inputValue는 0으로 전달.

함정 4: calculatedField 제거 시 기존 데이터

기존 config에 calculatedField 데이터가 남아 있을 수 있음. 타입에서 제거하더라도 런타임 에러는 나지 않음 (unknown 속성은 무시됨). 다만 이전에 계산 필드로 설정한 내용은 사라짐 - 마이그레이션 없이 제거.

함정 5: columnName optional 변경

CardFieldBinding.columnName이 optional이 됨. 기존에 row[field.columnName]으로 직접 접근하던 코드 전부 수정 필요. field.columnName ?? "" 또는 valueType 분기 처리.


6. 검증 방법

시나리오 1: 기존 본문 필드 (하위 호환)

  1. 기존 저장된 카드리스트 열기
  2. 본문 필드에 기존 DB 컬럼 필드가 정상 표시되는지 확인
  3. 설정 패널에서 기존 필드가 "DB 컬럼" 유형으로 표시되는지 확인

시나리오 2: 계산식 본문 필드 추가

  1. 본문 필드 추가 -> 값 유형 "계산식" 선택
  2. 수식: order_qty - received_qty 입력
  3. 카드에서 계산 결과가 정상 표시되는지 확인

시나리오 3: $input 참조 계산식

  1. 입력 필드 활성화
  2. 본문 필드 추가 -> 값 유형 "계산식" -> 수식: $input - received_qty
  3. 키패드에서 수량 입력 시 계산 결과가 실시간 갱신되는지 확인

시나리오 4: 제한 기준 컬럼

  1. 입력 필드 -> 제한 기준 컬럼: order_qty
  2. order_qty=1000인 카드에서 키패드 열기
  3. MAX 버튼 클릭 시 1000이 입력되고, 1001 이상 입력 불가 확인

시나리오 5: 포장등록 on/off

  1. 입력 필드 -> 포장등록 버튼: off
  2. 키패드 모달에서 포장등록 버튼이 숨겨지는지 확인

이전 완료 계획 (아카이브)

pop-dashboard 4가지 아이템 모드 완성 (완료)
  • groupBy UI 추가
  • xAxisColumn 입력 UI 추가
  • 통계카드 카테고리 설정 UI 추가
  • 차트 xAxisColumn 자동 보정 로직
  • 통계카드 카테고리별 필터 적용
  • SQL 빌더 방어 로직
  • refreshInterval 최소값 강제
POP 뷰어 스크롤 수정 (완료)
  • overflow-hidden 제거
  • overflow-auto 공통 적용
  • 일반 모드 min-h-full 추가
POP 뷰어 실제 컴포넌트 렌더링 (완료)
  • 뷰어 페이지에 레지스트리 초기화 import 추가
  • renderActualComponent() 실제 컴포넌트 렌더링으로 교체