ERP-node/docs/ycshin-node/MPN[맥락]-품번-수동접두어채번.md

10 KiB

[맥락노트] 품번 수동 접두어 채번 - 접두어별 독립 순번 생성

관련 문서: 계획서 | 체크리스트


왜 이 작업을 하는가

  • 기준정보 - 품목정보 등록 모달에서 품번 인풋에 사용자가 값을 입력해도 무시되고 "BULK1"로 저장됨
  • 서로 다른 접두어("ㅁㅁㅁ", "ㅇㅇㅇ")를 입력해도 전부 같은 시퀀스 카운터를 공유함
  • 카테고리 미선택 시 --제발-015-003 처럼 연속 구분자가 발생함
  • 사용자 입력이 반영되고, 접두어별로 독립된 순번이 부여되어야 함

핵심 결정 사항과 근거

1. 수동 값 추출을 buildPrefixKey 전으로 이동

  • 결정: allocateCode 내부에서 수동 값 추출 → buildPrefixKey 순서로 변경
  • 근거: 기존에는 buildPrefixKey(L1306)가 먼저 실행된 후 수동 값 추출(L1332)이 진행됨. 수동 값이 prefix_key에 포함되려면 추출이 먼저 되어야 함
  • 대안 검토: buildPrefixKey 내부에서 직접 추출 → 기각 (역할 분리 위반, previewCode 호출에도 영향)

2. buildPrefixKey에 수동 파트 값 포함

  • 결정: manualValues optional 파라미터 추가, 전달되면 prefix_key에 포함
  • 근거: 기존 continue(L85-87)로 수동 파트가 prefix_key에서 제외되어 모든 접두어가 같은 시퀀스를 공유함
  • 하위호환: optional 파라미터이므로 previewCode(L1091) 등 기존 호출부는 영향 없음

3. 템플릿 파싱 실패 시 userInputCode 전체를 수동 값으로 사용

  • 결정: 수동 파트가 1개이고 템플릿 기반 추출이 실패하면 userInputCode 전체를 수동 값으로 사용
  • 근거: 사용자가 "ㅁㅁㅁ"처럼 접두어 부분만 입력하면 템플릿 "카테고리값-____-XXX"와 불일치. startsWith 조건 실패로 추출이 안 됨. 이 경우 입력 전체가 수동 값임
  • 제한: 수동 파트가 2개 이상이면 이 폴백 불가 (어디서 분리할지 알 수 없음)

4. 코드 조합에서 manualConfig.value 폴백 제거

  • 결정: extractedManualValues[i] || part.manualConfig?.value || ""extractedManualValues[i] || ""
  • 근거: manualConfig.value는 UI에서 입력/편집할 수 없는 유령 필드. ManualConfigPanel.tsxvalue 입력란이 없어 DB에 한번 저장되면 스프레드 연산자로 계속 보존됨
  • 이중 조치: 코드에서 폴백 제거 + DB 마이그레이션으로 기존 "BULK1" 값 정리

5. DB 마이그레이션은 BULK1만 타겟팅

  • 결정: manual_config->>'value' = 'BULK1' 조건으로 한정
  • 근거: 다른 value가 의도적으로 설정된 경우가 있을 수 있음. 확인된 문제("BULK1")만 정리하여 부작용 방지
  • 대안 검토: 전체 manual_config.value 키 제거 → 보류 (운영 판단 필요)

6. extractManualValuesFromInput 헬퍼 분리

  • 결정: 기존 allocateCode 내부의 수동 값 추출 로직(L1332-1442)을 별도 private 메서드로 추출
  • 근거: 추출 로직이 약 110줄로 allocateCode가 과도하게 비대함. 헬퍼로 분리하면 순서 변경도 자연스러움
  • 원칙: 로직 자체는 변경 없음, 위치만 이동 (구조적 변경과 행위적 변경 분리)

7. 프론트엔드 변경 불필요

  • 결정: 프론트엔드 코드 수정 없음
  • 근거: _numberingRuleId가 사용자 입력 시에도 유지되고 있음 확인. buttonActions.ts가 정상적으로 allocateCode를 호출함. 문제는 백엔드 로직에만 있음

8. joinPartsWithSeparators 연속 구분자 방지

  • 결정: 빈 파트 뒤에 이미 같은 구분자가 있으면 중복 추가하지 않음
  • 근거: 카테고리가 비면 파트 값 "" + 구분자 -가 반복되어 -- 발생. 구분자 구조(-ㅁㅁㅁ-001)는 유지하되 연속(--)만 방지
  • 조건: if (val || !result.endsWith(sep)) — 값이 있으면 항상 추가, 값이 없으면 이미 같은 구분자로 끝나면 스킵

9. 템플릿 카테고리/참조 플레이스홀더를 실제값으로 변경

  • 결정: extractManualValuesFromInput 내부의 카테고리/참조 빈 값 반환을 "CATEGORY"/"REF"""로 변경
  • 근거: 실제 코드 생성에서 빈 카테고리는 ""인데 템플릿에서 "CATEGORY"를 쓰면 구조 불일치로 추출 실패. 로그로 확인: userInputCode=-제발-015, previewTemplate=CATEGORY-____-XXX, extractedManualValues=[]
  • 카테고리 있을 때: catMapping2?.format 반환은 수정 전후 동일하여 영향 없음

관련 파일 위치

