- 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.
421 lines
18 KiB
Markdown
421 lines
18 KiB
Markdown
# [계획서] 품번 수동 접두어 채번 - 접두어별 독립 순번 생성
|
|
|
|
> 관련 문서: [맥락노트](./MPN[맥락]-품번-수동접두어채번.md) | [체크리스트](./MPN[체크]-품번-수동접두어채번.md)
|
|
|
|
## 개요
|
|
|
|
기준정보 - 품목 정보 등록 모달에서 품번(`item_number`) 채번의 세 가지 문제를 해결합니다.
|
|
|
|
1. **BULK1 덮어쓰기 문제**: 사용자가 "ㅁㅁㅁ"을 입력해도 수동 값 추출이 실패하여 DB 숨은 값 `manualConfig.value = "BULK1"`로 덮어씌워짐
|
|
2. **순번 공유 문제**: `buildPrefixKey`가 수동 파트를 건너뛰어 모든 접두어가 같은 시퀀스 카운터를 공유함
|
|
3. **연속 구분자(--) 문제**: 카테고리가 비었을 때 `joinPartsWithSeparators`가 빈 파트에도 구분자를 붙여 `--` 발생 + 템플릿 불일치로 수동 값 추출 실패 → `userInputCode` 전체(구분자 포함)가 수동 값이 됨
|
|
|
|
---
|
|
|
|
## 현재 동작
|
|
|
|
### 채번 규칙 구성 (옵션설정 > 코드설정)
|
|
|
|
```
|
|
규칙1(카테고리/재질, 자동) → "-" → 규칙2(문자, 직접입력) → "-" → 규칙3(순번, 자동, 3자리, 시작=5)
|
|
```
|
|
|
|
### 실제 저장 흐름 (사용자가 "ㅁㅁㅁ" 입력 시)
|
|
|
|
1. 모달 열림 → `_numberingRuleId` 설정됨 (TextInputComponent L117-128)
|
|
2. 사용자가 "ㅁㅁㅁ" 입력 → `formData.item_number = "ㅁㅁㅁ"`
|
|
3. 저장 클릭 → `buttonActions.ts`가 `_numberingRuleId` 확인 → `allocateCode(ruleId, "ㅁㅁㅁ", formData)` 호출
|
|
4. 백엔드: 템플릿 기반 수동 값 추출 시도 → **실패** (입력 "ㅁㅁㅁ"이 템플릿 "CATEGORY-____-XXX"와 불일치)
|
|
5. 폴백: `manualConfig.value = "BULK1"` 사용 → **사용자 입력 "ㅁㅁㅁ" 완전 무시됨**
|
|
6. `buildPrefixKey`가 수동 파트를 건너뜀 → prefix_key에 접두어 미포함 → 공유 카운터 사용
|
|
7. 결과: **-BULK1-015** (사용자가 뭘 입력하든 항상 BULK1, 항상 공유 카운터)
|
|
|
|
### 문제 1: 순번 공유 (buildPrefixKey)
|
|
|
|
**위치**: `numberingRuleService.ts` L85-88
|
|
|
|
```typescript
|
|
if (part.generationMethod === "manual") {
|
|
// 수동 입력 파트는 prefix에서 제외 (값이 매번 달라질 수 있으므로)
|
|
continue; // ← 접두어별 순번 분리를 막는 원인
|
|
}
|
|
```
|
|
|
|
이 `continue` 때문에 수동 입력값이 prefix_key에 포함되지 않습니다.
|
|
"ㅁㅁㅁ", "ㅇㅇㅇ", "BULK1" 전부 **같은 시퀀스 카운터를 공유**합니다.
|
|
|
|
### 문제 2: BULK1 덮어쓰기 (추출 실패 + manualConfig.value 폴백)
|
|
|
|
**발생 흐름**:
|
|
|
|
1. 사용자가 "ㅁㅁㅁ" 입력 → `userInputCode = "ㅁㅁㅁ"` 으로 `allocateCode` 호출
|
|
2. `allocateCode` 내부에서 **prefix_key를 먼저 빌드** (L1306) → 수동 값 추출은 그 이후 (L1332-1442)
|
|
3. 템플릿 기반 수동 값 추출 시도 (L1411-1436):
|
|
```
|
|
템플릿: "카테고리값-____-XXX" (카테고리값-수동입력위치-순번)
|
|
사용자 입력: "ㅁㅁㅁ"
|
|
```
|
|
4. "ㅁㅁㅁ"은 "카테고리값-"으로 시작하지 않음 → `startsWith` 불일치 → **추출 실패** → `extractedManualValues = []`
|
|
5. 코드 조합 단계 (L1448-1454)에서 폴백 체인 동작:
|
|
```typescript
|
|
const manualValue =
|
|
extractedManualValues[0] || // undefined (추출 실패)
|
|
part.manualConfig?.value || // "BULK1" (DB 숨은 값) ← 여기서 덮어씌워짐
|
|
"";
|
|
```
|
|
6. 결과: `-BULK1-015` (사용자 입력 "ㅁㅁㅁ"이 완전히 무시됨)
|
|
|
|
**DB 숨은 값 원인**:
|
|
- DB `numbering_rule_parts.manual_config` 컬럼에 `{"value": "BULK1", "placeholder": "..."}` 저장됨
|
|
- `ManualConfigPanel.tsx`에는 `placeholder` 입력란만 있고 **`value` 입력란이 없음**
|
|
- 플레이스홀더 수정 시 `{ ...config, placeholder: ... }` 스프레드로 기존 `value: "BULK1"`이 계속 보존됨
|
|
|
|
### 문제 3: 연속 구분자(--) 문제
|
|
|
|
**발생 흐름**:
|
|
|
|
1. 카테고리 미선택 → 카테고리 파트 값 = `""` (빈 문자열)
|
|
2. `joinPartsWithSeparators`가 빈 파트에도 구분자 `-`를 추가 → 연속 빈 파트 시 `--` 발생
|
|
3. 사용자 입력 필드에 `-제발-015` 형태로 표시 (선행 `-`)
|
|
4. `extractManualValuesFromInput`에서 템플릿이 `CATEGORY-____-XXX`로 생성됨 (실제 값 `""` 대신 플레이스홀더 `"CATEGORY"` 사용)
|
|
5. 입력 `-제발-015`이 `CATEGORY-`로 시작하지 않음 → 추출 실패
|
|
6. 폴백: `userInputCode` 전체 `-제발-015`가 수동 값이 됨
|
|
7. 코드 조합: `""` + `-` + `-제발-015` + `-` + `003` = `--제발-015-003`
|
|
|
|
### 정상 동작 확인된 부분
|
|
|
|
| 항목 | 상태 | 근거 |
|
|
|------|------|------|
|
|
| `_numberingRuleId` 유지 | 정상 | 사용자 입력해도 allocateCode가 호출됨 |
|
|
| 시퀀스 증가 | 정상 | 순번이 증가하고 있음 (015 등) |
|
|
| 코드 조합 | 정상 | 구분자, 파트 순서 등 올바르게 결합됨 |
|
|
|
|
### 비정상 확인된 부분
|
|
|
|
| 항목 | 상태 | 근거 |
|
|
|------|------|------|
|
|
| 수동 값 추출 | **실패** | 사용자 입력 "ㅁㅁㅁ"이 템플릿과 불일치 → 추출 실패 → BULK1 폴백 |
|
|
| prefix_key 분리 | **실패** | `buildPrefixKey`가 수동 파트 skip → 모든 접두어가 같은 시퀀스 공유 |
|
|
| 연속 구분자 | **실패** | 빈 파트에 구분자 추가 + 템플릿 플레이스홀더 불일치 → `--` 발생 |
|
|
|
|
---
|
|
|
|
## 변경 후 동작
|
|
|
|
### prefix_key에 수동 파트 값 포함
|
|
|
|
```
|
|
현재: prefix_key = 카테고리값만 (수동 파트 무시)
|
|
변경: prefix_key = 카테고리값 + "|" + 수동입력값
|
|
```
|
|
|
|
### allocateCode 실행 순서 변경
|
|
|
|
```
|
|
현재: buildPrefixKey → 시퀀스 할당 → 수동 값 추출 → 코드 조합
|
|
변경: 수동 값 추출 → buildPrefixKey(수동 값 포함) → 시퀀스 할당 → 코드 조합
|
|
```
|
|
|
|
### 순번 동작
|
|
|
|
```
|
|
"ㅁㅁㅁ" 첫 등록 → prefix_key="카테고리|ㅁㅁㅁ", sequence=1 → -ㅁㅁㅁ-001
|
|
"ㅁㅁㅁ" 두번째 → prefix_key="카테고리|ㅁㅁㅁ", sequence=2 → -ㅁㅁㅁ-002
|
|
"ㅇㅇㅇ" 첫 등록 → prefix_key="카테고리|ㅇㅇㅇ", sequence=1 → -ㅇㅇㅇ-001
|
|
"ㅁㅁㅁ" 세번째 → prefix_key="카테고리|ㅁㅁㅁ", sequence=3 → -ㅁㅁㅁ-003
|
|
```
|
|
|
|
### BULK1 폴백 제거 (코드 + DB 이중 조치)
|
|
|
|
```
|
|
코드: 폴백 체인에서 manualConfig.value 제거 → extractedManualValues만 사용
|
|
DB: manual_config에서 "value": "BULK1" 키 제거 → 유령 기본값 정리
|
|
```
|
|
|
|
### 연속 구분자 방지 + 템플릿 정합성 복원
|
|
|
|
```
|
|
joinPartsWithSeparators: 빈 파트 뒤에 이미 구분자가 있으면 중복 추가하지 않음
|
|
extractManualValuesFromInput: 카테고리/참조 빈 값 시 "" 반환 (플레이스홀더 "CATEGORY"/"REF" 대신)
|
|
→ 템플릿이 실제 코드 구조와 일치 → 추출 성공 → -- 방지
|
|
```
|
|
|
|
---
|
|
|
|
## 시각적 예시
|
|
|
|
| 사용자 입력 | 현재 동작 | 원인 | 변경 후 동작 |
|
|
|------------|----------|------|-------------|
|
|
| `ㅁㅁㅁ` (첫번째) | `-BULK1-015` | 추출 실패 → BULK1 폴백 + 공유 카운터 | `카테고리값-ㅁㅁㅁ-001` |
|
|
| `ㅁㅁㅁ` (두번째) | `-BULK1-016` | 동일 | `카테고리값-ㅁㅁㅁ-002` |
|
|
| `ㅇㅇㅇ` (첫번째) | `-BULK1-017` | 동일 | `카테고리값-ㅇㅇㅇ-001` |
|
|
| (입력 안 함) | `-BULK1-018` | manualConfig.value 폴백 | 에러 반환 (수동 파트 필수 입력) |
|
|
| 카테고리 비었을 때 | `--제발-015-003` | 빈 파트 구분자 중복 + 템플릿 불일치 | `-제발-001` |
|
|
|
|
---
|
|
|
|
## 아키텍처
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User as 사용자
|
|
participant BA as buttonActions.ts
|
|
participant API as allocateNumberingCode API
|
|
participant NRS as numberingRuleService
|
|
participant DB as numbering_rule_sequences
|
|
|
|
User->>BA: 저장 클릭 (item_number = "ㅁㅁㅁ")
|
|
BA->>API: allocateCode(ruleId, "ㅁㅁㅁ", formData)
|
|
API->>NRS: allocateCode()
|
|
|
|
Note over NRS: 1단계: 수동 값 추출 (buildPrefixKey 전에 수행)
|
|
NRS->>NRS: extractManualValuesFromInput("ㅁㅁㅁ")
|
|
Note over NRS: 템플릿 파싱 실패 → 폴백: userInputCode 전체 사용
|
|
NRS->>NRS: extractedManualValues = ["ㅁㅁㅁ"]
|
|
|
|
Note over NRS: 2단계: prefix_key 빌드 (수동 값 포함)
|
|
NRS->>NRS: buildPrefixKey(rule, formData, ["ㅁㅁㅁ"])
|
|
Note over NRS: prefix_key = "카테고리값|ㅁㅁㅁ"
|
|
|
|
Note over NRS: 3단계: 시퀀스 할당
|
|
NRS->>DB: UPSERT sequences (prefix_key="카테고리값|ㅁㅁㅁ")
|
|
DB-->>NRS: current_sequence = 1
|
|
|
|
Note over NRS: 4단계: 코드 조합
|
|
NRS->>NRS: 카테고리값 + "-" + "ㅁㅁㅁ" + "-" + "001"
|
|
NRS-->>API: "카테고리값-ㅁㅁㅁ-001"
|
|
API-->>BA: generatedCode
|
|
BA->>BA: formData.item_number = "카테고리값-ㅁㅁㅁ-001"
|
|
```
|
|
|
|
---
|
|
|
|
## 변경 대상 파일
|
|
|
|
| 파일 | 변경 내용 | 규모 |
|
|
|------|----------|------|
|
|
| `backend-node/src/services/numberingRuleService.ts` | `buildPrefixKey`에 `manualValues` 파라미터 추가, `allocateCode`에서 수동 값 추출 순서 변경 + 폴백 체인 정리, `extractManualValuesFromInput` 헬퍼 분리, `joinPartsWithSeparators` 연속 구분자 방지, 템플릿 카테고리/참조 플레이스홀더를 실제값으로 변경, `previewCode`에 `manualInputValue` 파라미터 추가 + `startFrom` 적용 | ~80줄 |
|
|
| `backend-node/src/controllers/numberingRuleController.ts` | preview 엔드포인트에 `manualInputValue` body 파라미터 수신 추가 | ~2줄 |
|
|
| `frontend/lib/api/numberingRule.ts` | `previewNumberingCode`에 `manualInputValue` 파라미터 추가 | ~3줄 |
|
|
| `frontend/components/v2/V2Input.tsx` | 수동 입력값 변경 시 디바운스(300ms) preview API 호출 + suffix(순번) 실시간 갱신 | ~35줄 |
|
|
| `db/migrations/1053_remove_bulk1_manual_config_value.sql` | `numbering_rule_parts.manual_config`에서 `value: "BULK1"` 제거 | SQL 1건 |
|
|
|
|
### buildPrefixKey 호출부 영향 분석
|
|
|
|
| 호출부 | 위치 | `manualValues` 전달 | 영향 |
|
|
|--------|------|---------------------|------|
|
|
| `previewCode` | L1091 | `manualInputValue` 전달 시 포함 | 접두어별 정확한 순번 조회 |
|
|
| `allocateCode` | L1332 | 전달 | prefix_key에 수동 값 포함됨 |
|
|
|
|
### 멀티테넌시 체크
|
|
|
|
| 항목 | 상태 | 근거 |
|
|
|------|------|------|
|
|
| `buildPrefixKey` | 영향 없음 | 시그니처만 확장, company_code 관련 변경 없음 |
|
|
| `allocateCode` | 이미 준수 | L1302에서 `companyCode`로 규칙 조회, L1313에서 시퀀스 할당 시 `companyCode` 전달 |
|
|
| `joinPartsWithSeparators` | 영향 없음 | 순수 문자열 조합 함수, company_code 무관 |
|
|
| DB 마이그레이션 | 해당 없음 | JSONB 내부 값 정리, company_code 무관 |
|
|
|
|
---
|
|
|
|
## 코드 설계
|
|
|
|
### 1. `joinPartsWithSeparators` 수정 - 연속 구분자 방지
|
|
|
|
**위치**: L36-48
|
|
**변경**: 빈 파트 뒤에 이미 구분자가 있으면 중복 추가하지 않음
|
|
|
|
```typescript
|
|
function joinPartsWithSeparators(partValues: string[], sortedParts: any[], globalSeparator: string): string {
|
|
let result = "";
|
|
partValues.forEach((val, idx) => {
|
|
result += val;
|
|
if (idx < partValues.length - 1) {
|
|
const sep = sortedParts[idx].separatorAfter ?? sortedParts[idx].autoConfig?.separatorAfter ?? globalSeparator;
|
|
if (val || !result.endsWith(sep)) {
|
|
result += sep;
|
|
}
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
```
|
|
|
|
### 2. `buildPrefixKey` 수정 - 수동 파트 값을 prefix에 포함
|
|
|
|
**위치**: L75-88
|
|
**변경**: 세 번째 파라미터 `manualValues` 추가. 전달되면 prefix_key에 포함.
|
|
|
|
```typescript
|
|
private async buildPrefixKey(
|
|
rule: NumberingRuleConfig,
|
|
formData?: Record<string, any>,
|
|
manualValues?: string[]
|
|
): Promise<string> {
|
|
const sortedParts = [...rule.parts].sort((a: any, b: any) => a.order - b.order);
|
|
const prefixParts: string[] = [];
|
|
let manualIndex = 0;
|
|
|
|
for (const part of sortedParts) {
|
|
if (part.partType === "sequence") continue;
|
|
|
|
if (part.generationMethod === "manual") {
|
|
const manualValue = manualValues?.[manualIndex] || "";
|
|
manualIndex++;
|
|
if (manualValue) {
|
|
prefixParts.push(manualValue);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// ... 나머지 기존 로직 (text, date, category, reference 등) 그대로 유지 ...
|
|
}
|
|
|
|
return prefixParts.join("|");
|
|
}
|
|
```
|
|
|
|
**하위 호환성**: `manualValues`는 optional. `previewCode`(L1091)는 전달하지 않으므로 동작 변화 없음.
|
|
|
|
### 3. `allocateCode` 수정 - 수동 값 추출 순서 변경 + 폴백 정리
|
|
|
|
**위치**: L1290-1584
|
|
**핵심 변경 2가지**:
|
|
|
|
(A) 기존에는 `buildPrefixKey`(L1306) → 수동 값 추출(L1332) 순서였으나, **수동 값 추출 → `buildPrefixKey`** 순서로 변경.
|
|
|
|
(B) 코드 조합 단계(L1448-1454)에서 `manualConfig.value` 폴백 제거.
|
|
|
|
```typescript
|
|
async allocateCode(ruleId, companyCode, formData?, userInputCode?) {
|
|
// ... 규칙 조회 ...
|
|
|
|
// 1단계: 수동 파트 값 추출 (buildPrefixKey 호출 전에 수행)
|
|
const manualParts = rule.parts.filter(p => p.generationMethod === "manual");
|
|
let extractedManualValues: string[] = [];
|
|
|
|
if (manualParts.length > 0 && userInputCode) {
|
|
extractedManualValues = await this.extractManualValuesFromInput(
|
|
rule, userInputCode, formData
|
|
);
|
|
|
|
// 폴백: 추출 실패 시 userInputCode 전체를 수동 값으로 사용
|
|
if (extractedManualValues.length === 0 && manualParts.length === 1) {
|
|
extractedManualValues = [userInputCode];
|
|
}
|
|
}
|
|
|
|
// 2단계: 수동 값을 포함하여 prefix_key 빌드
|
|
const prefixKey = await this.buildPrefixKey(rule, formData, extractedManualValues);
|
|
|
|
// 3단계: 시퀀스 할당 (기존 로직 그대로)
|
|
|
|
// 4단계: 코드 조합 (manualConfig.value 폴백 제거)
|
|
// 기존: extractedManualValues[i] || part.manualConfig?.value || ""
|
|
// 변경: extractedManualValues[i] || ""
|
|
}
|
|
```
|
|
|
|
### 4. `extractManualValuesFromInput` 헬퍼 분리 + 템플릿 정합성 복원
|
|
|
|
기존 `allocateCode` 내부의 수동 값 추출 로직(L1332-1442)을 별도 private 메서드로 추출.
|
|
로직 자체는 변경 없음, 위치만 이동.
|
|
카테고리/참조 파트의 빈 값 처리를 실제 코드 생성과 일치시킴.
|
|
|
|
```typescript
|
|
private async extractManualValuesFromInput(
|
|
rule: NumberingRuleConfig,
|
|
userInputCode: string,
|
|
formData?: Record<string, any>
|
|
): Promise<string[]> {
|
|
// 기존 L1332-1442의 로직을 그대로 이동
|
|
// 변경: 카테고리/참조 빈 값 시 "CATEGORY"/"REF" 대신 "" 반환
|
|
// → 템플릿이 실제 코드 구조와 일치 → 추출 성공률 향상
|
|
}
|
|
```
|
|
|
|
### 5. DB 마이그레이션 - BULK1 유령 기본값 제거
|
|
|
|
**파일**: `db/migrations/1053_remove_bulk1_manual_config_value.sql`
|
|
|
|
`numbering_rule_parts.manual_config` 컬럼에서 `value` 키를 제거합니다.
|
|
|
|
```sql
|
|
-- manual_config에서 "value" 키 제거 (BULK1 유령 기본값 정리)
|
|
UPDATE numbering_rule_parts
|
|
SET manual_config = manual_config - 'value'
|
|
WHERE generation_method = 'manual'
|
|
AND manual_config ? 'value'
|
|
AND manual_config->>'value' = 'BULK1';
|
|
```
|
|
|
|
> PostgreSQL JSONB 연산자 `-`를 사용하여 특정 키만 제거.
|
|
> `manual_config`의 나머지 필드(`placeholder` 등)는 유지됨.
|
|
> "BULK1" 값을 가진 레코드만 대상으로 하여 안전성 확보.
|
|
|
|
---
|
|
|
|
## 설계 원칙
|
|
|
|
- **변경 범위 최소화**: `numberingRuleService.ts` 코드 변경 + DB 마이그레이션 1건
|
|
- **이중 조치**: 코드에서 `manualConfig.value` 폴백 제거 + DB에서 유령 값 정리
|
|
- `buildPrefixKey`의 `manualValues`는 optional → 기존 호출부(`previewCode` 등)에 영향 없음
|
|
- `allocateCode` 내부 로직 순서만 변경 (추출 → prefix_key 빌드), 새 로직 추가 아님
|
|
- 수동 값 추출 로직은 기존 코드를 헬퍼로 분리할 뿐, 로직 자체는 변경 없음
|
|
- DB 마이그레이션은 "BULK1" 값만 정확히 타겟팅하여 부작용 방지
|
|
- `TextInputComponent.tsx` 변경 불필요 (현재 동작이 올바름)
|
|
- 프론트엔드 변경 없음 → 프론트엔드 테스트 불필요
|
|
- `joinPartsWithSeparators`는 연속 구분자만 방지, 기존 구분자 구조 유지
|
|
- 템플릿 카테고리/참조 빈 값을 실제 코드와 일치시켜 추출 성공률 향상
|
|
|
|
---
|
|
|
|
## 실시간 순번 미리보기 (추가 기능)
|
|
|
|
### 배경
|
|
|
|
품목 등록 모달에서 수동 입력 세그먼트 우측에 표시되는 순번(suffix)이 입력값과 무관하게 고정되어 있었음. 사용자가 "ㅇㅇ"을 입력하면 해당 접두어로 이미 몇 개가 등록되었는지에 따라 순번이 달라져야 함.
|
|
|
|
### 목표 동작
|
|
|
|
```
|
|
모달 열림 : -[입력하시오]-005 (startFrom=5 기반 기본 순번)
|
|
"ㅇㅇ" 입력 : -[ㅇㅇ]-005 (기존 "ㅇㅇ" 등록 0건)
|
|
저장 후 재입력 "ㅇㅇ": -[ㅇㅇ]-006 (기존 "ㅇㅇ" 등록 1건)
|
|
```
|
|
|
|
### 아키텍처
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User as 사용자
|
|
participant V2 as V2Input
|
|
participant API as previewNumberingCode
|
|
participant BE as numberingRuleService.previewCode
|
|
participant DB as numbering_rule_sequences
|
|
|
|
User->>V2: 수동 입력 "ㅇㅇ"
|
|
Note over V2: 디바운스 300ms
|
|
V2->>API: preview(ruleId, formData, "ㅇㅇ")
|
|
API->>BE: previewCode(ruleId, companyCode, formData, "ㅇㅇ")
|
|
BE->>BE: buildPrefixKey(rule, formData, ["ㅇㅇ"])
|
|
Note over BE: prefix_key = "카테고리|ㅇㅇ"
|
|
BE->>DB: getSequenceForPrefix(prefix_key)
|
|
DB-->>BE: currentSeq = 0
|
|
Note over BE: nextSequence = 0 + startFrom(5) = 5
|
|
BE-->>API: "-____-005"
|
|
API-->>V2: generatedCode
|
|
V2->>V2: suffix = "-005" 갱신
|
|
Note over V2: 화면 표시: -[ㅇㅇ]-005
|
|
```
|
|
|
|
### 변경 내용
|
|
|
|
1. **백엔드 컨트롤러**: preview 엔드포인트가 `req.body.manualInputValue` 수신
|
|
2. **백엔드 서비스**: `previewCode`가 `manualInputValue`를 받아 `buildPrefixKey`에 전달 → 접두어별 정확한 시퀀스 조회
|
|
3. **백엔드 서비스**: 수동 파트가 있는데 `manualInputValue`가 없는 초기 상태 → 레거시 공용 시퀀스 조회 건너뜀, `currentSeq = 0` 사용 → `startFrom` 기본값 표시
|
|
4. **프론트엔드 API**: `previewNumberingCode`에 `manualInputValue` 파라미터 추가
|
|
5. **V2Input**: `manualInputValue` 변경 시 디바운스(300ms) preview API 재호출 → `numberingTemplateRef` 갱신 → suffix 실시간 업데이트
|
|
6. **V2Input**: 카테고리 변경 시 초기 useEffect에서도 현재 `manualInputValue`를 preview에 전달 → 카테고리 변경/삭제 시 순번 즉시 반영
|
|
7. **코드 정리**: 카테고리 해석 로직 3곳 중복 → `resolveCategoryFormat` 헬퍼로 통합 (약 100줄 감소)
|