- Enhanced the `previewCode` endpoint to accept a new `manualInputValue` parameter, allowing for dynamic sequence generation based on user input. - Updated the `NumberingRuleService` to skip legacy sequence lookups when manual input is not provided, ensuring accurate initial sequence display. - Integrated debounce functionality in the `V2Input` component to optimize API calls for real-time suffix updates as users type. - Refactored category resolution logic into a helper function to reduce code duplication and improve maintainability. These changes significantly improve the user experience by providing immediate feedback on numbering sequences based on manual inputs.
10 KiB
10 KiB
[맥락노트] 품번 수동 접두어 채번 - 접두어별 독립 순번 생성
왜 이 작업을 하는가
- 기준정보 - 품목정보 등록 모달에서 품번 인풋에 사용자가 값을 입력해도 무시되고 "BULK1"로 저장됨
- 서로 다른 접두어("ㅁㅁㅁ", "ㅇㅇㅇ")를 입력해도 전부 같은 시퀀스 카운터를 공유함
- 카테고리 미선택 시
--제발-015-003처럼 연속 구분자가 발생함 - 사용자 입력이 반영되고, 접두어별로 독립된 순번이 부여되어야 함
핵심 결정 사항과 근거
1. 수동 값 추출을 buildPrefixKey 전으로 이동
- 결정:
allocateCode내부에서 수동 값 추출 → buildPrefixKey 순서로 변경 - 근거: 기존에는 buildPrefixKey(L1306)가 먼저 실행된 후 수동 값 추출(L1332)이 진행됨. 수동 값이 prefix_key에 포함되려면 추출이 먼저 되어야 함
- 대안 검토: buildPrefixKey 내부에서 직접 추출 → 기각 (역할 분리 위반, previewCode 호출에도 영향)
2. buildPrefixKey에 수동 파트 값 포함
- 결정:
manualValuesoptional 파라미터 추가, 전달되면 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.tsx에value입력란이 없어 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가 이미manualValuesoptional 파라미터를 지원하므로 자연스럽게 확장 가능. 순번 조회 시 접두어별 독립 시퀀스를 정확히 반영함 - 하위호환: 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,extractManualValuesFromInput3곳에 복붙된 카테고리 매핑 해석 로직을resolveCategoryFormatprivate 메서드로 추출 - 근거: 동일 로직 약 50줄이 3곳에 복사되어 있었음 (변수명만 pool2/ct2/cc2 등으로 다름). 한 곳을 수정하면 나머지도 동일하게 수정해야 하는 유지보수 위험
- 원칙: 구조적 변경만 수행 (로직 변경 없음)
BULK1이 DB에 남아있는 이유
ManualConfigPanel.tsx: placeholder 입력란만 존재 (value 입력란 없음)
플레이스홀더 수정 시: { ...existingConfig, placeholder: newValue }
→ 기존 config에 value: "BULK1"이 있으면 스프레드로 계속 보존됨
→ UI에서 제거 불가능한 유령 값