구분 파일 경로 설명
수정 대상 backend-node/src/services/numberingRuleService.ts joinPartsWithSeparators(L36), buildPrefixKey(L75), extractManualValuesFromInput(신규), allocateCode(L1296)
신규 생성 db/migrations/1053_remove_bulk1_manual_config_value.sql BULK1 유령 값 정리 마이그레이션
변경 없음 frontend/components/screen/widgets/TextInputComponent.tsx _numberingRuleId 유지 확인 완료
변경 없음 frontend/lib/registry/components/numbering-rule/config.ts 채번 설정 레지스트리
변경 없음 frontend/components/screen/config-panels/NumberConfigPanel.tsx 채번 규칙 설정 패널
참고 backend-node/src/controllers/numberingRuleController.ts allocateNumberingCode 컨트롤러

기술 참고

allocateCode 실행 순서 (변경 전 → 후)

변경 전: buildPrefixKey(L1306) → 시퀀스 할당 → 수동 값 추출(L1332) → 코드 조합
변경 후: 수동 값 추출 → buildPrefixKey(수동 값 포함) → 시퀀스 할당 → 코드 조합

prefix_key 구성 (변경 전 → 후)

변경 전: "카테고리값"        (수동 파트 무시, 모든 접두어가 같은 키)
변경 후: "카테고리값|ㅁㅁㅁ"  (수동 파트 포함, 접두어별 독립 키)

폴백 체인 (변경 전 → 후)

변경 전: extractedManualValues[i] || manualConfig.value || ""
변경 후: extractedManualValues[i] || ""

joinPartsWithSeparators 연속 구분자 방지 (변경 전 → 후)

변경 전: "" + "-" + "" + "-" + "ㅁㅁㅁ" → "--ㅁㅁㅁ"
변경 후: "" + "-" (이미 "-"로 끝남, 스킵) + "ㅁㅁㅁ" → "-ㅁㅁㅁ"

템플릿 정합성 (변경 전 → 후)

변경 전: 카테고리 비었을 때 템플릿 = "CATEGORY-____-XXX" / 입력 = "-제발-015" → 불일치 → 추출 실패
변경 후: 카테고리 비었을 때 템플릿 = "-____-XXX"        / 입력 = "-제발-015" → 일치 → 추출 성공

10. 실시간 순번 미리보기 구현 방식

  • 결정: V2Input에서 manualInputValue 변경 시 디바운스(300ms)로 preview API를 재호출하여 suffix(순번)를 갱신
  • 근거: 기존 preview API는 manualInputValue 없이 호출되어 모든 접두어가 같은 기본 순번을 표시함. 접두어별 정확한 순번을 보여주려면 preview 시점에도 수동 값을 전달하여 해당 prefix_key의 시퀀스를 조회해야 함
  • 대안 검토: 프론트엔드에서 카운트 API를 별도 호출 → 기각 (기존 previewCode 흐름 재사용이 프로젝트 관행에 부합)
  • 디바운스 300ms: 사용자 타이핑 중 과도한 API 호출 방지. 프로젝트 기존 패턴(검색 디바운스 등)과 동일

11. previewCode에 manualInputValue 전달

  • 결정: previewCode 시그니처에 manualInputValue?: string 추가, buildPrefixKey[manualInputValue]로 전달
  • 근거: buildPrefixKey가 이미 manualValues optional 파라미터를 지원하므로 자연스럽게 확장 가능. 순번 조회 시 접두어별 독립 시퀀스를 정확히 반영함
  • 하위호환: optional 파라미터이므로 기존 호출(formData만 전달)에 영향 없음

12. 초기 상태에서 레거시 시퀀스 조회 방지

  • 결정: previewCode에서 수동 파트가 있는데 manualInputValue가 없으면 시퀀스 조회를 건너뛰고 currentSeq = 0 사용
  • 근거: 수정 전에는 모든 할당이 수동 파트 없는 공용 prefix_key를 사용했으므로 레거시 시퀀스가 누적되어 있음(예: 16). 모달 초기 상태에서 이 공용 키를 조회하면 -016이 표시됨. 아직 어떤 접두어인지 모르는 상태이므로 startFrom 기본값을 보여주는 것이 정확함
  • currentSeq = 0 + startFrom: nextSequence = 0 + startFrom(5) = 5-005 표시. 사용자가 입력하면 디바운스 preview가 해당 접두어의 실제 시퀀스를 조회

13. 카테고리 변경 시 수동 입력값 포함하여 순번 재조회

  • 결정: 초기 useEffect(카테고리 변경 트리거)에서 previewNumberingCode 호출 시 현재 manualInputValue도 함께 전달
  • 근거: 카테고리를 바꾸거나 삭제하면 prefix_key가 달라지므로 순번도 달라져야 함. 기존에는 입력값 변경과 카테고리 변경이 별도 트리거여서 카테고리 변경 시 수동 값이 누락됨
  • 빈 입력값 처리: manualInputValue || undefined로 처리하여 빈 문자열일 때는 기존처럼 skipSequenceLookup 작동

14. 카테고리 해석 로직 resolveCategoryFormat 헬퍼 통합

  • 결정: previewCode, allocateCode, extractManualValuesFromInput 3곳에 복붙된 카테고리 매핑 해석 로직을 resolveCategoryFormat private 메서드로 추출
  • 근거: 동일 로직 약 50줄이 3곳에 복사되어 있었음 (변수명만 pool2/ct2/cc2 등으로 다름). 한 곳을 수정하면 나머지도 동일하게 수정해야 하는 유지보수 위험
  • 원칙: 구조적 변경만 수행 (로직 변경 없음)

BULK1이 DB에 남아있는 이유

ManualConfigPanel.tsx: placeholder 입력란만 존재 (value 입력란 없음)
플레이스홀더 수정 시: { ...existingConfig, placeholder: newValue }
→ 기존 config에 value: "BULK1"이 있으면 스프레드로 계속 보존됨
→ UI에서 제거 불가능한 유령 값