refactor: 코드 정리 및 가독성 향상

- numberingRuleController.ts에서 API 엔드포인트의 코드 스타일을 일관되게 정리하여 가독성을 높였습니다.
- 불필요한 줄바꿈을 제거하고, 코드 블록을 명확하게 정리하여 유지보수성을 개선했습니다.
- tableManagementService.ts와 ButtonConfigPanel.tsx에서 코드 정리를 통해 일관성을 유지하고, 가독성을 향상시켰습니다.
- 전반적으로 코드의 깔끔함을 유지하고, 향후 개발 시 이해하기 쉽게 개선했습니다.
This commit is contained in:
kjs
2026-02-05 17:38:06 +09:00
parent 73d05b991c
commit e31bb970a2
11 changed files with 1570 additions and 1348 deletions

View File

@@ -26,13 +26,9 @@ export async function getNumberingRules(): Promise<ApiResponse<NumberingRuleConf
* @param menuObjid 현재 메뉴의 objid (선택)
* @returns 사용 가능한 채번 규칙 목록
*/
export async function getAvailableNumberingRules(
menuObjid?: number
): Promise<ApiResponse<NumberingRuleConfig[]>> {
export async function getAvailableNumberingRules(menuObjid?: number): Promise<ApiResponse<NumberingRuleConfig[]>> {
try {
const url = menuObjid
? `/numbering-rules/available/${menuObjid}`
: "/numbering-rules/available";
const url = menuObjid ? `/numbering-rules/available/${menuObjid}` : "/numbering-rules/available";
const response = await apiClient.get(url);
return response.data;
} catch (error: any) {
@@ -46,7 +42,7 @@ export async function getAvailableNumberingRules(
* @returns 해당 테이블의 채번 규칙 목록
*/
export async function getAvailableNumberingRulesForScreen(
tableName: string
tableName: string,
): Promise<ApiResponse<NumberingRuleConfig[]>> {
try {
const response = await apiClient.get("/numbering-rules/available-for-screen", {
@@ -70,9 +66,7 @@ export async function getNumberingRuleById(ruleId: string): Promise<ApiResponse<
}
}
export async function createNumberingRule(
config: NumberingRuleConfig
): Promise<ApiResponse<NumberingRuleConfig>> {
export async function createNumberingRule(config: NumberingRuleConfig): Promise<ApiResponse<NumberingRuleConfig>> {
try {
const response = await apiClient.post("/numbering-rules", config);
return response.data;
@@ -83,7 +77,7 @@ export async function createNumberingRule(
export async function updateNumberingRule(
ruleId: string,
config: Partial<NumberingRuleConfig>
config: Partial<NumberingRuleConfig>,
): Promise<ApiResponse<NumberingRuleConfig>> {
try {
const response = await apiClient.put(`/numbering-rules/${ruleId}`, config);
@@ -110,7 +104,7 @@ export async function deleteNumberingRule(ruleId: string): Promise<ApiResponse<v
*/
export async function previewNumberingCode(
ruleId: string,
formData?: Record<string, unknown>
formData?: Record<string, unknown>,
): Promise<ApiResponse<{ generatedCode: string }>> {
// ruleId 유효성 검사
if (!ruleId || ruleId === "undefined" || ruleId === "null") {
@@ -127,11 +121,8 @@ export async function previewNumberingCode(
return response.data;
} catch (error: unknown) {
const err = error as { response?: { data?: { error?: string; message?: string } }; message?: string };
const errorMessage =
err.response?.data?.error ||
err.response?.data?.message ||
err.message ||
"코드 미리보기 실패";
const errorMessage =
err.response?.data?.error || err.response?.data?.message || err.message || "코드 미리보기 실패";
return { success: false, error: errorMessage };
}
}
@@ -146,7 +137,7 @@ export async function previewNumberingCode(
export async function allocateNumberingCode(
ruleId: string,
userInputCode?: string,
formData?: Record<string, any>
formData?: Record<string, any>,
): Promise<ApiResponse<{ generatedCode: string }>> {
try {
const response = await apiClient.post(`/numbering-rules/${ruleId}/allocate`, {
@@ -162,9 +153,7 @@ export async function allocateNumberingCode(
/**
* @deprecated 기존 generateNumberingCode는 previewNumberingCode를 사용하세요
*/
export async function generateNumberingCode(
ruleId: string
): Promise<ApiResponse<{ generatedCode: string }>> {
export async function generateNumberingCode(ruleId: string): Promise<ApiResponse<{ generatedCode: string }>> {
console.warn("generateNumberingCode는 deprecated. previewNumberingCode 사용 권장");
return previewNumberingCode(ruleId);
}
@@ -188,13 +177,9 @@ export async function resetSequence(ruleId: string): Promise<ApiResponse<void>>
* numbering_rules 테이블 사용
* @param menuObjid 메뉴 OBJID (선택) - 필터링용
*/
export async function getNumberingRulesFromTest(
menuObjid?: number
): Promise<ApiResponse<NumberingRuleConfig[]>> {
export async function getNumberingRulesFromTest(menuObjid?: number): Promise<ApiResponse<NumberingRuleConfig[]>> {
try {
const url = menuObjid
? `/numbering-rules/test/list/${menuObjid}`
: "/numbering-rules/test/list";
const url = menuObjid ? `/numbering-rules/test/list/${menuObjid}` : "/numbering-rules/test/list";
const response = await apiClient.get(url);
return response.data;
} catch (error: any) {
@@ -211,7 +196,7 @@ export async function getNumberingRulesFromTest(
*/
export async function getNumberingRuleByColumn(
tableName: string,
columnName: string
columnName: string,
): Promise<ApiResponse<NumberingRuleConfig>> {
try {
const response = await apiClient.get("/numbering-rules/test/by-column", {
@@ -230,9 +215,7 @@ export async function getNumberingRuleByColumn(
* [테스트] 테스트 테이블에 채번규칙 저장
* numbering_rules 테이블 사용
*/
export async function saveNumberingRuleToTest(
config: NumberingRuleConfig
): Promise<ApiResponse<NumberingRuleConfig>> {
export async function saveNumberingRuleToTest(config: NumberingRuleConfig): Promise<ApiResponse<NumberingRuleConfig>> {
try {
const response = await apiClient.post("/numbering-rules/test/save", config);
return response.data;
@@ -248,9 +231,7 @@ export async function saveNumberingRuleToTest(
* [테스트] 테스트 테이블에서 채번규칙 삭제
* numbering_rules 테이블 사용
*/
export async function deleteNumberingRuleFromTest(
ruleId: string
): Promise<ApiResponse<void>> {
export async function deleteNumberingRuleFromTest(ruleId: string): Promise<ApiResponse<void>> {
try {
const response = await apiClient.delete(`/numbering-rules/test/${ruleId}`);
return response.data;
@@ -270,7 +251,7 @@ export async function getNumberingRuleByColumnWithCategory(
tableName: string,
columnName: string,
categoryColumn?: string,
categoryValueId?: number
categoryValueId?: number,
): Promise<ApiResponse<NumberingRuleConfig>> {
try {
const response = await apiClient.get("/numbering-rules/test/by-column-with-category", {
@@ -290,7 +271,7 @@ export async function getNumberingRuleByColumnWithCategory(
*/
export async function getRulesByTableColumn(
tableName: string,
columnName: string
columnName: string,
): Promise<ApiResponse<NumberingRuleConfig[]>> {
try {
const response = await apiClient.get("/numbering-rules/test/rules-by-table-column", {
@@ -304,4 +285,3 @@ export async function getRulesByTableColumn(
};
}
}

View File

@@ -115,14 +115,14 @@ const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
className="absolute top-0 right-0 h-full px-2 hover:bg-transparent"
onClick={() => !isDisabled && setOpen(!open)}
disabled={isDisabled}
>
{loading ? (
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
) : (
<ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
<ChevronsUpDown className="text-muted-foreground h-4 w-4" />
)}
</Button>
</div>
@@ -149,12 +149,7 @@ const CascadingSelectField: React.FC<CascadingSelectFieldProps> = ({
setOpen(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
value === option.value ? "opacity-100" : "opacity-0"
)}
/>
<Check className={cn("mr-2 h-4 w-4", value === option.value ? "opacity-100" : "opacity-0")} />
{option.label}
</CommandItem>
))}
@@ -437,19 +432,19 @@ export function UniversalFormModalComponent({
}
// 🆕 테이블 섹션 데이터 병합 (품목 리스트 등)
// 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블),
// 참고: initializeForm에서 DB 로드 시 __tableSection_ (더블),
// handleTableDataChange에서 수정 시 _tableSection_ (싱글) 사용
for (const [key, value] of Object.entries(formData)) {
// 싱글/더블 언더스코어 모두 처리
if ((key.startsWith("_tableSection_") || key.startsWith("__tableSection_")) && Array.isArray(value)) {
// 저장 시에는 _tableSection_ 키로 통일 (buttonActions.ts에서 이 키를 기대)
const normalizedKey = key.startsWith("__tableSection_")
? key.replace("__tableSection_", "_tableSection_")
const normalizedKey = key.startsWith("__tableSection_")
? key.replace("__tableSection_", "_tableSection_")
: key;
event.detail.formData[normalizedKey] = value;
console.log(`[UniversalFormModal] 테이블 섹션 병합: ${key}${normalizedKey}, ${value.length}개 항목`);
}
// 🆕 원본 테이블 섹션 데이터도 병합 (삭제 추적용)
if (key.startsWith("_originalTableSectionData_") && Array.isArray(value)) {
event.detail.formData[key] = value;
@@ -948,13 +943,17 @@ export function UniversalFormModalComponent({
// 각 테이블 섹션별로 별도의 키에 원본 데이터 저장 (groupedDataInitializedRef와 무관하게 항상 저장)
const originalTableSectionKey = `_originalTableSectionData_${section.id}`;
newFormData[originalTableSectionKey] = JSON.parse(JSON.stringify(items));
console.log(`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`);
console.log(
`[initializeForm] 테이블 섹션 ${section.id}: formData[${originalTableSectionKey}]에 원본 ${items.length}건 저장`,
);
// 기존 originalGroupedData에도 추가 (하위 호환성)
if (!groupedDataInitializedRef.current) {
setOriginalGroupedData((prev) => {
const newOriginal = [...prev, ...JSON.parse(JSON.stringify(items))];
console.log(`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`);
console.log(
`[initializeForm] 테이블 섹션 ${section.id}: originalGroupedData에 ${items.length}건 추가 (총 ${newOriginal.length}건)`,
);
return newOriginal;
});
}
@@ -1639,12 +1638,12 @@ export function UniversalFormModalComponent({
/>
);
}
// 🆕 연쇄 드롭다운 처리 (selectOptions.type === "cascading" 방식)
if (field.selectOptions?.type === "cascading" && field.selectOptions?.cascading?.parentField) {
const cascadingOpts = field.selectOptions.cascading;
const parentValue = formData[cascadingOpts.parentField];
// selectOptions 기반 cascading config를 CascadingDropdownConfig 형태로 변환
const cascadingConfig: CascadingDropdownConfig = {
enabled: true,
@@ -2393,7 +2392,7 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
const [inputValue, setInputValue] = useState(value || "");
const allowCustomInput = optionConfig?.allowCustomInput || false;
useEffect(() => {
@@ -2433,14 +2432,14 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
type="button"
variant="ghost"
size="sm"
className="absolute right-0 top-0 h-full px-2 hover:bg-transparent"
className="absolute top-0 right-0 h-full px-2 hover:bg-transparent"
onClick={() => !disabled && !loading && setOpen(!open)}
disabled={disabled || loading}
>
{loading ? (
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin" />
) : (
<ChevronsUpDown className="h-4 w-4 text-muted-foreground" />
<ChevronsUpDown className="text-muted-foreground h-4 w-4" />
)}
</Button>
</div>
@@ -2463,12 +2462,7 @@ function SelectField({ fieldId, value, onChange, optionConfig, placeholder, disa
setOpen(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
value === option.value ? "opacity-100" : "opacity-0"
)}
/>
<Check className={cn("mr-2 h-4 w-4", value === option.value ? "opacity-100" : "opacity-0")} />
{option.label}
</CommandItem>
))}

View File

@@ -47,7 +47,7 @@ export class V2InputRenderer extends AutoRegisteringComponentRenderer {
const style = component.style || {};
const labelDisplay = style.labelDisplay ?? (component as any).labelDisplay;
// labelDisplay: true → 라벨 표시, false → 숨김, undefined → 기존 동작 유지(숨김)
const effectiveLabel = labelDisplay === true ? (style.labelText || component.label) : undefined;
const effectiveLabel = labelDisplay === true ? style.labelText || component.label : undefined;
return (
<V2Input

View File

@@ -533,14 +533,14 @@ export class ButtonActionExecutor {
// 🆕 저장 전 이벤트 발생 (SelectedItemsDetailInput 등에서 최신 데이터 수집)
// context.formData를 이벤트 detail에 포함하여 직접 수정 가능하게 함
// skipDefaultSave 플래그를 통해 기본 저장 로직을 건너뛸 수 있음
// 🔧 디버그: beforeFormSave 이벤트 전 formData 확인
console.log("🔍 [handleSave] beforeFormSave 이벤트 전:", {
keys: Object.keys(context.formData || {}),
hasCompanyImage: "company_image" in (context.formData || {}),
companyImageValue: context.formData?.company_image,
});
const beforeSaveEventDetail = {
formData: context.formData,
skipDefaultSave: false,
@@ -555,7 +555,7 @@ export class ButtonActionExecutor {
// 약간의 대기 시간을 주어 이벤트 핸들러가 formData를 업데이트할 수 있도록 함
await new Promise((resolve) => setTimeout(resolve, 100));
// 🔧 디버그: beforeFormSave 이벤트 후 formData 확인
console.log("🔍 [handleSave] beforeFormSave 이벤트 후:", {
keys: Object.keys(context.formData || {}),
@@ -1626,7 +1626,9 @@ export class ButtonActionExecutor {
// saveResult.data.data.data = 실제 폼 데이터 { sabun, user_name... }
const savedRecord = saveResult?.data?.data || saveResult?.data || {};
const actualFormData = savedRecord?.data || savedRecord;
const formData: Record<string, any> = (Object.keys(actualFormData).length > 0 ? actualFormData : context.formData || {}) as Record<string, any>;
const formData: Record<string, any> = (
Object.keys(actualFormData).length > 0 ? actualFormData : context.formData || {}
) as Record<string, any>;
console.log("📦 [executeAfterSaveControl] savedRecord 구조:", Object.keys(savedRecord));
console.log("📦 [executeAfterSaveControl] actualFormData 추출:", Object.keys(formData));
console.log("📦 [executeAfterSaveControl] formData.sabun:", formData.sabun);
@@ -2924,8 +2926,7 @@ export class ButtonActionExecutor {
if (v2ListComponent) {
dataSourceId =
v2ListComponent.componentConfig.dataSource?.table ||
v2ListComponent.componentConfig.tableName;
v2ListComponent.componentConfig.dataSource?.table || v2ListComponent.componentConfig.tableName;
console.log("✨ V2List 자동 감지:", {
componentId: v2ListComponent.id,
tableName: dataSourceId,
@@ -3061,7 +3062,7 @@ export class ButtonActionExecutor {
// 🔧 수정: openModalWithData는 "신규 등록 + 연결 데이터 전달"용이므로
// editData가 아닌 splitPanelParentData로 전달해야 채번 등이 정상 작동함
const isPassDataMode = passSelectedData && selectedData.length > 0;
// 🔧 isEditMode 옵션이 명시적으로 true인 경우에만 수정 모드로 처리
const useAsEditData = config.isEditMode === true;