발주 상세
- {isEditMode && (
+
전체 {category.items.length}건
+ {isEditMode && selectedItems.size > 0 && (
<>
-
- {selectedItems.size}건 선택
-
+
/
+
{selectedItems.size}개 항목 선택됨
>
)}
diff --git a/src/components/business/construction/progress-billing/ProgressBillingDetailForm.tsx b/src/components/business/construction/progress-billing/ProgressBillingDetailForm.tsx
index f474601e..7775ecbf 100644
--- a/src/components/business/construction/progress-billing/ProgressBillingDetailForm.tsx
+++ b/src/components/business/construction/progress-billing/ProgressBillingDetailForm.tsx
@@ -75,7 +75,6 @@ export default function ProgressBillingDetailForm({
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
try {
// TODO: API 호출
- console.log('Save billing data:', formData);
await new Promise((resolve) => setTimeout(resolve, 500));
toast.success('저장되었습니다.');
router.push('/ko/construction/billing/progress-billing-management/' + billingId);
diff --git a/src/components/business/construction/progress-billing/actions.ts b/src/components/business/construction/progress-billing/actions.ts
index 92fe695c..0578fc0a 100644
--- a/src/components/business/construction/progress-billing/actions.ts
+++ b/src/components/business/construction/progress-billing/actions.ts
@@ -234,7 +234,6 @@ export async function saveProgressBilling(
await new Promise((resolve) => setTimeout(resolve, 500));
- console.log('Save progress billing:', { id, data });
return {
success: true,
@@ -266,7 +265,6 @@ export async function deleteProgressBilling(id: string): Promise<{
await new Promise((resolve) => setTimeout(resolve, 500));
- console.log('Delete progress billing:', id);
return {
success: true,
@@ -296,11 +294,9 @@ export async function updateProgressBillingStatus(
await new Promise((resolve) => setTimeout(resolve, 300));
- console.log('Update progress billing status:', { id, status });
// 기성청구완료 시 매출 자동 등록 로직
if (status === 'completed') {
- console.log('Auto-register sales for completed billing:', id);
// TODO: 매출 자동 등록 API 호출
}
diff --git a/src/components/business/construction/progress-billing/hooks/useProgressBillingDetailForm.ts b/src/components/business/construction/progress-billing/hooks/useProgressBillingDetailForm.ts
index d230fddf..cb334d00 100644
--- a/src/components/business/construction/progress-billing/hooks/useProgressBillingDetailForm.ts
+++ b/src/components/business/construction/progress-billing/hooks/useProgressBillingDetailForm.ts
@@ -90,7 +90,6 @@ export function useProgressBillingDetailForm({
setIsLoading(true);
try {
// TODO: API 호출
- console.log('Save billing data:', formData);
await new Promise((resolve) => setTimeout(resolve, 500));
setShowSaveDialog(false);
router.push('/construction/billing/progress-billing-management/' + billingId);
@@ -110,7 +109,6 @@ export function useProgressBillingDetailForm({
setIsLoading(true);
try {
// TODO: API 호출
- console.log('Delete billing:', billingId);
await new Promise((resolve) => setTimeout(resolve, 500));
setShowDeleteDialog(false);
router.push('/construction/billing/progress-billing-management');
diff --git a/src/components/business/construction/progress-billing/tables/PhotoTable.tsx b/src/components/business/construction/progress-billing/tables/PhotoTable.tsx
index 4a73b7d8..c51c959e 100644
--- a/src/components/business/construction/progress-billing/tables/PhotoTable.tsx
+++ b/src/components/business/construction/progress-billing/tables/PhotoTable.tsx
@@ -50,8 +50,14 @@ export function PhotoTable({
사진대지
- 총 {items.length}건{selectedItems.size > 0 && ', ' + selectedItems.size + '건 선택'}
+ 전체 {items.length}건
+ {selectedItems.size > 0 && (
+ <>
+ /
+ {selectedItems.size}개 항목 선택됨
+ >
+ )}
{isEditMode && selectedItems.size > 0 && (
{weekDays.map((day, i) => {
- const dateStr = formatDate(day, 'yyyy-MM-dd');
+ const dateStr = formatCalendarDate(day, 'yyyy-MM-dd');
const allDayEvents = eventsByDate.get(dateStr)?.allDay || [];
return (
{/* 요일별 셀 */}
{weekDays.map((day, i) => {
- const dateStr = formatDate(day, 'yyyy-MM-dd');
+ const dateStr = formatCalendarDate(day, 'yyyy-MM-dd');
const timedEvents = eventsByDate.get(dateStr)?.timed || [];
const slotEvents = timedEvents.filter((event) => {
if (!event.startTime) return false;
diff --git a/src/components/common/ScheduleCalendar/utils.ts b/src/components/common/ScheduleCalendar/utils.ts
index ca158d53..90638f30 100644
--- a/src/components/common/ScheduleCalendar/utils.ts
+++ b/src/components/common/ScheduleCalendar/utils.ts
@@ -93,7 +93,7 @@ export function isSameDate(date1: Date | null, date2: Date): boolean {
/**
* 날짜 포맷
*/
-export function formatDate(date: Date, formatStr: string = 'yyyy-MM-dd'): string {
+export function formatCalendarDate(date: Date, formatStr: string = 'yyyy-MM-dd'): string {
return format(date, formatStr, { locale: ko });
}
diff --git a/src/components/hr/AttendanceManagement/index.tsx b/src/components/hr/AttendanceManagement/index.tsx
index ef8e9670..d82a5190 100644
--- a/src/components/hr/AttendanceManagement/index.tsx
+++ b/src/components/hr/AttendanceManagement/index.tsx
@@ -348,7 +348,6 @@ export function AttendanceManagement() {
}, [attendanceDialogMode, selectedAttendance]);
const handleSubmitReason = useCallback((data: ReasonFormData) => {
- console.log('Submit reason:', data);
// 문서 작성 화면으로 이동
router.push(`/ko/hr/documents/new?type=${data.reasonType}`);
}, [router]);
diff --git a/src/components/hr/DepartmentManagement/DepartmentToolbar.tsx b/src/components/hr/DepartmentManagement/DepartmentToolbar.tsx
index adeda702..ee0d3e57 100644
--- a/src/components/hr/DepartmentManagement/DepartmentToolbar.tsx
+++ b/src/components/hr/DepartmentManagement/DepartmentToolbar.tsx
@@ -32,21 +32,28 @@ export function DepartmentToolbar({
{/* 선택 카운트 + 버튼 */}
- 총 {totalCount}건 {selectedCount > 0 && `| ${selectedCount}건 선택`}
+ 전체 {totalCount}건
+ {selectedCount > 0 && (
+ <>
+
/
+
{selectedCount}개 항목 선택됨
+ >
+ )}
+ {selectedCount > 0 && (
+
+
+ 선택삭제
+
+ )}
추가
-
-
- 삭제
-
);
diff --git a/src/components/hr/DepartmentManagement/DepartmentTreeItem.tsx b/src/components/hr/DepartmentManagement/DepartmentTreeItem.tsx
index 25c53336..8a29a07d 100644
--- a/src/components/hr/DepartmentManagement/DepartmentTreeItem.tsx
+++ b/src/components/hr/DepartmentManagement/DepartmentTreeItem.tsx
@@ -1,5 +1,6 @@
'use client';
+import { memo } from 'react';
import { Checkbox } from '@/components/ui/checkbox';
import { Button } from '@/components/ui/button';
import { ChevronRight, ChevronDown, Plus, Pencil, Trash2 } from 'lucide-react';
@@ -10,7 +11,7 @@ import type { DepartmentTreeItemProps } from './types';
* - 무제한 깊이 지원
* - depth에 따른 동적 들여쓰기
*/
-export function DepartmentTreeItem({
+export const DepartmentTreeItem = memo(function DepartmentTreeItem({
department,
depth,
expandedIds,
@@ -114,4 +115,4 @@ export function DepartmentTreeItem({
)}
>
);
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/components/hr/EmployeeManagement/index.tsx b/src/components/hr/EmployeeManagement/index.tsx
index 42ef7217..35388a6c 100644
--- a/src/components/hr/EmployeeManagement/index.tsx
+++ b/src/components/hr/EmployeeManagement/index.tsx
@@ -725,7 +725,6 @@ export function EmployeeManagement() {
open={userInviteOpen}
onOpenChange={setUserInviteOpen}
onInvite={(data) => {
- console.log('[EmployeeManagement] Invite user - API 미구현:', data);
setUserInviteOpen(false);
}}
/>
diff --git a/src/components/hr/SalaryManagement/index.tsx b/src/components/hr/SalaryManagement/index.tsx
index 1eb312e7..ca803df2 100644
--- a/src/components/hr/SalaryManagement/index.tsx
+++ b/src/components/hr/SalaryManagement/index.tsx
@@ -397,38 +397,39 @@ export function SalaryManagement() {
onEndDateChange: setEndDate,
},
- headerActions: ({ selectedItems: selected }) => (
-
- {/* 지급완료/지급예정 버튼 - 선택된 항목이 있을 때만 표시 */}
- {selected.size > 0 && (
- <>
-
- {isActionLoading ? (
-
- ) : (
-
- )}
- 지급완료
-
-
- {isActionLoading ? (
-
- ) : (
-
- )}
- 지급예정
-
- >
- )}
+ selectionActions: () => (
+ <>
+
+ {isActionLoading ? (
+
+ ) : (
+
+ )}
+ 지급완료
+
+
+ {isActionLoading ? (
+
+ ) : (
+
+ )}
+ 지급예정
+
+ >
+ ),
+ headerActions: () => (
+
{canExport && (
toast.info('엑셀 다운로드 기능은 준비 중입니다.')}>
diff --git a/src/components/hr/VacationManagement/index.tsx b/src/components/hr/VacationManagement/index.tsx
index 3bca6a1b..1a048609 100644
--- a/src/components/hr/VacationManagement/index.tsx
+++ b/src/components/hr/VacationManagement/index.tsx
@@ -596,40 +596,23 @@ export function VacationManagement() {
}
}, [mainTab, handleApproveClick, handleRejectClick]);
- // ===== 헤더 액션 (탭별 버튼들만 - DateRangeSelector와 검색창은 공통 옵션 사용) =====
- const headerActions = useCallback(({ selectedItems: selected }: { selectedItems: Set; onClearSelection?: () => void; onRefresh?: () => void }) => (
+ // ===== 헤더 액션 (탭별 버튼들만 - 선택 액션은 selectionActions로 분리) =====
+ const headerActions = useCallback(() => (
- {/* 탭별 액션 버튼 */}
{mainTab === 'grant' && (
setGrantDialogOpen(true)}>
부여등록
)}
-
{mainTab === 'request' && (
- <>
- {/* 버튼 순서: 승인 → 거절 → 휴가신청 (휴가신청 버튼 위치 고정) */}
- {selected.size > 0 && (
- <>
-
handleApproveClick(selected)}>
-
- 승인
-
-
handleRejectClick(selected)}>
-
- 거절
-
- >
- )}
-
setRequestDialogOpen(true)}>
-
- 휴가신청
-
- >
+
setRequestDialogOpen(true)}>
+
+ 휴가신청
+
)}
- ), [mainTab, handleApproveClick, handleRejectClick]);
+ ), [mainTab]);
// ===== filterConfig 기반 통합 필터 시스템 =====
const filterConfig: FilterFieldConfig[] = useMemo(() => [
@@ -729,6 +712,22 @@ export function VacationManagement() {
headerActions: headerActions,
+ selectionActions: ({ selectedItems: selected }) => {
+ if (mainTab !== 'request') return null;
+ return (
+ <>
+ handleApproveClick(selected)}>
+
+ 승인
+
+ handleRejectClick(selected)}>
+
+ 거절
+
+ >
+ );
+ },
+
renderTableRow: renderTableRow,
renderMobileCard: renderMobileCard,
@@ -827,6 +826,8 @@ export function VacationManagement() {
statCards,
filterConfig,
headerActions,
+ handleApproveClick,
+ handleRejectClick,
renderTableRow,
renderMobileCard,
grantDialogOpen,
diff --git a/src/components/items/DynamicItemForm/hooks/useFieldDetection.ts b/src/components/items/DynamicItemForm/hooks/useFieldDetection.ts
index 8c2ccb91..2d6c6c83 100644
--- a/src/components/items/DynamicItemForm/hooks/useFieldDetection.ts
+++ b/src/components/items/DynamicItemForm/hooks/useFieldDetection.ts
@@ -101,8 +101,7 @@ export function useFieldDetection({
// "구매 부품", "PURCHASED", "구매부품" 등 다양한 형태 지원
const isPurchased = currentPartType.includes('구매') || currentPartType.toUpperCase() === 'PURCHASED';
- // console.log('[useFieldDetection] 부품 유형 감지:', { partTypeFieldKey: foundPartTypeKey, currentPartType, isBending, isAssembly, isPurchased });
-
+ //
return {
partTypeFieldKey: foundPartTypeKey,
selectedPartType: currentPartType,
@@ -133,8 +132,7 @@ export function useFieldDetection({
fieldKey.includes('부품구성');
if (isCheckbox && isBomRelated) {
- // console.log('[useFieldDetection] BOM 체크박스 필드 발견:', { fieldKey, fieldName });
- return field.field_key || `field_${field.id}`;
+ // return field.field_key || `field_${field.id}`;
}
}
}
@@ -154,12 +152,10 @@ export function useFieldDetection({
fieldKey.includes('부품구성');
if (isCheckbox && isBomRelated) {
- // console.log('[useFieldDetection] BOM 체크박스 필드 발견 (직접필드):', { fieldKey, fieldName });
- return field.field_key || `field_${field.id}`;
+ // return field.field_key || `field_${field.id}`;
}
}
- // console.log('[useFieldDetection] BOM 체크박스 필드를 찾지 못함');
return '';
}, [structure]);
diff --git a/src/components/items/DynamicItemForm/hooks/useFileHandling.ts b/src/components/items/DynamicItemForm/hooks/useFileHandling.ts
index a6a5ef6a..2f9526e5 100644
--- a/src/components/items/DynamicItemForm/hooks/useFileHandling.ts
+++ b/src/components/items/DynamicItemForm/hooks/useFileHandling.ts
@@ -115,7 +115,6 @@ export function useFileHandling({
if (typeof filesRaw === 'string') {
try {
filesRaw = JSON.parse(filesRaw);
- console.log('[useFileHandling] files JSON 문자열 파싱 완료');
} catch (e) {
console.error('[useFileHandling] files JSON 파싱 실패:', e);
filesRaw = undefined;
@@ -125,12 +124,6 @@ export function useFileHandling({
const files = filesRaw as FilesObject | undefined;
// 2025-12-15: 파일 로드 디버깅
- console.log('[useFileHandling] 파일 로드 시작');
- console.log('[useFileHandling] initialData.files (raw):', initialData.files);
- console.log('[useFileHandling] filesRaw 타입:', typeof filesRaw);
- console.log('[useFileHandling] files 변수:', files);
- console.log('[useFileHandling] specification_file:', files?.specification_file);
- console.log('[useFileHandling] certification_file:', files?.certification_file);
// 전개도 파일 (배열의 마지막 파일 = 최신 파일을 가져옴)
const bendingFileArr = files?.bending_diagram;
@@ -138,12 +131,9 @@ export function useFileHandling({
? bendingFileArr[bendingFileArr.length - 1]
: undefined;
if (bendingFile) {
- console.log('[useFileHandling] bendingFile 전체 객체:', bendingFile);
- console.log('[useFileHandling] bendingFile 키 목록:', Object.keys(bendingFile));
setExistingBendingDiagram(bendingFile.file_path);
// API에서 id 또는 file_id로 올 수 있음
const bendingFileId = bendingFile.id || bendingFile.file_id;
- console.log('[useFileHandling] bendingFile ID 추출:', { id: bendingFile.id, file_id: bendingFile.file_id, final: bendingFileId });
setExistingBendingDiagramFileId(bendingFileId as number);
} else if (initialData.bending_diagram) {
setExistingBendingDiagram(initialData.bending_diagram as string);
@@ -154,14 +144,11 @@ export function useFileHandling({
const specFile = specFileArr && specFileArr.length > 0
? specFileArr[specFileArr.length - 1]
: undefined;
- console.log('[useFileHandling] specFile 전체 객체:', specFile);
- console.log('[useFileHandling] specFile 키 목록:', specFile ? Object.keys(specFile) : 'undefined');
if (specFile?.file_path) {
setExistingSpecificationFile(specFile.file_path);
setExistingSpecificationFileName(specFile.file_name || '시방서');
// API에서 id 또는 file_id로 올 수 있음
const specFileId = specFile.id || specFile.file_id;
- console.log('[useFileHandling] specFile ID 추출:', { id: specFile.id, file_id: specFile.file_id, final: specFileId });
setExistingSpecificationFileId(specFileId as number || null);
} else {
// 파일이 없으면 상태 초기화 (이전 값 제거)
@@ -175,14 +162,11 @@ export function useFileHandling({
const certFile = certFileArr && certFileArr.length > 0
? certFileArr[certFileArr.length - 1]
: undefined;
- console.log('[useFileHandling] certFile 전체 객체:', certFile);
- console.log('[useFileHandling] certFile 키 목록:', certFile ? Object.keys(certFile) : 'undefined');
if (certFile?.file_path) {
setExistingCertificationFile(certFile.file_path);
setExistingCertificationFileName(certFile.file_name || '인정서');
// API에서 id 또는 file_id로 올 수 있음
const certFileId = certFile.id || certFile.file_id;
- console.log('[useFileHandling] certFile ID 추출:', { id: certFile.id, file_id: certFile.file_id, final: certFileId });
setExistingCertificationFileId(certFileId as number || null);
} else {
// 파일이 없으면 상태 초기화 (이전 값 제거)
@@ -244,13 +228,6 @@ export function useFileHandling({
onBendingDiagramDeleted?: () => void;
}
) => {
- console.log('[useFileHandling] handleDeleteFile 호출:', {
- fileType,
- propItemId,
- existingBendingDiagramFileId,
- existingSpecificationFileId,
- existingCertificationFileId,
- });
if (!propItemId) {
console.error('[useFileHandling] propItemId가 없습니다');
@@ -267,7 +244,6 @@ export function useFileHandling({
fileId = existingCertificationFileId;
}
- console.log('[useFileHandling] 삭제할 파일 ID:', fileId);
if (!fileId) {
console.error('[useFileHandling] 파일 ID를 찾을 수 없습니다:', fileType);
diff --git a/src/components/items/DynamicItemForm/hooks/useFormStructure.ts b/src/components/items/DynamicItemForm/hooks/useFormStructure.ts
index 47d3c662..1a2d47e1 100644
--- a/src/components/items/DynamicItemForm/hooks/useFormStructure.ts
+++ b/src/components/items/DynamicItemForm/hooks/useFormStructure.ts
@@ -34,12 +34,10 @@ export function useFormStructure(
const initData = await itemMasterApi.init();
// 단위 옵션 저장 (SimpleUnitOption 형식으로 변환)
- console.log('[useFormStructure] API initData.unitOptions:', initData.unitOptions);
const simpleUnitOptions: SimpleUnitOption[] = (initData.unitOptions || []).map((u) => ({
label: u.unit_name,
value: u.unit_code,
}));
- console.log('[useFormStructure] Processed unitOptions:', simpleUnitOptions.length, 'items');
setUnitOptions(simpleUnitOptions);
// 2. 품목 유형에 해당하는 페이지 찾기
diff --git a/src/components/items/DynamicItemForm/hooks/usePartTypeHandling.ts b/src/components/items/DynamicItemForm/hooks/usePartTypeHandling.ts
index 975087a4..47ea55ff 100644
--- a/src/components/items/DynamicItemForm/hooks/usePartTypeHandling.ts
+++ b/src/components/items/DynamicItemForm/hooks/usePartTypeHandling.ts
@@ -74,8 +74,7 @@ export function usePartTypeHandling({
// 이전 값이 있고, 현재 값과 다른 경우에만 초기화
if (prevPartType && prevPartType !== currentPartType) {
- // console.log('[usePartTypeHandling] 부품 유형 변경 감지:', prevPartType, '→', currentPartType);
-
+ //
// setTimeout으로 다음 틱에서 초기화 실행
// → 부품 유형 Select 값 변경이 먼저 완료된 후 초기화
setTimeout(() => {
@@ -121,8 +120,7 @@ export function usePartTypeHandling({
// 중복 제거 후 초기화
const uniqueFields = [...new Set(fieldsToReset)];
- // console.log('[usePartTypeHandling] 초기화할 필드:', uniqueFields);
-
+ //
uniqueFields.forEach((fieldKey) => {
setFieldValue(fieldKey, '');
});
@@ -152,11 +150,6 @@ export function usePartTypeHandling({
}, 0);
const sumString = totalSum.toString();
- console.log('[usePartTypeHandling] bendingDetails 폭 합계 → formData 동기화:', {
- widthSumKey: bendingFieldKeys.widthSum,
- totalSum,
- bendingDetailsCount: bendingDetails.length,
- });
setFieldValue(bendingFieldKeys.widthSum, sumString);
bendingWidthSumSyncedRef.current = true;
@@ -175,14 +168,12 @@ export function usePartTypeHandling({
// 품목명이 변경되었고, 이전 값이 있었을 때만 종류 필드 초기화
if (prevItemNameValue && prevItemNameValue !== currentItemNameValue) {
- // console.log('[usePartTypeHandling] 품목명 변경 감지:', prevItemNameValue, '→', currentItemNameValue);
-
+ //
// 모든 종류 필드 값 초기화
allCategoryKeysWithIds.forEach(({ key }) => {
const currentVal = (formData[key] as string) || '';
if (currentVal) {
- // console.log('[usePartTypeHandling] 종류 필드 초기화:', key);
- setFieldValue(key, '');
+ // setFieldValue(key, '');
}
});
}
diff --git a/src/components/items/DynamicItemForm/index.tsx b/src/components/items/DynamicItemForm/index.tsx
index c6a7ce5f..a1918951 100644
--- a/src/components/items/DynamicItemForm/index.tsx
+++ b/src/components/items/DynamicItemForm/index.tsx
@@ -129,7 +129,6 @@ export default function DynamicItemForm({
useEffect(() => {
if (mode === 'edit' && initialBomLines && initialBomLines.length > 0) {
setBomLines(initialBomLines);
- console.log('[DynamicItemForm] initialBomLines로 BOM 데이터 로드:', initialBomLines.length, '건');
}
}, [mode, initialBomLines]);
@@ -159,7 +158,6 @@ export default function DynamicItemForm({
.map((item: { code?: string; item_code?: string }) => item.code || item.item_code || '')
.filter((code: string) => code);
setExistingItemCodes(codes);
- // console.log('[DynamicItemForm] PT 기존 품목코드 로드:', codes.length, '개');
}
} catch (err) {
console.error('[DynamicItemForm] PT 품목코드 조회 실패:', err);
@@ -213,18 +211,9 @@ export default function DynamicItemForm({
const [isEditDataMapped, setIsEditDataMapped] = useState(false);
useEffect(() => {
- console.log('[DynamicItemForm] Edit useEffect 체크:', {
- mode,
- hasStructure: !!structure,
- hasInitialData: !!initialData,
- isEditDataMapped,
- structureSections: structure?.sections?.length,
- });
if (mode !== 'edit' || !structure || !initialData || isEditDataMapped) return;
- console.log('[DynamicItemForm] Edit mode: initialData 직접 로드 (field_key 통일됨)');
- console.log('[DynamicItemForm] initialData:', initialData);
// structure의 field_key들 확인
const fieldKeys: string[] = [];
@@ -233,8 +222,6 @@ export default function DynamicItemForm({
fieldKeys.push(f.field.field_key || `field_${f.field.id}`);
});
});
- console.log('[DynamicItemForm] structure field_keys:', fieldKeys);
- console.log('[DynamicItemForm] initialData keys:', Object.keys(initialData));
// field_key가 통일되었으므로 initialData를 그대로 사용
// 기존 레거시 데이터(98_unit 형식)도 그대로 동작
@@ -348,7 +335,6 @@ export default function DynamicItemForm({
// PT (절곡/조립) 전개도 이미지 업로드
if (selectedItemType === 'PT' && (isBendingPart || isAssemblyPart) && bendingDiagramFile) {
try {
- console.log('[DynamicItemForm] 전개도 파일 업로드 시작:', bendingDiagramFile.name);
await uploadItemFile(itemId, bendingDiagramFile, 'bending_diagram', {
fieldKey: 'bending_diagram',
// 수정 모드: 기존 파일 ID가 있으면 덮어쓰기, 없으면 새 파일 등록
@@ -359,7 +345,6 @@ export default function DynamicItemForm({
type: d.shaded ? 'shaded' : 'normal',
})) : undefined,
});
- console.log('[DynamicItemForm] 전개도 파일 업로드 성공');
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('[DynamicItemForm] 전개도 파일 업로드 실패:', error);
@@ -370,13 +355,11 @@ export default function DynamicItemForm({
// FG (제품) 시방서 업로드
if (selectedItemType === 'FG' && specificationFile) {
try {
- console.log('[DynamicItemForm] 시방서 파일 업로드 시작:', specificationFile.name);
await uploadItemFile(itemId, specificationFile, 'specification', {
fieldKey: 'specification_file',
// 수정 모드: 기존 파일 ID가 있으면 덮어쓰기, 없으면 새 파일 등록
fileId: existingSpecificationFileId ?? undefined,
});
- console.log('[DynamicItemForm] 시방서 파일 업로드 성공');
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('[DynamicItemForm] 시방서 파일 업로드 실패:', error);
@@ -387,7 +370,6 @@ export default function DynamicItemForm({
// FG (제품) 인정서 업로드
if (selectedItemType === 'FG' && certificationFile) {
try {
- console.log('[DynamicItemForm] 인정서 파일 업로드 시작:', certificationFile.name);
// formData에서 인정서 관련 필드 추출
const certNumber = Object.entries(formData).find(([key]) =>
key.includes('certification_number') || key.includes('인정번호')
@@ -407,7 +389,6 @@ export default function DynamicItemForm({
certificationStartDate: certStartDate,
certificationEndDate: certEndDate,
});
- console.log('[DynamicItemForm] 인정서 파일 업로드 성공');
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('[DynamicItemForm] 인정서 파일 업로드 실패:', error);
@@ -457,7 +438,6 @@ export default function DynamicItemForm({
// 2025-12-09: field_key 통일로 변환 로직 제거
// formData의 field_key가 백엔드 필드명과 일치하므로 직접 사용
- console.log('[DynamicItemForm] 저장 시 formData:', formData);
// is_active 필드만 boolean 변환 (드롭다운 값 → boolean)
const convertedData: Record = {};
@@ -509,8 +489,7 @@ export default function DynamicItemForm({
finalSpec = convertedData.spec;
}
- // console.log('[DynamicItemForm] 품목명/규격 결정:', { finalName, finalSpec });
-
+ //
// 품목코드 결정
// 2025-12-11: 수정 모드에서는 기존 코드 유지 (자동생성으로 코드가 변경되는 버그 수정)
// 생성 모드에서만 자동생성 코드 사용
@@ -569,20 +548,17 @@ export default function DynamicItemForm({
} : {}),
} as DynamicFormData;
- // console.log('[DynamicItemForm] 제출 데이터:', submitData);
-
+ //
// 2025-12-11: 품목코드 중복 체크 (조립/절곡 부품만 해당)
// PT-조립부품, PT-절곡부품은 품목코드가 자동생성되므로 중복 체크 필요
const needsDuplicateCheck = selectedItemType === 'PT' && (isAssemblyPart || isBendingPart) && finalCode;
if (needsDuplicateCheck) {
- console.log('[DynamicItemForm] 품목코드 중복 체크:', finalCode);
// 수정 모드에서는 자기 자신 제외 (propItemId)
const excludeId = mode === 'edit' ? propItemId : undefined;
const duplicateResult = await checkItemCodeDuplicate(finalCode, excludeId);
- console.log('[DynamicItemForm] 중복 체크 결과:', duplicateResult);
if (duplicateResult.isDuplicate) {
// 중복 발견 → 다이얼로그 표시
@@ -983,8 +959,7 @@ export default function DynamicItemForm({
const isBomRequired = bomValue === true || bomValue === 'true' || bomValue === '1' || bomValue === 1;
// 디버깅 로그
- // console.log('[DynamicItemForm] BOM 체크 디버깅:', { bomRequiredFieldKey, bomValue, isBomRequired });
-
+ //
if (!isBomRequired) return null;
return (
@@ -1021,7 +996,6 @@ export default function DynamicItemForm({
const blob = new Blob([uint8Array], { type: mimeType });
const file = new File([blob], `bending_diagram_${Date.now()}.png`, { type: mimeType });
setBendingDiagramFile(file);
- console.log('[DynamicItemForm] 드로잉 캔버스 → File 변환 성공:', file.name);
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('[DynamicItemForm] 드로잉 캔버스 → File 변환 실패:', error);
diff --git a/src/components/items/ItemDetailEdit.tsx b/src/components/items/ItemDetailEdit.tsx
index 91a048bf..1b386aac 100644
--- a/src/components/items/ItemDetailEdit.tsx
+++ b/src/components/items/ItemDetailEdit.tsx
@@ -120,7 +120,6 @@ function mapApiResponseToFormData(data: ItemApiResponse): DynamicFormData {
formData['is_active'] = true;
}
- console.log('[ItemDetailEdit] mapApiResponseToFormData 결과:', formData);
return formData;
}
@@ -166,7 +165,6 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
queryParams.append('include_bom', 'true');
}
- console.log('[ItemDetailEdit] Fetching:', { urlItemId, urlItemType, isMaterial });
const queryString = queryParams.toString();
const response = await fetch(`/api/proxy/items/${urlItemId}${queryString ? `?${queryString}` : ''}`);
@@ -185,15 +183,6 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
if (result.success && result.data) {
const apiData = result.data as ItemApiResponse;
- console.log('========== [ItemDetailEdit] API 원본 데이터 (백엔드 응답) ==========');
- console.log('id:', apiData.id);
- console.log('specification:', apiData.specification);
- console.log('unit:', apiData.unit);
- console.log('is_active:', apiData.is_active);
- console.log('files:', (apiData as any).files); // 파일 데이터 확인
- console.log('전체:', apiData);
- console.log('==============================================================');
-
// ID, 품목 유형 저장
// Product: product_type, Material: material_type 또는 type_code
setItemId(apiData.id);
@@ -202,13 +191,6 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
// 폼 데이터로 변환
const formData = mapApiResponseToFormData(apiData);
- console.log('========== [ItemDetailEdit] 폼에 전달되는 initialData ==========');
- console.log('specification:', formData['specification']);
- console.log('unit:', formData['unit']);
- console.log('is_active:', formData['is_active']);
- console.log('files:', formData['files']); // 파일 데이터 확인
- console.log('전체:', formData);
- console.log('==========================================================');
setInitialData(formData);
// BOM 데이터 별도 API 호출 (expandBomItems로 품목 정보 포함)
@@ -238,7 +220,6 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
}));
setInitialBomLines(mappedBomLines);
- console.log('[ItemDetailEdit] BOM 데이터 로드 (expanded):', mappedBomLines.length, '건', mappedBomLines);
}
} catch (bomErr) {
console.error('[ItemDetailEdit] BOM 조회 실패:', bomErr);
@@ -328,14 +309,6 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
}
// API 호출
- console.log('========== [ItemDetailEdit] 수정 요청 데이터 ==========');
- console.log('URL:', updateUrl);
- console.log('Method:', method);
- console.log('specification:', submitData.specification);
- console.log('unit:', submitData.unit);
- console.log('is_active:', submitData.is_active);
- console.log('전체:', submitData);
- console.log('=================================================');
const response = await fetch(updateUrl, {
method,
diff --git a/src/components/items/ItemDetailView.tsx b/src/components/items/ItemDetailView.tsx
index 274e391e..2ed51fea 100644
--- a/src/components/items/ItemDetailView.tsx
+++ b/src/components/items/ItemDetailView.tsx
@@ -26,9 +26,6 @@ function mapApiResponseToItemMaster(data: Record): ItemMaster {
// details 객체 추출 (PT 부품의 상세 정보가 여기에 저장됨)
const details = (data.details || {}) as Record;
- console.log('[mapApiResponseToItemMaster] data.details:', data.details);
- console.log('[mapApiResponseToItemMaster] details.part_type:', details.part_type);
- console.log('[mapApiResponseToItemMaster] details.bending_details:', details.bending_details);
return {
id: String(data.id || ''),
@@ -169,7 +166,6 @@ export function ItemDetailView({ itemCode, itemType, itemId }: ItemDetailViewPro
try {
setIsLoading(true);
- console.log('[ItemDetailView] Fetching item:', { itemCode, itemType, itemId });
// 모든 품목: GET /api/proxy/items/{id} (id 기반 통일)
if (!itemId) {
@@ -185,7 +181,6 @@ export function ItemDetailView({ itemCode, itemType, itemId }: ItemDetailViewPro
queryParams.append('include_bom', 'true');
}
- console.log('[ItemDetailView] Fetching:', { itemId, itemType, isMaterial });
const queryString = queryParams.toString();
const response = await fetch(`/api/proxy/items/${itemId}${queryString ? `?${queryString}` : ''}`);
@@ -201,7 +196,6 @@ export function ItemDetailView({ itemCode, itemType, itemId }: ItemDetailViewPro
}
const result = await response.json();
- console.log('[ItemDetailView] API Response:', result);
if (result.success && result.data) {
let mappedItem = mapApiResponseToItemMaster(result.data);
@@ -229,7 +223,6 @@ export function ItemDetailView({ itemCode, itemType, itemId }: ItemDetailViewPro
isBending: Boolean(bomItem.is_bending ?? false),
})),
};
- console.log('[ItemDetailView] BOM 데이터 로드 (expanded):', mappedItem.bom?.length, '건');
}
} catch (bomErr) {
console.error('[ItemDetailView] BOM 조회 실패:', bomErr);
diff --git a/src/components/items/ItemListClient.tsx b/src/components/items/ItemListClient.tsx
index c61e4fe9..be9e0982 100644
--- a/src/components/items/ItemListClient.tsx
+++ b/src/components/items/ItemListClient.tsx
@@ -154,13 +154,11 @@ export default function ItemListClient() {
if (!itemToDelete) return;
try {
- console.log('[Delete] 삭제 요청:', itemToDelete);
// 2025-12-15: 백엔드 동적 테이블 라우팅 - 모든 품목이 /items 엔드포인트 사용
// /products/materials 라우트 삭제됨
const deleteUrl = `/api/proxy/items/${itemToDelete.id}?item_type=${itemToDelete.itemType}`;
- console.log('[Delete] URL:', deleteUrl, '(itemType:', itemToDelete.itemType, ')');
const response = await fetch(deleteUrl, {
method: 'DELETE',
@@ -175,7 +173,6 @@ export default function ItemListClient() {
}
const result = await response.json();
- console.log('[Delete] 응답:', { status: response.status, result });
if (result.success) {
refresh();
@@ -330,7 +327,6 @@ export default function ItemListClient() {
// TODO: 실제 API 호출로 데이터 저장
// 지금은 파싱 결과만 확인
- console.log('[Excel Upload] 파싱 결과:', result.data);
alert(`${result.data.length}건의 데이터가 파싱되었습니다.\n(실제 등록 기능은 추후 구현 예정)`);
} catch (error) {
diff --git a/src/components/items/ItemMasterDataManagement/components/DraggableField.tsx b/src/components/items/ItemMasterDataManagement/components/DraggableField.tsx
index 7b706020..c96e9fb1 100644
--- a/src/components/items/ItemMasterDataManagement/components/DraggableField.tsx
+++ b/src/components/items/ItemMasterDataManagement/components/DraggableField.tsx
@@ -55,7 +55,6 @@ export function DraggableField({ field, index, moveField, onDelete, onEdit }: Dr
const data = JSON.parse(e.dataTransfer.getData('application/json'));
// 2025-12-03: 타입 체크 - 필드 드래그만 처리
if (data.type !== 'field') {
- console.log('[DraggableField] 필드 드래그가 아님, 무시:', data);
return;
}
if (data.id !== field.id) {
diff --git a/src/components/items/ItemMasterDataManagement/dialogs/FieldDialog.tsx b/src/components/items/ItemMasterDataManagement/dialogs/FieldDialog.tsx
index b66eaf8c..d03a2f6d 100644
--- a/src/components/items/ItemMasterDataManagement/dialogs/FieldDialog.tsx
+++ b/src/components/items/ItemMasterDataManagement/dialogs/FieldDialog.tsx
@@ -462,25 +462,12 @@ export function FieldDialog({
취소
{
- console.log('[FieldDialog] 🔵 저장 버튼 클릭!', {
- fieldInputMode,
- editingFieldId,
- selectedMasterFieldId,
- newFieldName,
- newFieldKey,
- isNameEmpty,
- isKeyEmpty,
- isKeyInvalid,
- });
setIsSubmitted(true);
// 2025-11-28: field_key validation 추가
const shouldValidate = isCustomMode || editingFieldId;
- console.log('[FieldDialog] 🔵 shouldValidate:', shouldValidate);
if (shouldValidate && (isNameEmpty || isKeyEmpty || isKeyInvalid)) {
- console.log('[FieldDialog] ❌ 유효성 검사 실패로 return');
return;
}
- console.log('[FieldDialog] ✅ handleAddField 호출');
await handleAddField();
setIsSubmitted(false);
}}>저장
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts
index 0da17cb2..49100312 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useDeleteManagement.ts
@@ -47,7 +47,6 @@ export function useDeleteManagement({ itemPages }: UseDeleteManagementProps): Us
const sectionIds = pageToDelete?.sections.map(s => s.id) || [];
const fieldIds = pageToDelete?.sections.flatMap(s => s.fields?.map(f => f.id) || []) || [];
deleteItemPage(pageId);
- console.log('페이지 삭제 완료:', { pageId, removedSections: sectionIds.length, removedFields: fieldIds.length });
}, [itemPages, deleteItemPage]);
// 섹션 삭제 핸들러
@@ -56,14 +55,12 @@ export function useDeleteManagement({ itemPages }: UseDeleteManagementProps): Us
const sectionToDelete = page?.sections.find(s => s.id === sectionId);
const fieldIds = sectionToDelete?.fields?.map(f => f.id) || [];
deleteSection(Number(sectionId));
- console.log('섹션 삭제 완료:', { sectionId, removedFields: fieldIds.length });
}, [itemPages, deleteSection]);
// 필드 연결 해제 핸들러
const handleUnlinkField = useCallback(async (_pageId: string, sectionId: string, fieldId: string) => {
try {
await unlinkFieldFromSection(Number(sectionId), Number(fieldId));
- console.log('필드 연결 해제 완료:', fieldId);
} catch (error) {
if (isNextRedirectError(error)) throw error;
console.error('필드 연결 해제 실패:', error);
@@ -105,7 +102,6 @@ export function useDeleteManagement({ itemPages }: UseDeleteManagementProps): Us
setAttributeSubTabs([]);
- console.log('🗑️ 모든 품목기준관리 데이터가 초기화되었습니다');
toast.success('✅ 모든 데이터가 초기화되었습니다!\n계층구조, 섹션, 항목, 속성이 모두 삭제되었습니다.');
setTimeout(() => {
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useFieldManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useFieldManagement.ts
index 04512444..42d8c9d3 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useFieldManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useFieldManagement.ts
@@ -153,22 +153,8 @@ export function useFieldManagement(): UseFieldManagementReturn {
// 필드 추가 (2025-11-27: async/await 추가 - 다른 탭 실시간 동기화)
const handleAddField = async (selectedPage: ItemPage | undefined) => {
- console.log('[useFieldManagement] 🟢 handleAddField 시작!', {
- selectedPage: selectedPage?.id,
- selectedSectionForField,
- newFieldName,
- newFieldKey,
- fieldInputMode,
- selectedMasterFieldId,
- });
if (!selectedPage || !selectedSectionForField || !newFieldName.trim() || !newFieldKey.trim()) {
- console.log('[useFieldManagement] ❌ 필수값 누락으로 return', {
- selectedPage: !!selectedPage,
- selectedSectionForField,
- newFieldName: newFieldName.trim(),
- newFieldKey: newFieldKey.trim(),
- });
toast.error('모든 필수 항목을 입력해주세요');
return;
}
@@ -221,7 +207,6 @@ export function useFieldManagement(): UseFieldManagementReturn {
try {
if (editingFieldId) {
- console.log('Updating field:', { pageId: selectedPage.id, sectionId: selectedSectionForField, fieldId: editingFieldId, fieldName: newField.field_name });
await updateField(Number(editingFieldId), newField);
// 항목관리 탭의 마스터 항목도 업데이트 (동일한 fieldKey가 있으면)
@@ -238,7 +223,6 @@ export function useFieldManagement(): UseFieldManagementReturn {
toast.success('항목이 섹션에 수정되었습니다!');
} else {
- console.log('Adding field to section:', { pageId: selectedPage.id, sectionId: selectedSectionForField, fieldName: newField.field_name });
// 섹션에 항목 추가 (await로 완료 대기)
// 2025-11-27: addItemMasterField 호출 제거 - 중복 필드 생성 방지
@@ -256,8 +240,6 @@ export function useFieldManagement(): UseFieldManagementReturn {
// 422 ValidationException 상세 메시지 처리 (field_key 중복/예약어, field_name 중복 등)
if (error instanceof ApiError) {
- console.log('🔍 ApiError.errors:', error.errors); // 디버깅용
-
// errors 객체에서 첫 번째 에러 메시지 추출 → AlertDialog로 표시
if (error.errors && Object.keys(error.errors).length > 0) {
const firstKey = Object.keys(error.errors)[0];
@@ -306,7 +288,6 @@ export function useFieldManagement(): UseFieldManagementReturn {
// 필드 삭제
const handleDeleteField = (pageId: string, sectionId: string, fieldId: string) => {
deleteField(Number(fieldId));
- console.log('필드 삭제 완료:', fieldId);
};
// 폼 초기화
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts b/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts
index 5a2c1d06..5ff96e8d 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useInitialDataLoading.ts
@@ -62,7 +62,6 @@ export function useInitialDataLoading({
// Context와 병행 운영 - 점진적 마이그레이션
try {
await initFromApi();
- console.log('✅ [Zustand] Store initialized');
} catch (zustandError) {
// Zustand 초기화 실패해도 Context로 fallback
console.warn('⚠️ [Zustand] Init failed, falling back to Context:', zustandError);
@@ -78,7 +77,6 @@ export function useInitialDataLoading({
if (data.sections && data.sections.length > 0) {
const transformedSections = transformSectionsResponse(data.sections);
loadIndependentSections(transformedSections);
- console.log('✅ 독립 섹션 로드:', transformedSections.length);
}
// 3. 섹션 템플릿 로드
@@ -106,10 +104,6 @@ export function useInitialDataLoading({
loadItemMasterFields(transformedFields as any);
loadIndependentFields(independentOnlyFields);
- console.log('✅ 필드 로드:', {
- total: transformedFields.length,
- independent: independentOnlyFields.length,
- });
}
// 5. 커스텀 탭 로드
@@ -124,13 +118,6 @@ export function useInitialDataLoading({
setUnitOptions(transformedUnits);
}
- console.log('✅ Initial data loaded:', {
- pages: data.pages?.length || 0,
- sections: data.sections?.length || 0,
- fields: data.fields?.length || 0,
- customTabs: data.customTabs?.length || 0,
- unitOptions: data.unitOptions?.length || 0,
- });
} catch (err) {
if (err instanceof ApiError && err.errors) {
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useMasterFieldManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useMasterFieldManagement.ts
index 46d67320..53e23ca7 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useMasterFieldManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useMasterFieldManagement.ts
@@ -127,8 +127,6 @@ export function useMasterFieldManagement(): UseMasterFieldManagementReturn {
// 422 ValidationException 상세 메시지 처리 (field_key 중복/예약어) → AlertDialog로 표시
if (error instanceof ApiError) {
- console.log('🔍 ApiError.errors:', error.errors); // 디버깅용
-
// errors 객체에서 첫 번째 에러 메시지 추출
if (error.errors && Object.keys(error.errors).length > 0) {
const firstKey = Object.keys(error.errors)[0];
@@ -202,8 +200,6 @@ export function useMasterFieldManagement(): UseMasterFieldManagementReturn {
// 422 ValidationException 상세 메시지 처리 (field_key 중복/예약어, field_name 중복 등) → AlertDialog로 표시
if (error instanceof ApiError) {
- console.log('🔍 ApiError.errors:', error.errors); // 디버깅용
-
// errors 객체에서 첫 번째 에러 메시지 추출
if (error.errors && Object.keys(error.errors).length > 0) {
const firstKey = Object.keys(error.errors)[0];
diff --git a/src/components/items/ItemMasterDataManagement/hooks/usePageManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/usePageManagement.ts
index d14b12e0..7a00085f 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/usePageManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/usePageManagement.ts
@@ -80,7 +80,6 @@ export function usePageManagement(): UsePageManagementReturn {
migrationDoneRef.current.add(page.id);
});
- console.log(`절대경로가 자동으로 생성되었습니다 (${pagesToMigrate.length}개 페이지)`);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [itemPages.length]); // itemPages 길이가 변경될 때만 체크
@@ -180,11 +179,6 @@ export function usePageManagement(): UsePageManagementReturn {
setSelectedPageId(remainingPages[0]?.id || null);
}
- console.log('페이지 삭제 완료:', {
- pageId,
- sectionsMovedToIndependent: sectionCount,
- fieldsPreserved: fieldCount
- });
};
// 페이지명 수정
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useSectionManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useSectionManagement.ts
index 24a22052..3d62232a 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useSectionManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useSectionManagement.ts
@@ -85,13 +85,6 @@ export function useSectionManagement(): UseSectionManagementReturn {
bom_items: sectionType === 'BOM' ? [] : undefined
};
- console.log('Adding section to page:', {
- pageId: selectedPage.id,
- page_name: selectedPage.page_name,
- sectionTitle: newSection.title,
- sectionType: newSection.section_type,
- currentSectionCount: selectedPage.sections.length,
- });
try {
// 페이지에 섹션 추가 (API 호출)
@@ -99,9 +92,6 @@ export function useSectionManagement(): UseSectionManagementReturn {
// 별도의 addSectionTemplate 호출 불필요 (자동 동기화)
await addSectionToPage(selectedPage.id, newSection);
- console.log('Section added to page:', {
- sectionTitle: newSection.title
- });
resetSectionForm();
toast.success(`${newSectionType === 'bom' ? 'BOM' : '일반'} 섹션이 추가되었습니다!`);
@@ -127,12 +117,6 @@ export function useSectionManagement(): UseSectionManagementReturn {
return;
}
- console.log('Linking existing section to page:', {
- sectionId: template.id,
- sectionName: template.template_name,
- pageId: selectedPage.id,
- orderNo: selectedPage.sections.length + 1,
- });
try {
// 기존 섹션을 페이지에 연결 (entity_relationships에 레코드 추가)
@@ -176,7 +160,6 @@ export function useSectionManagement(): UseSectionManagementReturn {
const handleUnlinkSection = async (pageId: number, sectionId: number) => {
try {
await unlinkSectionFromPage(pageId, sectionId);
- console.log('섹션 연결 해제 완료:', { pageId, sectionId });
toast.success('섹션 연결이 해제되었습니다');
} catch (error) {
if (isNextRedirectError(error)) throw error;
@@ -193,10 +176,6 @@ export function useSectionManagement(): UseSectionManagementReturn {
try {
await deleteSection(sectionId);
- console.log('섹션 삭제 완료:', {
- sectionId,
- removedFields: fieldIds.length
- });
toast.success('섹션이 삭제되었습니다!');
} catch (error) {
if (isNextRedirectError(error)) throw error;
diff --git a/src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts b/src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts
index 843b1935..f8deef97 100644
--- a/src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts
+++ b/src/components/items/ItemMasterDataManagement/hooks/useTemplateManagement.ts
@@ -174,7 +174,6 @@ export function useTemplateManagement(): UseTemplateManagementReturn {
is_default: false,
};
- console.log('Adding independent section (from section tab):', newSectionData);
try {
await createIndependentSection(newSectionData);
@@ -211,7 +210,6 @@ export function useTemplateManagement(): UseTemplateManagementReturn {
section_type: (newSectionTemplateType === 'bom' ? 'BOM' : 'BASIC') as 'BASIC' | 'BOM' | 'CUSTOM'
};
- console.log('Updating section (from template handler):', { id: editingSectionTemplateId, updateData });
try {
// updateSection 호출 (템플릿이 아닌 실제 섹션 API)
await updateSection(editingSectionTemplateId, updateData);
@@ -266,7 +264,6 @@ export function useTemplateManagement(): UseTemplateManagementReturn {
bom_items: template.section_type === 'BOM' ? [] : undefined
};
- console.log('Loading template to section:', template.template_name, 'newSection:', newSection);
addSectionToPage(selectedPage.id, newSection);
setSelectedTemplateId(null);
setIsLoadTemplateDialogOpen(false);
@@ -365,8 +362,6 @@ export function useTemplateManagement(): UseTemplateManagementReturn {
// 422 ValidationException 상세 메시지 처리 (field_key 중복/예약어, field_name 중복 등) → AlertDialog로 표시
if (error instanceof ApiError) {
- console.log('🔍 ApiError.errors:', error.errors); // 디버깅용
-
// errors 객체에서 첫 번째 에러 메시지 추출
if (error.errors && Object.keys(error.errors).length > 0) {
const firstKey = Object.keys(error.errors)[0];
diff --git a/src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx b/src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx
index 7acbf3b2..b21c08d5 100644
--- a/src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx
+++ b/src/components/items/ItemMasterDataManagement/tabs/HierarchyTab/index.tsx
@@ -1,5 +1,5 @@
import type { Dispatch, SetStateAction } from 'react';
-import type { ItemPage, ItemSection, BOMItem } from '@/contexts/ItemMasterContext';
+import type { ItemPage, ItemSection, ItemField, BOMItem } from '@/contexts/ItemMasterContext';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -33,12 +33,12 @@ interface HierarchyTabProps {
setEditingSectionTitle: (title: string) => void;
hasUnsavedChanges: boolean;
pendingChanges: {
- pages: any[];
- sections: any[];
- fields: any[];
- masterFields: any[];
- attributes: any[];
- sectionTemplates: any[];
+ pages: Record[];
+ sections: Record[];
+ fields: Record[];
+ masterFields: Record[];
+ attributes: Record[];
+ sectionTemplates: Record[];
};
selectedSectionForField: number | null;
setSelectedSectionForField: (id: number | null) => void;
@@ -46,8 +46,8 @@ interface HierarchyTabProps {
setNewSectionType: Dispatch>;
// Functions
- updateItemPage: (id: number, data: any) => void;
- trackChange: (type: 'pages' | 'sections' | 'fields' | 'masterFields' | 'attributes' | 'sectionTemplates', id: string, action: 'add' | 'update', data: any, attributeType?: string) => void;
+ updateItemPage: (id: number, data: Partial) => void;
+ trackChange: (type: 'pages' | 'sections' | 'fields' | 'masterFields' | 'attributes' | 'sectionTemplates', id: string, action: 'add' | 'update', data: Record, attributeType?: string) => void;
deleteItemPage: (id: number) => void;
duplicatePage: (id: number) => void;
setIsPageDialogOpen: (open: boolean) => void;
@@ -59,7 +59,7 @@ interface HierarchyTabProps {
unlinkSection: (pageId: number, sectionId: number) => void; // 연결 해제 (삭제 아님)
updateSection: (sectionId: number, updates: Partial) => Promise;
deleteField: (pageId: string, sectionId: string, fieldId: string) => void; // 2025-11-27: 연결 해제로 변경 (삭제 아님, 항목 탭에 유지)
- handleEditField: (sectionId: string, field: any) => void;
+ handleEditField: (sectionId: string, field: ItemField) => void;
// 2025-12-03: ID 기반으로 변경 (index stale 문제 해결)
moveField: (sectionId: number, dragFieldId: number, hoverFieldId: number) => void | Promise;
// 2025-11-26 추가: 섹션/필드 불러오기
@@ -372,7 +372,6 @@ export function HierarchyTab({
bomItems={section.bom_items || []}
onAddItem={async (item) => {
// 2025-11-27: API 함수로 BOM 항목 추가
- console.log('[HierarchyTab] BOM 추가 시작:', { sectionId: section.id, item, addBOMItemExists: !!addBOMItem });
if (addBOMItem) {
try {
await addBOMItem(section.id, {
@@ -383,7 +382,6 @@ export function HierarchyTab({
unit: item.unit,
spec: item.spec,
});
- console.log('[HierarchyTab] BOM 추가 성공');
toast.success('BOM 항목이 추가되었습니다');
} catch (error) {
if (isNextRedirectError(error)) throw error;
diff --git a/src/components/items/ItemMasterDataManagement/tabs/SectionsTab.tsx b/src/components/items/ItemMasterDataManagement/tabs/SectionsTab.tsx
index 81ea0e4d..024a2dc7 100644
--- a/src/components/items/ItemMasterDataManagement/tabs/SectionsTab.tsx
+++ b/src/components/items/ItemMasterDataManagement/tabs/SectionsTab.tsx
@@ -71,14 +71,6 @@ export function SectionsTab({
}: SectionsTabProps) {
// 2025-11-27: prop 변경 추적 (디버깅용)
useEffect(() => {
- console.log('[SectionsTab] 📥 sectionTemplates prop changed:', {
- count: sectionTemplates.length,
- sections: sectionTemplates.map(s => ({
- id: s.id,
- name: s.template_name,
- fieldsCount: s.fields?.length || 0
- }))
- });
}, [sectionTemplates]);
return (
@@ -118,16 +110,6 @@ export function SectionsTab({
{/* 일반 섹션 탭 */}
{(() => {
- console.log('[SectionsTab] 🔄 Rendering section templates:', {
- totalTemplates: sectionTemplates.length,
- generalTemplates: sectionTemplates.filter(t => t.section_type !== 'BOM').length,
- templates: sectionTemplates.map(t => ({
- id: t.id,
- template_name: t.template_name,
- section_type: t.section_type,
- fieldsCount: t.fields?.length || 0 // 필드 개수 추가
- }))
- });
return null;
})()}
{sectionTemplates.filter(t => t.section_type !== 'BOM').length === 0 ? (
diff --git a/src/components/material/ReceivingManagement/InspectionCreate.tsx b/src/components/material/ReceivingManagement/InspectionCreate.tsx
index 94e44825..0f63c4dd 100644
--- a/src/components/material/ReceivingManagement/InspectionCreate.tsx
+++ b/src/components/material/ReceivingManagement/InspectionCreate.tsx
@@ -162,14 +162,6 @@ export function InspectionCreate({ id }: Props) {
}
// TODO: API 호출
- console.log('검사 저장:', {
- targetId: selectedTargetId,
- inspectionDate,
- inspector,
- lotNo,
- items: inspectionItems,
- opinion,
- });
setShowSuccess(true);
}, [validateForm, selectedTargetId, inspectionDate, inspector, lotNo, inspectionItems, opinion]);
diff --git a/src/components/material/ReceivingManagement/ReceivingReceiptDialog.tsx b/src/components/material/ReceivingManagement/ReceivingReceiptDialog.tsx
index c310faec..e1a9597a 100644
--- a/src/components/material/ReceivingManagement/ReceivingReceiptDialog.tsx
+++ b/src/components/material/ReceivingManagement/ReceivingReceiptDialog.tsx
@@ -21,7 +21,6 @@ interface Props {
export function ReceivingReceiptDialog({ open, onOpenChange, detail }: Props) {
const handleDownload = () => {
// TODO: PDF 다운로드 기능
- console.log('PDF 다운로드:', detail);
};
const toolbarExtra = (
diff --git a/src/components/molecules/DateRangeSelector.tsx b/src/components/molecules/DateRangeSelector.tsx
index 3f174c30..5f7cbe00 100644
--- a/src/components/molecules/DateRangeSelector.tsx
+++ b/src/components/molecules/DateRangeSelector.tsx
@@ -151,11 +151,11 @@ export function DateRangeSelector({
{/* 날짜 범위 선택 */}
{!hideDateInputs && (
-
+
@@ -163,7 +163,7 @@ export function DateRangeSelector({
@@ -187,11 +187,11 @@ export function DateRangeSelector({
{/* 날짜 범위 선택 */}
{!hideDateInputs && (
-
+
@@ -199,7 +199,7 @@ export function DateRangeSelector({
diff --git a/src/components/organisms/DataTable.tsx b/src/components/organisms/DataTable.tsx
index 1049bf81..d624ab64 100644
--- a/src/components/organisms/DataTable.tsx
+++ b/src/components/organisms/DataTable.tsx
@@ -81,6 +81,7 @@ interface DataTableProps
{
}
// 셀 렌더러
+// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 다양한 셀 타입(text/date/icon/badge 등)을 다형적으로 처리
function renderCell(column: Column, value: any, row: T, index?: number): ReactNode {
// 값 포맷팅 또는 render 호출
let formattedValue: any;
@@ -182,7 +183,7 @@ function renderCell(column: Column, value: any, row: T, index?: number): R
}
// 정렬 함수
-function getAlignClass(column: Column): string {
+function getAlignClass(column: Column): string {
if (column.align) {
return column.align === "center" ? "text-center" : column.align === "right" ? "text-right" : "text-left";
}
diff --git a/src/components/outbound/VehicleDispatchManagement/actions.ts b/src/components/outbound/VehicleDispatchManagement/actions.ts
index 34b6be15..dbf87748 100644
--- a/src/components/outbound/VehicleDispatchManagement/actions.ts
+++ b/src/components/outbound/VehicleDispatchManagement/actions.ts
@@ -152,7 +152,6 @@ export async function updateVehicleDispatch(
_data: VehicleDispatchEditFormData
): Promise<{ success: boolean; error?: string }> {
try {
- console.log('[VehicleDispatchActions] updateVehicleDispatch:', id, _data);
// Mock: 항상 성공 반환
return { success: true };
} catch (error) {
diff --git a/src/components/pricing/PricingListClient.tsx b/src/components/pricing/PricingListClient.tsx
index 8301249f..9577cb4b 100644
--- a/src/components/pricing/PricingListClient.tsx
+++ b/src/components/pricing/PricingListClient.tsx
@@ -175,7 +175,6 @@ export function PricingListClient({
const handleHistory = (item: PricingListItem) => {
// TODO: 이력 다이얼로그 열기
- console.log('이력 조회:', item.id);
};
// 탭 옵션
@@ -334,7 +333,6 @@ export function PricingListClient({
variant="outline"
onClick={() => {
// TODO: API 연동 시 품목 마스터 동기화 로직 구현
- console.log('품목 마스터 동기화');
}}
className="ml-auto gap-2"
>
diff --git a/src/components/production/WorkOrders/actions.ts b/src/components/production/WorkOrders/actions.ts
index 2eb6aeef..82b21dc8 100644
--- a/src/components/production/WorkOrders/actions.ts
+++ b/src/components/production/WorkOrders/actions.ts
@@ -90,7 +90,6 @@ export async function getWorkOrders(params?: {
const queryString = searchParams.toString();
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders${queryString ? `?${queryString}` : ''}`;
- console.log('[WorkOrderActions] GET work-orders:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
@@ -148,7 +147,6 @@ export async function getWorkOrderStats(): Promise<{
try {
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/stats`;
- console.log('[WorkOrderActions] GET stats:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
@@ -192,7 +190,6 @@ export async function getWorkOrderById(id: string): Promise<{
try {
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}`;
- console.log('[WorkOrderActions] GET work-order:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
@@ -249,7 +246,6 @@ export async function createWorkOrder(
team_id: data.teamId,
};
- console.log('[WorkOrderActions] POST work-order request:', apiData);
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders`,
@@ -264,7 +260,6 @@ export async function createWorkOrder(
}
const result = await response.json();
- console.log('[WorkOrderActions] POST work-order response:', result);
if (!response.ok || !result.success) {
return {
@@ -292,7 +287,6 @@ export async function updateWorkOrder(
try {
const apiData = transformFrontendToApi(data);
- console.log('[WorkOrderActions] PUT work-order request:', apiData);
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}`,
@@ -307,7 +301,6 @@ export async function updateWorkOrder(
}
const result = await response.json();
- console.log('[WorkOrderActions] PUT work-order response:', result);
if (!response.ok || !result.success) {
return {
@@ -340,7 +333,6 @@ export async function deleteWorkOrder(id: string): Promise<{ success: boolean; e
}
const result = await response.json();
- console.log('[WorkOrderActions] DELETE work-order response:', result);
if (!response.ok || !result.success) {
return {
@@ -363,7 +355,6 @@ export async function updateWorkOrderStatus(
status: WorkOrderStatus
): Promise<{ success: boolean; data?: WorkOrder; error?: string }> {
try {
- console.log('[WorkOrderActions] PATCH status request:', { status });
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}/status`,
@@ -378,7 +369,6 @@ export async function updateWorkOrderStatus(
}
const result = await response.json();
- console.log('[WorkOrderActions] PATCH status response:', result);
if (!response.ok || !result.success) {
return {
@@ -410,7 +400,6 @@ export async function assignWorkOrder(
const body: { assignee_ids: number[]; team_id?: number } = { assignee_ids: ids };
if (teamId) body.team_id = teamId;
- console.log('[WorkOrderActions] PATCH assign request:', body);
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}/assign`,
@@ -425,7 +414,6 @@ export async function assignWorkOrder(
}
const result = await response.json();
- console.log('[WorkOrderActions] PATCH assign response:', result);
if (!response.ok || !result.success) {
return {
@@ -451,7 +439,6 @@ export async function toggleBendingField(
field: string
): Promise<{ success: boolean; data?: WorkOrder; error?: string }> {
try {
- console.log('[WorkOrderActions] PATCH bending toggle request:', { field });
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}/bending/toggle`,
@@ -466,7 +453,6 @@ export async function toggleBendingField(
}
const result = await response.json();
- console.log('[WorkOrderActions] PATCH bending toggle response:', result);
if (!response.ok || !result.success) {
return {
@@ -496,7 +482,6 @@ export async function addWorkOrderIssue(
}
): Promise<{ success: boolean; data?: WorkOrder; error?: string }> {
try {
- console.log('[WorkOrderActions] POST issue request:', data);
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${id}/issues`,
@@ -511,7 +496,6 @@ export async function addWorkOrderIssue(
}
const result = await response.json();
- console.log('[WorkOrderActions] POST issue response:', result);
if (!response.ok || !result.success) {
return {
@@ -537,7 +521,6 @@ export async function resolveWorkOrderIssue(
issueId: string
): Promise<{ success: boolean; data?: WorkOrder; error?: string }> {
try {
- console.log('[WorkOrderActions] PATCH issue resolve:', { workOrderId, issueId });
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${workOrderId}/issues/${issueId}/resolve`,
@@ -549,7 +532,6 @@ export async function resolveWorkOrderIssue(
}
const result = await response.json();
- console.log('[WorkOrderActions] PATCH issue resolve response:', result);
if (!response.ok || !result.success) {
return {
@@ -585,7 +567,6 @@ export async function updateWorkOrderItemStatus(
error?: string;
}> {
try {
- console.log('[WorkOrderActions] PATCH item status request:', { workOrderId, itemId, status });
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/status`,
@@ -600,7 +581,6 @@ export async function updateWorkOrderItemStatus(
}
const result = await response.json();
- console.log('[WorkOrderActions] PATCH item status response:', result);
if (!response.ok || !result.success) {
return {
@@ -699,7 +679,6 @@ export async function saveInspectionData(
data: unknown
): Promise<{ success: boolean; error?: string }> {
try {
- console.log('[WorkOrderActions] POST inspection data:', { workOrderId, processType });
const { response, error } = await serverFetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/work-orders/${workOrderId}/inspection`,
@@ -717,7 +696,6 @@ export async function saveInspectionData(
}
const result = await response.json();
- console.log('[WorkOrderActions] POST inspection response:', result);
if (!response.ok || !result.success) {
return {
@@ -867,7 +845,6 @@ export async function getSalesOrdersForWorkOrder(params?: {
const queryString = searchParams.toString();
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/orders${queryString ? `?${queryString}` : ''}`;
- console.log('[WorkOrderActions] GET orders for work-order:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
@@ -947,7 +924,6 @@ export async function getDepartmentsWithUsers(): Promise<{
try {
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/departments/tree?with_users=1`;
- console.log('[WorkOrderActions] GET departments with users:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
@@ -1019,7 +995,6 @@ export async function getProcessOptions(): Promise<{
try {
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/processes/options`;
- console.log('[WorkOrderActions] GET process options:', url);
const { response, error } = await serverFetch(url, { method: 'GET' });
diff --git a/src/components/production/WorkResults/WorkResultList.tsx b/src/components/production/WorkResults/WorkResultList.tsx
index 630c0ca4..460620e5 100644
--- a/src/components/production/WorkResults/WorkResultList.tsx
+++ b/src/components/production/WorkResults/WorkResultList.tsx
@@ -67,7 +67,6 @@ export function WorkResultList() {
// ===== 상세 보기 핸들러 =====
const handleView = useCallback((item: WorkResult) => {
- console.log('상세 보기:', item.id);
// TODO: 상세 보기 기능 구현
}, []);
diff --git a/src/components/production/WorkerScreen/IssueReportModal.tsx b/src/components/production/WorkerScreen/IssueReportModal.tsx
index 17c044de..9c03cbf9 100644
--- a/src/components/production/WorkerScreen/IssueReportModal.tsx
+++ b/src/components/production/WorkerScreen/IssueReportModal.tsx
@@ -53,12 +53,6 @@ export function IssueReportModal({ open, onOpenChange, order }: IssueReportModal
return;
}
- console.log('[이슈보고]', {
- orderId: order?.id,
- orderNo: order?.orderNo,
- issueType: selectedType,
- description,
- });
setShowSuccessAlert(true);
};
diff --git a/src/components/production/WorkerScreen/index.tsx b/src/components/production/WorkerScreen/index.tsx
index 7361fcef..96c5d655 100644
--- a/src/components/production/WorkerScreen/index.tsx
+++ b/src/components/production/WorkerScreen/index.tsx
@@ -735,7 +735,6 @@ export default function WorkerScreen() {
// 자재 수정 핸들러
const handleEditMaterial = useCallback(
(itemId: string, material: MaterialListItem) => {
- console.log('[WorkerScreen] editMaterial:', itemId, material);
// 추후 구현
},
[]
@@ -744,7 +743,6 @@ export default function WorkerScreen() {
// 자재 삭제 핸들러
const handleDeleteMaterial = useCallback(
(itemId: string, materialId: string) => {
- console.log('[WorkerScreen] deleteMaterial:', itemId, materialId);
// 추후 구현
},
[]
diff --git a/src/components/quality/PerformanceReportManagement/PerformanceReportList.tsx b/src/components/quality/PerformanceReportManagement/PerformanceReportList.tsx
index 948830da..3d2a3785 100644
--- a/src/components/quality/PerformanceReportManagement/PerformanceReportList.tsx
+++ b/src/components/quality/PerformanceReportManagement/PerformanceReportList.tsx
@@ -412,31 +412,36 @@ export function PerformanceReportList() {
) : undefined,
- // 헤더 액션 (선택 기반 버튼)
- headerActions: ({ selectedItems, onClearSelection, onRefresh }) => {
+ // 선택 액션 (체크박스 선택 시 테이블 좌측에 표시)
+ selectionActions: ({ selectedItems, onClearSelection, onRefresh }) => {
+ if (currentTab !== 'quarterly') return null;
+ return (
+ <>
+
handleConfirm(selectedItems, onClearSelection, onRefresh)}>
+
+ 선택 확정
+
+
handleUnconfirm(selectedItems, onClearSelection, onRefresh)}>
+
+ 확정 해제
+
+
handleDistribute(selectedItems, onClearSelection, onRefresh)}>
+
+ 배포
+
+
handleOpenMemoModal(selectedItems)}>
+
+ 메모
+
+ >
+ );
+ },
+
+ // 헤더 액션 (항상 표시)
+ headerActions: () => {
if (currentTab !== 'quarterly') return null;
return (
- {selectedItems.size > 0 && (
- <>
-
handleConfirm(selectedItems, onClearSelection, onRefresh)}>
-
- 선택 확정
-
-
handleUnconfirm(selectedItems, onClearSelection, onRefresh)}>
-
- 확정 해제
-
-
handleDistribute(selectedItems, onClearSelection, onRefresh)}>
-
- 배포
-
-
handleOpenMemoModal(selectedItems)}>
-
- 메모
-
- >
- )}
확정건 엑셀다운로드
diff --git a/src/components/quotes/LocationDetailPanel.tsx b/src/components/quotes/LocationDetailPanel.tsx
index aadbde44..c2e40663 100644
--- a/src/components/quotes/LocationDetailPanel.tsx
+++ b/src/components/quotes/LocationDetailPanel.tsx
@@ -819,7 +819,6 @@ export function LocationDetailPanel({
unitPrice: updatedGrandTotal,
totalPrice: updatedGrandTotal * location.quantity,
});
- console.log(`[품목 추가] ${item.code} - ${item.name} → ${categoryLabel} (${categoryCode}), 단가: ${unitPrice}`);
}}
tabLabel={detailTabs.find((t) => t.value === activeTab)?.label}
/>
diff --git a/src/components/quotes/QuotePreviewModal.tsx b/src/components/quotes/QuotePreviewModal.tsx
index 2549ae51..f1e168fb 100644
--- a/src/components/quotes/QuotePreviewModal.tsx
+++ b/src/components/quotes/QuotePreviewModal.tsx
@@ -56,12 +56,10 @@ export function QuotePreviewModal({
const vatIncluded = quoteData.vatType === 'included';
const handleDuplicate = () => {
- console.log('[테스트] 복제');
onDuplicate?.();
};
const handleEdit = () => {
- console.log('[테스트] 수정');
onEdit?.();
};
diff --git a/src/components/quotes/QuoteRegistration.tsx b/src/components/quotes/QuoteRegistration.tsx
index ef485e05..9b8e40da 100644
--- a/src/components/quotes/QuoteRegistration.tsx
+++ b/src/components/quotes/QuoteRegistration.tsx
@@ -323,15 +323,6 @@ export function QuoteRegistration({
// editingQuote가 변경되면 formData 업데이트 및 calculationResults 초기화
useEffect(() => {
- console.log('[QuoteRegistration] useEffect editingQuote:', JSON.stringify({
- hasEditingQuote: !!editingQuote,
- itemCount: editingQuote?.items?.length,
- item0: editingQuote?.items?.[0] ? {
- quantity: editingQuote.items[0].quantity,
- wingSize: editingQuote.items[0].wingSize,
- inspectionFee: editingQuote.items[0].inspectionFee,
- } : null,
- }, null, 2));
if (editingQuote) {
setFormData(editingQuote);
// 수정 모드 진입 시 이전 산출 결과 초기화
@@ -449,10 +440,6 @@ export function QuoteRegistration({
field: keyof QuoteFormData,
value: string | QuoteItem[]
) => {
- // DEBUG: manager, contact, remarks 필드 변경 추적
- if (field === 'manager' || field === 'contact' || field === 'remarks') {
- console.log(`[handleFieldChange] ${field} 변경:`, value);
- }
setFormData({ ...formData, [field]: value });
if (errors[field]) {
setErrors((prev) => {
@@ -617,11 +604,6 @@ export function QuoteRegistration({
};
// 렌더링 직전 디버그 로그
- console.log('[QuoteRegistration] 렌더링 직전 formData.items[0]:', JSON.stringify({
- quantity: formData.items[0]?.quantity,
- wingSize: formData.items[0]?.wingSize,
- inspectionFee: formData.items[0]?.inspectionFee,
- }, null, 2));
// 폼 콘텐츠 렌더링
const renderFormContent = useCallback(
diff --git a/src/components/quotes/QuoteRegistrationV2.tsx b/src/components/quotes/QuoteRegistrationV2.tsx
index 0776908a..d929bf6b 100644
--- a/src/components/quotes/QuoteRegistrationV2.tsx
+++ b/src/components/quotes/QuoteRegistrationV2.tsx
@@ -222,7 +222,6 @@ export function QuoteRegistrationV2({
useDevFill("quoteV2", useCallback(() => {
// BOM이 있는 제품만 필터링
const productsWithBom = finishedGoods.filter((fg) => fg.has_bom === true || (fg.bom && Array.isArray(fg.bom) && fg.bom.length > 0));
- console.log(`[DevFill] BOM 있는 제품: ${productsWithBom.length}개 / 전체: ${finishedGoods.length}개`);
// 랜덤 개소 생성 함수
const createRandomLocation = (index: number): LocationItem => {
@@ -611,12 +610,6 @@ export function QuoteRegistrationV2({
const apiData = result.data as BomBulkResponse;
const bomResponseItems = apiData.items || [];
- console.log('[QuoteRegistrationV2] BOM 계산 결과:', {
- success: apiData.success,
- summary: apiData.summary,
- itemsCount: bomResponseItems.length,
- firstItem: bomResponseItems[0],
- });
// 결과 반영 (수동 추가 품목 보존)
const updatedLocations = formData.locations.map((loc, index) => {
@@ -638,13 +631,6 @@ export function QuoteRegistrationV2({
const mergedItems = [...(bomResult.items || []), ...manualItems];
const mergedGrandTotal = bomResult.grand_total + manualTotal;
- console.log(`[QuoteRegistrationV2] Location ${index} bomResult:`, {
- items: bomResult.items?.length,
- manualItems: manualItems.length,
- mergedItems: mergedItems.length,
- subtotals: bomResult.subtotals,
- grand_total: mergedGrandTotal,
- });
return {
...loc,
diff --git a/src/components/quotes/types.ts b/src/components/quotes/types.ts
index eec74a6f..c15ad936 100644
--- a/src/components/quotes/types.ts
+++ b/src/components/quotes/types.ts
@@ -488,10 +488,6 @@ export function transformQuoteToFormData(quote: Quote): QuoteFormData {
const amountPerItem = Math.round(totalBomAmount / itemCount);
// 디버깅 로그
- console.log('[transformQuoteToFormData] quote.calculationInputs:', JSON.stringify(quote.calculationInputs, null, 2));
- console.log('[transformQuoteToFormData] calcInputs:', JSON.stringify(calcInputs, null, 2));
- console.log('[transformQuoteToFormData] quote.items.length:', quote.items.length);
- console.log('[transformQuoteToFormData] totalBomAmount:', totalBomAmount, 'amountPerItem:', amountPerItem);
return {
id: quote.id,
@@ -588,7 +584,6 @@ export function transformApiDataToFormData(apiData: QuoteApiData): QuoteFormData
const itemCount = calcInputs.length || 1;
const amountPerItem = Math.round(totalBomAmount / itemCount);
- console.log('[transformApiDataToFormData] totalBomAmount:', totalBomAmount, 'itemCount:', itemCount, 'amountPerItem:', amountPerItem);
return {
id: String(apiData.id),
@@ -1135,23 +1130,6 @@ export function transformFormDataToApi(formData: QuoteFormData): Record ({
- item_name: i.item_name,
- quantity: i.quantity,
- base_quantity: i.base_quantity,
- calculated_quantity: i.calculated_quantity,
- })),
- }, null, 2));
return result;
}
\ No newline at end of file
diff --git a/src/components/settings/NotificationSettings/index.tsx b/src/components/settings/NotificationSettings/index.tsx
index 7716eae3..62cf1324 100644
--- a/src/components/settings/NotificationSettings/index.tsx
+++ b/src/components/settings/NotificationSettings/index.tsx
@@ -128,7 +128,6 @@ interface NotificationSectionProps {
}
function NotificationSection({ title, enabled, onEnabledChange, children }: NotificationSectionProps) {
- console.log(`[NotificationSection] ${title} enabled:`, enabled);
return (
@@ -137,7 +136,6 @@ function NotificationSection({ title, enabled, onEnabledChange, children }: Noti
{
- console.log(`[Switch] ${title} clicked:`, checked);
onEnabledChange(checked);
}}
/>
diff --git a/src/components/settings/PermissionManagement/PermissionDetailClient.tsx b/src/components/settings/PermissionManagement/PermissionDetailClient.tsx
index 956a5593..69b26028 100644
--- a/src/components/settings/PermissionManagement/PermissionDetailClient.tsx
+++ b/src/components/settings/PermissionManagement/PermissionDetailClient.tsx
@@ -157,12 +157,9 @@ export function PermissionDetailClient({ permissionId, isNew = false, mode = 'vi
try {
// 메뉴 트리 로드
const menusResult = await fetchPermissionMenus();
- console.log('[PermissionDetail] menusResult:', menusResult);
if (menusResult.success && menusResult.data) {
- console.log('[PermissionDetail] menus (flat):', menusResult.data.menus);
// 플랫 배열을 트리 구조로 변환
const treeMenus = buildMenuTree(menusResult.data.menus as FlatMenuItem[]);
- console.log('[PermissionDetail] menus (tree):', treeMenus);
setMenuTree(treeMenus);
setPermissionTypes(menusResult.data.permission_types);
} else {
diff --git a/src/components/templates/IntegratedListTemplateV2.tsx b/src/components/templates/IntegratedListTemplateV2.tsx
index ab1859f2..b1583718 100644
--- a/src/components/templates/IntegratedListTemplateV2.tsx
+++ b/src/components/templates/IntegratedListTemplateV2.tsx
@@ -83,10 +83,10 @@ export interface DevMetadata {
componentName: string;
pagePath: string;
description: string;
- apis?: any[];
- dataStructures?: any[];
- dbSchema?: any[];
- businessLogic?: any[];
+ apis?: unknown[];
+ dataStructures?: unknown[];
+ dbSchema?: unknown[];
+ businessLogic?: unknown[];
}
export interface IntegratedListTemplateV2Props {
@@ -212,6 +212,8 @@ export interface IntegratedListTemplateV2Props {
onToggleSelectAll: () => void;
getItemId: (item: T) => string; // 아이템에서 ID 추출
onBulkDelete?: () => void; // 일괄 삭제 핸들러
+ /** 선택 액션 버튼 (테이블 왼쪽 "전체 N건 / N개 선택됨" 옆에 표시) */
+ selectionActions?: ReactNode;
// 테이블 표시 옵션
showCheckbox?: boolean; // 체크박스 표시 여부 (기본: true)
@@ -280,6 +282,7 @@ export function IntegratedListTemplateV2({
onToggleSelectAll,
getItemId,
onBulkDelete,
+ selectionActions,
showCheckbox = true, // 기본값 true
showRowNumber = true, // 기본값 true (번호 컬럼은 renderTableRow에서 처리)
renderTableRow,
@@ -637,7 +640,7 @@ export function IntegratedListTemplateV2({
)}
{/* 버튼 영역 (오른쪽 배치, 공간 부족시 자연스럽게 줄바꿈) */}
{(headerActions || createButton) && (
-
+
{/* 헤더 액션 (엑셀 다운로드 등 추가 버튼들) */}
{headerActions}
{/* 등록 버튼 */}
@@ -665,8 +668,8 @@ export function IntegratedListTemplateV2
({
{/* 탭 - 카드 밖 (tabsPosition === 'above-stats') */}
{tabsPosition === 'above-stats' && tabs && tabs.length > 0 && (
-
-
+
+
{tabs.map((tab) => (
({
{/* 데스크톱 (1280px+) - TabChip 탭 */}
-
+ {/* 왼쪽: 전체 건수 + 선택 정보 + 선택삭제 */}
+
+
+ 전체 {pagination.totalItems}건
+
+ {selectedItems.size > 0 && (
+ <>
+ /
+
+ {selectedItems.size}개 항목 선택됨
+
+ >
+ )}
+ {selectedItems.size >= 1 && onBulkDelete && (
+
+
+ 선택삭제
+
+ )}
+ {/* 선택 액션 버튼 (상신, 승인, 삭제 등) */}
+ {selectedItems.size > 0 && selectionActions}
+
+ {/* 오른쪽: 탭 + 헤더 액션 + 필터 */}
+
{tabsPosition !== 'above-stats' && tabs && tabs.map((tab) => (
({
color={tab.color as any}
/>
))}
-
-
- {/* 선택된 항목 수 표시 */}
- {selectedItems.size > 0 && (
-
- {selectedItems.size}개 항목 선택됨
-
- )}
- {/* 테이블 헤더 액션 (총 N건 등) - 필터 앞에 배치 */}
{tableHeaderActions}
- {/* filterConfig 기반 자동 필터 (PC) */}
{renderAutoFilters()}
- {selectedItems.size >= 1 && onBulkDelete && (
-
-
- 선택 삭제({selectedItems.size})
-
- )}
{/* 모바일/태블릿 (~1279px) - TabChip 탭 */}
{tabsPosition !== 'above-stats' && tabs && tabs.length > 0 && (
-
-
+
+
{tabs.map((tab) => (
({
{/* 탭 컨텐츠 */}
{(tabs || [{ value: 'default', label: '', count: 0 }]).map((tab) => (
- {/* 모바일/태블릿/소형 노트북 (~1279px) - 선택 삭제 버튼 */}
- {selectedItems.size >= 2 && onBulkDelete && (
-
-
-
- 선택 삭제 ({selectedItems.size})
-
+ {/* 모바일/태블릿/소형 노트북 (~1279px) - 선택 액션 + 선택 삭제 하단 고정 바 */}
+ {selectedItems.size > 0 && (selectionActions || onBulkDelete) && (
+
+
+
+ {selectedItems.size}개 선택됨
+
+
+
+ {selectionActions}
+ {selectedItems.size >= 1 && onBulkDelete && (
+
+
+ 선택삭제
+
+ )}
+
)}
diff --git a/src/components/templates/UniversalListPage/index.tsx b/src/components/templates/UniversalListPage/index.tsx
index 9043fa6b..787a616b 100644
--- a/src/components/templates/UniversalListPage/index.tsx
+++ b/src/components/templates/UniversalListPage/index.tsx
@@ -542,10 +542,6 @@ export function UniversalListPage
({
let dataToDownload: T[];
// 디버깅: 데이터 개수 확인
- console.log('[Excel] clientSideFiltering:', config.clientSideFiltering);
- console.log('[Excel] rawData.length:', rawData.length);
- console.log('[Excel] filteredData.length:', filteredData.length);
- console.log('[Excel] fetchAllUrl:', fetchAllUrl);
// fetchAllUrl이 있으면 서버에서 전체 데이터 페이지별 순차 조회 (clientSideFiltering 여부 무관)
if (fetchAllUrl) {
@@ -584,7 +580,6 @@ export function UniversalListPage({
const total = firstResult.data?.total ?? firstPageData.length;
const totalPages = Math.ceil(total / PAGE_SIZE);
- console.log(`[Excel] 전체 ${total}건, ${totalPages}페이지 조회 시작`);
// 2) 나머지 페이지 병렬 호출
const allData: T[] = [...(firstPageData as T[])];
@@ -621,7 +616,6 @@ export function UniversalListPage({
}
}
- console.log(`[Excel] 총 ${allData.length}건 조회 완료`);
dataToDownload = allData;
}
// fetchAllUrl 없으면 현재 로드된 데이터 사용
@@ -958,6 +952,11 @@ export function UniversalListPage({
onToggleSelectAll={toggleSelectAll}
getItemId={effectiveGetItemId}
onBulkDelete={config.actions?.deleteItem ? handleBulkDeleteClick : undefined}
+ selectionActions={config.selectionActions?.({
+ selectedItems: effectiveSelectedItems,
+ onClearSelection: () => externalSelection ? undefined : setSelectedItems(new Set()),
+ onRefresh: fetchData,
+ })}
// 표시 옵션
showCheckbox={config.showCheckbox}
showRowNumber={config.showRowNumber}
diff --git a/src/components/templates/UniversalListPage/types.ts b/src/components/templates/UniversalListPage/types.ts
index 04360195..204d900a 100644
--- a/src/components/templates/UniversalListPage/types.ts
+++ b/src/components/templates/UniversalListPage/types.ts
@@ -257,6 +257,16 @@ export interface UniversalListConfig {
onClearSelection: () => void;
onRefresh: () => void;
}) => ReactNode;
+ /**
+ * 선택 액션 버튼 (테이블 왼쪽 "전체 N건 / N개 선택됨" 옆에 표시)
+ * - 체크박스 선택 시에만 표시되는 버튼들 (상신, 승인, 삭제 등)
+ * - headerActions에서 선택 의존 버튼을 분리하여 이쪽으로 이동
+ */
+ selectionActions?: (params: {
+ selectedItems: Set;
+ onClearSelection: () => void;
+ onRefresh: () => void;
+ }) => ReactNode;
/** 커스텀 액션 버튼 (상신, 승인 등) */
customActions?: CustomAction[];
diff --git a/src/contexts/ApiErrorContext.tsx b/src/contexts/ApiErrorContext.tsx
index 10b54ed7..00a93687 100644
--- a/src/contexts/ApiErrorContext.tsx
+++ b/src/contexts/ApiErrorContext.tsx
@@ -40,6 +40,7 @@ export function ApiErrorProvider({ children }: { children: ReactNode }) {
* 인증 에러 처리 - 로그아웃 후 로그인 페이지 이동
*/
const handleAuthError = useCallback(async () => {
+ if (typeof window === 'undefined') return;
// 중복 리다이렉트 방지
if (isRedirecting.current) return;
isRedirecting.current = true;
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx
index 204cb2bb..ac365128 100644
--- a/src/contexts/AuthContext.tsx
+++ b/src/contexts/AuthContext.tsx
@@ -195,7 +195,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const currentTenantId = currentUser?.tenant?.id;
if (prevTenantId && currentTenantId && prevTenantId !== currentTenantId) {
- console.log(`[Auth] Tenant changed: ${prevTenantId} → ${currentTenantId}`);
clearTenantCache(prevTenantId);
}
@@ -220,7 +219,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
Object.keys(localStorage).forEach(key => {
if (key.startsWith(tenantAwarePrefix)) {
localStorage.removeItem(key);
- console.log(`[Cache] Cleared localStorage: ${key}`);
}
});
@@ -228,14 +226,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
Object.keys(sessionStorage).forEach(key => {
if (key.startsWith(tenantAwarePrefix) || key.startsWith(pageConfigPrefix)) {
sessionStorage.removeItem(key);
- console.log(`[Cache] Cleared sessionStorage: ${key}`);
}
});
};
// ✅ 추가: 로그아웃 함수 (완전한 캐시 정리)
const logout = async () => {
- console.log('[Auth] Starting logout...');
// 1. React 상태 초기화 (UI 즉시 반영)
setCurrentUser(null);
@@ -246,7 +242,6 @@ export function AuthProvider({ children }: { children: ReactNode }) {
redirectTo: null, // 리다이렉트는 호출하는 곳에서 처리
});
- console.log('[Auth] Logout completed');
};
// Context value
diff --git a/src/contexts/ItemMasterContext.tsx b/src/contexts/ItemMasterContext.tsx
index 60053a98..546b3b0e 100644
--- a/src/contexts/ItemMasterContext.tsx
+++ b/src/contexts/ItemMasterContext.tsx
@@ -538,7 +538,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadItemMasterFields = (fields: ItemMasterField[]) => {
setItemMasterFields(fields);
- console.log('[ItemMasterContext] 마스터 필드 로드 완료:', fields.length);
};
/**
@@ -586,7 +585,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
};
setItemMasterFields(prev => [...prev, newField]);
- console.log('[ItemMasterContext] 마스터 필드 생성 성공:', newField.id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 마스터 필드 생성 실패:', errorMessage);
@@ -601,7 +599,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const updateItemMasterField = async (id: number, updates: Partial) => {
try {
// API 호출 - 2025-11-27: fields.update() 사용 (masterFields.update() deprecated)
- const requestData: any = {};
+ const requestData: Record = {};
if (updates.field_name !== undefined) requestData.field_name = updates.field_name;
if (updates.field_key !== undefined) requestData.field_key = updates.field_key; // 2025-11-28: field_key 추가
if (updates.field_type !== undefined) requestData.field_type = updates.field_type;
@@ -680,7 +678,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
}))
})));
- console.log('[ItemMasterContext] 마스터 필드 수정 성공:', id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 마스터 필드 수정 실패:', errorMessage);
@@ -719,7 +716,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
}))
})));
- console.log('[ItemMasterContext] 마스터 필드 삭제 성공:', id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 마스터 필드 삭제 실패:', errorMessage);
@@ -733,7 +729,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadSectionTemplates = (templates: SectionTemplate[]) => {
setSectionTemplates(templates);
- console.log('[ItemMasterContext] 섹션 템플릿 로드 완료:', templates.length);
};
const addSectionTemplate = async (template: Omit) => {
@@ -777,7 +772,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
};
setSectionTemplates(prev => [...prev, newTemplate]);
- console.log('[ItemMasterContext] 섹션 템플릿 생성 성공:', newTemplate.id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 템플릿 생성 실패:', errorMessage);
@@ -797,7 +791,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// API 요청 형식으로 변환 (frontend → API)
// frontend: template_name, section_type
// API: title, type
- const requestData: any = {};
+ const requestData: Record = {};
if (updates.template_name !== undefined) requestData.title = updates.template_name;
if (updates.section_type !== undefined) {
// section_type 변환: 'BASIC' | 'CUSTOM' → 'fields', 'BOM' → 'bom'
@@ -840,7 +834,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
)
})));
- console.log('[ItemMasterContext] 섹션 템플릿 수정 성공 (양방향 동기화):', id);
}
// 로컬 전용 필드 업데이트 (default_fields, fields, category, bomItems)
@@ -869,7 +862,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
return updatedTemplate;
}));
- console.log('[ItemMasterContext] 섹션 템플릿 수정 성공 (로컬):', id, Object.keys(updates).filter(k => localOnlyUpdates.includes(k)));
}
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -896,7 +888,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
sections: page.sections.filter(section => section.id !== id)
})));
- console.log('[ItemMasterContext] 섹션 템플릿 삭제 성공 (양방향 동기화):', id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 템플릿 삭제 실패:', errorMessage);
@@ -912,7 +903,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadItemPages = (pages: ItemPage[]) => {
setItemPages(pages); // 덮어쓰기 (append가 아님!)
- console.log('[ItemMasterContext] 페이지 데이터 로드 완료:', pages.length);
};
/**
@@ -936,7 +926,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const newPage = transformPageResponse(response);
setItemPages(prev => [...prev, newPage]);
- console.log('[ItemMasterContext] 페이지 생성 성공:', newPage);
return newPage;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -974,7 +963,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
return page;
}));
- console.log('[ItemMasterContext] 페이지 수정 성공:', { id, updates });
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 페이지 수정 실패:', errorMessage);
@@ -1008,19 +996,10 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const existingIds = new Set(prev.map(s => s.id));
// 중복되지 않는 섹션만 추가 (필드 포함된 원본 데이터)
const newSections = sectionsToPreserve.filter(s => !existingIds.has(s.id));
- console.log('[ItemMasterContext] 페이지 삭제 - 섹션을 독립 섹션으로 이동:', {
- preserved: newSections.length,
- withFields: newSections.map(s => ({
- id: s.id,
- title: s.title,
- fieldCount: s.fields?.length || 0
- }))
- });
return [...prev, ...newSections];
});
}
- console.log('[ItemMasterContext] 페이지 삭제 성공:', id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 페이지 삭제 실패:', errorMessage);
@@ -1055,7 +1034,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const reorderedPages = response.data.map(transformPageResponse);
setItemPages(reorderedPages);
- console.log('[ItemMasterContext] 페이지 순서 변경 성공');
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 페이지 순서 변경 실패:', errorMessage);
@@ -1087,7 +1065,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
: page
));
- console.log('[ItemMasterContext] 섹션 생성 성공:', newSection);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 생성 실패:', errorMessage);
@@ -1098,7 +1075,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const updateSection = async (sectionId: number, updates: Partial) => {
try {
// API 요청 데이터 변환
- const requestData: any = {};
+ const requestData: Record = {};
if (updates.title) requestData.title = updates.title;
// page_id 변경 지원 (연결 해제 시 null)
if ('page_id' in updates) requestData.page_id = updates.page_id;
@@ -1133,7 +1110,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
setIndependentSections(prev => [...prev, unlinkedSection!]);
}
- console.log('[ItemMasterContext] 섹션 연결 해제 (계층구조→독립):', sectionId);
} else {
// 일반 수정: 섹션 데이터 업데이트
setItemPages(prev => prev.map(page => ({
@@ -1158,7 +1134,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
: section
));
- console.log('[ItemMasterContext] 섹션 수정 성공 (3방향 동기화):', updatedSection);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 수정 실패:', errorMessage);
@@ -1187,7 +1162,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 3. independentSections 업데이트 (독립 섹션) - sectionsAsTemplates useMemo 재계산 트리거
setIndependentSections(prev => prev.filter(section => section.id !== sectionId));
- console.log('[ItemMasterContext] 섹션 삭제 성공 (3방향 동기화):', sectionId);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 삭제 실패:', errorMessage);
@@ -1201,15 +1175,9 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const requestData = {
items: sectionIds.map((id, index) => ({ id, order_no: index + 1 })) // order_no는 1부터 시작
};
- console.log('[ItemMasterContext] 섹션 순서 변경 요청:', {
- pageId,
- sectionIds,
- requestData
- });
// API 호출
const response = await itemMasterApi.sections.reorder(pageId, requestData);
- console.log('[ItemMasterContext] 섹션 순서 변경 응답:', response);
if (!response.success) {
throw new Error(response.message || '섹션 순서 변경 실패');
@@ -1243,7 +1211,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
}));
}
- console.log('[ItemMasterContext] 섹션 순서 변경 성공');
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 순서 변경 실패:', errorMessage);
@@ -1295,7 +1262,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
updated_at: response.data.updated_at,
};
- console.log('[addFieldToSection] Before setItemPages, sectionId:', sectionId, 'newField:', newField.field_name);
// 2025-11-27: itemPages 업데이트 (페이지에 연결된 섹션)
setItemPages(prev => {
@@ -1307,14 +1273,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
: section
)
}));
- console.log('[addFieldToSection] After setItemPages, updated pages:', updated.map(p => ({
- id: p.id,
- sections: p.sections.map(s => ({
- id: s.id,
- title: s.title,
- fieldsCount: s.fields?.length || 0
- }))
- })));
return updated;
});
@@ -1327,11 +1285,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
? { ...section, fields: [...(section.fields || []), newField] }
: section
);
- console.log('[addFieldToSection] After setIndependentSections, updated sections:', updated.map(s => ({
- id: s.id,
- title: s.title,
- fieldsCount: s.fields?.length || 0
- })));
return updated;
});
@@ -1359,7 +1312,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
};
setItemMasterFields(prev => [...prev, newMasterField]);
- console.log('[ItemMasterContext] 필드 생성 성공:', newField.id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 필드 생성 실패:', errorMessage);
@@ -1370,7 +1322,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const updateField = async (fieldId: number, updates: Partial) => {
try {
// API 호출 (null → undefined 변환)
- const requestData: any = {};
+ const requestData: Record = {};
if (updates.field_name !== undefined) requestData.field_name = updates.field_name;
// 2025-11-28: field_key 추가
if (updates.field_key !== undefined) requestData.field_key = updates.field_key ?? undefined;
@@ -1476,7 +1428,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
)
})));
- console.log('[ItemMasterContext] 필드 수정 성공:', fieldId);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 필드 수정 실패:', errorMessage);
@@ -1515,7 +1466,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
fields: (section.fields || []).filter(field => field.id !== fieldId)
})));
- console.log('[ItemMasterContext] 필드 삭제 성공:', fieldId);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 필드 삭제 실패:', errorMessage);
@@ -1655,7 +1605,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
: section
));
- console.log('[ItemMasterContext] BOM 항목 생성 성공 (양방향 동기화):', newBOM.id);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] BOM 항목 생성 실패:', errorMessage);
@@ -1666,7 +1615,7 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const updateBOMItem = async (bomId: number, updates: Partial) => {
try {
// API 호출 (null → undefined 변환)
- const requestData: any = {};
+ const requestData: Record = {};
if (updates.item_code !== undefined) requestData.item_code = updates.item_code ?? undefined;
if (updates.item_name !== undefined) requestData.item_name = updates.item_name;
if (updates.quantity !== undefined) requestData.quantity = updates.quantity;
@@ -1715,7 +1664,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
)
})));
- console.log('[ItemMasterContext] BOM 항목 수정 성공 (양방향 동기화):', bomId);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] BOM 항목 수정 실패:', errorMessage);
@@ -1748,7 +1696,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
bom_items: (section.bom_items || []).filter((bom: BOMItem) => bom.id !== bomId)
})));
- console.log('[ItemMasterContext] BOM 항목 삭제 성공 (양방향 동기화):', bomId);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] BOM 항목 삭제 실패:', errorMessage);
@@ -1765,7 +1712,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadIndependentSections = (sections: ItemSection[]) => {
setIndependentSections(sections);
- console.log('[ItemMasterContext] 독립 섹션 로드 완료:', sections.length);
};
/**
@@ -1773,7 +1719,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadIndependentFields = (fields: ItemField[]) => {
setIndependentFields(fields);
- console.log('[ItemMasterContext] 독립 필드 로드 완료:', fields.length);
};
/**
@@ -1781,7 +1726,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
*/
const loadIndependentBomItems = (bomItems: BOMItem[]) => {
setIndependentBomItems(bomItems);
- console.log('[ItemMasterContext] 독립 BOM 로드 완료:', bomItems.length);
};
/**
@@ -1796,7 +1740,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// API가 배열을 직접 반환하므로 바로 변환
const sections = sectionsData.map(transformSectionResponse);
setIndependentSections(sections);
- console.log('[ItemMasterContext] 독립 섹션 새로고침 완료:', sections.length);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 독립 섹션 새로고침 실패:', errorMessage);
@@ -1815,7 +1758,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// API가 배열을 직접 반환하므로 바로 변환
const fields = fieldsData.map(transformFieldResponse);
setIndependentFields(fields);
- console.log('[ItemMasterContext] 독립 필드 새로고침 완료:', fields.length);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 독립 필드 새로고침 실패:', errorMessage);
@@ -1834,7 +1776,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// API가 배열을 직접 반환하므로 바로 변환
const bomItems = bomItemsData.map(transformBomItemResponse);
setIndependentBomItems(bomItems);
- console.log('[ItemMasterContext] 독립 BOM 새로고침 완료:', bomItems.length);
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 독립 BOM 새로고침 실패:', errorMessage);
@@ -1853,7 +1794,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const newSection = transformSectionResponse(sectionData);
setIndependentSections(prev => [...prev, newSection]);
- console.log('[ItemMasterContext] 독립 섹션 생성 성공:', newSection.id);
return newSection;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -1873,7 +1813,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const newField = transformFieldResponse(fieldData);
setIndependentFields(prev => [...prev, newField]);
- console.log('[ItemMasterContext] 독립 필드 생성 성공:', newField.id);
return newField;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -1893,7 +1832,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const newBomItem = transformBomItemResponse(bomItemData);
setIndependentBomItems(prev => [...prev, newBomItem]);
- console.log('[ItemMasterContext] 독립 BOM 생성 성공:', newBomItem.id);
return newBomItem;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -1923,18 +1861,13 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 페이지 구조 새로고침
// 2025-11-26: API가 PageStructureResponse를 직접 반환 (ApiResponse 래퍼 없음)
const structureData = await itemMasterApi.pages.getStructure(pageId);
- console.log('[linkSectionToPage] getStructure 응답:', structureData);
- console.log('[linkSectionToPage] sections 배열:', structureData?.sections);
- if (structureData?.sections?.[0]) {
- console.log('[linkSectionToPage] 첫번째 섹션 원본:', structureData.sections[0]);
- console.log('[linkSectionToPage] 첫번째 섹션 키:', Object.keys(structureData.sections[0]));
- }
if (structureData && structureData.page) {
const updatedPage = transformPageResponse(structureData.page);
// getStructure API 응답 형식: { section: {...}, order_no, fields: [], bom_items: [] }
// section wrapper를 해제하고 fields/bom_items를 병합
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- API 응답 구조가 런타임에 결정됨 (wrapper/unwrapped 혼재)
updatedPage.sections = structureData.sections?.map((sectionData: any) => {
// sectionData가 wrapper 형태인지 확인
const rawSection = sectionData.section || sectionData;
@@ -1955,8 +1888,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
return transformed;
}) || [];
- console.log('[linkSectionToPage] 변환된 페이지:', updatedPage);
- console.log('[linkSectionToPage] 변환된 sections:', updatedPage.sections);
setItemPages(prev => prev.map(page => page.id === pageId ? updatedPage : page));
} else {
console.warn('[linkSectionToPage] structureData.page가 없음, 페이지 데이터를 다시 로드해주세요');
@@ -1967,7 +1898,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 독립 섹션 목록에서 제거 (연결되면 더 이상 독립이 아님)
setIndependentSections(prev => prev.filter(s => s.id !== sectionId));
- console.log('[ItemMasterContext] 섹션 연결 성공:', { pageId, sectionId });
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 연결 실패:', errorMessage);
@@ -2001,7 +1931,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
console.warn('[ItemMasterContext] 독립 섹션 새로고침 실패 (무시됨):', getErrorMessage(refreshError));
}
- console.log('[ItemMasterContext] 섹션 연결 해제 성공:', { pageId, sectionId });
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 섹션 연결 해제 실패:', errorMessage);
@@ -2071,7 +2000,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 독립 필드 목록에서 제거 (fieldData가 전달된 경우에도 실행)
setIndependentFields(prev => prev.filter(f => f.id !== fieldId));
- console.log('[ItemMasterContext] 필드 연결 성공:', { sectionId, fieldId });
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 필드 연결 실패:', errorMessage);
@@ -2111,7 +2039,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 독립 필드 목록 새로고침
await refreshIndependentFields();
- console.log('[ItemMasterContext] 필드 연결 해제 성공:', { sectionId, fieldId });
} catch (error) {
const errorMessage = getErrorMessage(error);
console.error('[ItemMasterContext] 필드 연결 해제 실패:', errorMessage);
@@ -2131,7 +2058,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 2025-11-26: API가 SectionUsageResponse를 직접 반환 (ApiResponse 래퍼 없음)
const usageData = await itemMasterApi.sections.getUsage(sectionId);
- console.log('[ItemMasterContext] 섹션 사용처 조회 성공:', sectionId);
return usageData;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -2148,7 +2074,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// 2025-11-26: API가 FieldUsageResponse를 직접 반환 (ApiResponse 래퍼 없음)
const usageData = await itemMasterApi.fields.getUsage(fieldId);
- console.log('[ItemMasterContext] 필드 사용처 조회 성공:', fieldId);
return usageData;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -2168,35 +2093,22 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
try {
// 2025-11-26: API가 ItemSectionResponse를 직접 반환 (ApiResponse 래퍼 없음)
const sectionData = await itemMasterApi.sections.clone(sectionId);
- console.log('[cloneSection] API 응답 원본:', sectionData);
const clonedSection = transformSectionResponse(sectionData);
- console.log('[cloneSection] 변환 후 섹션:', {
- id: clonedSection.id,
- title: clonedSection.title,
- page_id: clonedSection.page_id,
- section_type: clonedSection.section_type,
- fieldsCount: clonedSection.fields?.length || 0,
- fields: clonedSection.fields,
- });
// 2025-11-27: 복제된 섹션을 적절한 상태에 추가 (즉시 UI 업데이트)
// page_id가 null 또는 undefined면 독립 섹션 (API가 null 대신 undefined를 반환할 수 있음)
if (clonedSection.page_id == null) {
// 독립 섹션(일반 섹션)인 경우 independentSections에 추가
- console.log('[cloneSection] 독립 섹션 추가 (independentSections)');
setIndependentSections(prev => {
const newSections = [...prev, clonedSection];
- console.log('[cloneSection] independentSections 업데이트:', newSections.length);
return newSections;
});
} else {
// 모듈 섹션인 경우 해당 페이지의 sections에 추가
- console.log('[cloneSection] 모듈 섹션 추가 (itemPages), page_id:', clonedSection.page_id);
setItemPages(prev => {
const newPages = prev.map(page => {
if (page.id === clonedSection.page_id) {
- console.log('[cloneSection] 페이지 찾음:', page.id, '기존 섹션 수:', page.sections.length);
return {
...page,
sections: [...page.sections, clonedSection]
@@ -2208,7 +2120,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
});
}
- console.log('[ItemMasterContext] 섹션 복제 성공:', clonedSection.id);
return clonedSection;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -2232,7 +2143,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
setIndependentFields(prev => [...prev, clonedField]);
}
- console.log('[ItemMasterContext] 필드 복제 성공:', clonedField.id);
return clonedField;
} catch (error) {
const errorMessage = getErrorMessage(error);
@@ -2245,7 +2155,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
const clearCache = () => {
if (cache) {
cache.clear();
- console.log('[ItemMasterContext] TenantAwareCache cleared');
}
};
@@ -2268,7 +2177,6 @@ export function ItemMasterProvider({ children }: { children: ReactNode }) {
// TenantAwareCache도 정리
clearCache();
- console.log('[ItemMasterContext] All data reset to initial state');
};
// Context value
diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx
index c8111094..b0e9a387 100644
--- a/src/contexts/ThemeContext.tsx
+++ b/src/contexts/ThemeContext.tsx
@@ -24,12 +24,14 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
}, []);
const setTheme = (newTheme: Theme) => {
+ if (typeof window === 'undefined') return;
setThemeState(newTheme);
localStorage.setItem("theme", newTheme);
applyTheme(newTheme);
};
const applyTheme = (theme: Theme) => {
+ if (typeof window === 'undefined') return;
const root = document.documentElement;
// Remove all theme classes
diff --git a/src/hooks/useAuthGuard.ts b/src/hooks/useAuthGuard.ts
index 5788b26f..5f93d2a3 100644
--- a/src/hooks/useAuthGuard.ts
+++ b/src/hooks/useAuthGuard.ts
@@ -25,7 +25,6 @@ export function useAuthGuard() {
const handlePageShow = (event: PageTransitionEvent) => {
if (event.persisted) {
// 브라우저 캐시에서 로드된 경우 새로고침
- console.log('🔄 캐시된 페이지 감지: 새로고침');
window.location.reload();
}
};
diff --git a/src/hooks/useCEODashboard.ts b/src/hooks/useCEODashboard.ts
index 0dc412e5..b09c9df6 100644
--- a/src/hooks/useCEODashboard.ts
+++ b/src/hooks/useCEODashboard.ts
@@ -1,4 +1,3 @@
-'use client';
/**
* CEO Dashboard API 연동 Hook
diff --git a/src/hooks/useCRUDHandlers.ts b/src/hooks/useCRUDHandlers.ts
index f65c132f..a5f2fea9 100644
--- a/src/hooks/useCRUDHandlers.ts
+++ b/src/hooks/useCRUDHandlers.ts
@@ -1,4 +1,3 @@
-'use client';
import { useCallback, useState } from 'react';
import { useRouter } from 'next/navigation';
diff --git a/src/hooks/useCardManagementModals.ts b/src/hooks/useCardManagementModals.ts
index 16c1b7d9..ba6cc3a4 100644
--- a/src/hooks/useCardManagementModals.ts
+++ b/src/hooks/useCardManagementModals.ts
@@ -1,4 +1,3 @@
-'use client';
/**
* Card Management Modals Hook
diff --git a/src/hooks/useClientList.ts b/src/hooks/useClientList.ts
index e034ca7e..54ef19d0 100644
--- a/src/hooks/useClientList.ts
+++ b/src/hooks/useClientList.ts
@@ -1,4 +1,3 @@
-"use client";
import { useState, useCallback } from "react";
diff --git a/src/hooks/useCommonCodes.ts b/src/hooks/useCommonCodes.ts
index 11521c8b..186283d4 100644
--- a/src/hooks/useCommonCodes.ts
+++ b/src/hooks/useCommonCodes.ts
@@ -1,4 +1,3 @@
-'use client';
import { useState, useEffect, useCallback, useRef } from 'react';
import type { CommonCode, CommonCodeOption } from '@/lib/api/common-codes';
diff --git a/src/hooks/useDaumPostcode.ts b/src/hooks/useDaumPostcode.ts
index 9428ec15..26a08019 100644
--- a/src/hooks/useDaumPostcode.ts
+++ b/src/hooks/useDaumPostcode.ts
@@ -1,4 +1,3 @@
-'use client';
import { useCallback, useEffect, useState } from 'react';
diff --git a/src/hooks/useDeleteDialog.ts b/src/hooks/useDeleteDialog.ts
index 999191f2..96501da6 100644
--- a/src/hooks/useDeleteDialog.ts
+++ b/src/hooks/useDeleteDialog.ts
@@ -1,4 +1,3 @@
-'use client';
/**
* useDeleteDialog - 삭제 확인 다이얼로그 상태/핸들러 훅
diff --git a/src/hooks/useDetailData.ts b/src/hooks/useDetailData.ts
index 66a3abd5..6c3f3c14 100644
--- a/src/hooks/useDetailData.ts
+++ b/src/hooks/useDetailData.ts
@@ -1,4 +1,3 @@
-'use client';
import { useState, useEffect, useCallback } from 'react';
diff --git a/src/hooks/useDetailPageState.ts b/src/hooks/useDetailPageState.ts
index 5e4f877d..143b3c09 100644
--- a/src/hooks/useDetailPageState.ts
+++ b/src/hooks/useDetailPageState.ts
@@ -1,4 +1,3 @@
-'use client';
import { useState, useCallback, useMemo } from 'react';
import { useRouter, useParams, useSearchParams } from 'next/navigation';
@@ -137,6 +136,7 @@ export function useDetailPageState(
}, [router, locale, listPath]);
const goToEdit = useCallback(() => {
+ if (typeof window === 'undefined') return;
setModeState('edit');
if (id) {
// URL에 mode=edit 추가 (현재 경로 유지)
@@ -146,6 +146,7 @@ export function useDetailPageState(
}, [router, id]);
const goToView = useCallback(() => {
+ if (typeof window === 'undefined') return;
setModeState('view');
if (id) {
// URL에서 mode 파라미터 제거
diff --git a/src/hooks/useDetailPermissions.ts b/src/hooks/useDetailPermissions.ts
index 468ac33d..e351cd34 100644
--- a/src/hooks/useDetailPermissions.ts
+++ b/src/hooks/useDetailPermissions.ts
@@ -1,4 +1,3 @@
-'use client';
import { useMemo } from 'react';
import { usePermission } from './usePermission';
diff --git a/src/hooks/useFCM.ts b/src/hooks/useFCM.ts
index 228e568d..1b0aa2d8 100644
--- a/src/hooks/useFCM.ts
+++ b/src/hooks/useFCM.ts
@@ -1,4 +1,3 @@
-'use client';
/**
* FCM 푸시 알림 훅
@@ -82,7 +81,6 @@ export function useFCM() {
useEffect(() => {
// 네이티브 환경이 아니면 무시
if (!isCapacitorNative()) {
- console.log('[useFCM] Not in native environment, skipping');
return;
}
@@ -93,19 +91,14 @@ export function useFCM() {
// 로그인 상태가 아니면 무시
if (!hasAuthToken()) {
- console.log('[useFCM] No auth token, skipping FCM initialization');
return;
}
// FCM 초기화
initialized.current = true;
- console.log('[useFCM] Initializing FCM...');
initializeFCM(handleForegroundNotification).then((success) => {
- if (success) {
- console.log('[useFCM] FCM initialized successfully');
- } else {
- console.log('[useFCM] FCM initialization failed or skipped');
+ if (!success) {
initialized.current = false;
}
});
@@ -115,7 +108,6 @@ export function useFCM() {
* FCM 토큰 해제 (로그아웃 시 호출)
*/
const cleanup = useCallback(async () => {
- console.log('[useFCM] Cleaning up FCM...');
await unregisterFCMToken();
initialized.current = false;
}, []);
diff --git a/src/hooks/useItemList.ts b/src/hooks/useItemList.ts
index 0af6b56e..80a07fe9 100644
--- a/src/hooks/useItemList.ts
+++ b/src/hooks/useItemList.ts
@@ -5,7 +5,6 @@
* - custom-tabs 기반 동적 테이블 컬럼
*/
-'use client';
import { useState, useEffect, useCallback, useRef } from 'react';
import type { ItemMaster, ItemType, PartType } from '@/types/item';
diff --git a/src/hooks/useListHandlers.ts b/src/hooks/useListHandlers.ts
index 7140f915..e0bfe41c 100644
--- a/src/hooks/useListHandlers.ts
+++ b/src/hooks/useListHandlers.ts
@@ -1,4 +1,3 @@
-'use client';
import { useCallback } from 'react';
import { useRouter } from 'next/navigation';
diff --git a/src/hooks/useMenuPolling.ts b/src/hooks/useMenuPolling.ts
index 90ab4bc3..b1175c04 100644
--- a/src/hooks/useMenuPolling.ts
+++ b/src/hooks/useMenuPolling.ts
@@ -146,11 +146,9 @@ export function useMenuPolling(options: UseMenuPollingOptions = {}): UseMenuPoll
// 401 세션 만료 응답
if (result.sessionExpired) {
sessionExpiredCountRef.current += 1;
- console.log(`[Menu] 401 응답 (${sessionExpiredCountRef.current}/${MAX_SESSION_EXPIRED_COUNT})`);
// 3회 연속 401 → 폴링 중지
if (sessionExpiredCountRef.current >= MAX_SESSION_EXPIRED_COUNT) {
- console.log('[Menu] 세션 만료로 폴링 중지');
isSessionExpiredRef.current = true;
stopPolling();
onSessionExpiredRef.current?.();
@@ -179,7 +177,6 @@ export function useMenuPolling(options: UseMenuPollingOptions = {}): UseMenuPoll
const resume = useCallback(() => {
// 세션 만료 상태면 재개 불가 (restartAfterAuth 사용)
if (isSessionExpiredRef.current) {
- console.log('[Menu] 세션 만료 상태 - resume 불가, restartAfterAuth 사용 필요');
return;
}
@@ -191,7 +188,6 @@ export function useMenuPolling(options: UseMenuPollingOptions = {}): UseMenuPoll
// 인증 성공 후 폴링 재시작 (401 카운트 리셋)
const restartAfterAuth = useCallback(() => {
- console.log('[Menu] 인증 성공 - 폴링 재시작');
sessionExpiredCountRef.current = 0;
isSessionExpiredRef.current = false;
isPausedRef.current = false;
@@ -263,7 +259,6 @@ export function useMenuPolling(options: UseMenuPollingOptions = {}): UseMenuPoll
// 세션 만료 상태였다면 폴링 재시작
if (isSessionExpiredRef.current) {
- console.log('[Menu] 🔔 토큰 갱신 감지 - 폴링 재시작');
sessionExpiredCountRef.current = 0;
isSessionExpiredRef.current = false;
isPausedRef.current = false;
diff --git a/src/hooks/usePermission.ts b/src/hooks/usePermission.ts
index 103063ad..ba807be6 100644
--- a/src/hooks/usePermission.ts
+++ b/src/hooks/usePermission.ts
@@ -1,4 +1,3 @@
-'use client';
import { usePathname } from 'next/navigation';
import { usePermissionContext } from '@/contexts/PermissionContext';
diff --git a/src/hooks/useStatsLoader.ts b/src/hooks/useStatsLoader.ts
index 6bac5d71..e1454669 100644
--- a/src/hooks/useStatsLoader.ts
+++ b/src/hooks/useStatsLoader.ts
@@ -1,4 +1,3 @@
-'use client';
import { useState, useEffect, useCallback, useRef } from 'react';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
diff --git a/src/layouts/AuthenticatedLayout.tsx b/src/layouts/AuthenticatedLayout.tsx
index f0932550..4aa09009 100644
--- a/src/layouts/AuthenticatedLayout.tsx
+++ b/src/layouts/AuthenticatedLayout.tsx
@@ -161,17 +161,45 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
setIsMounted(true);
}, []);
+ // 🔧 스크롤바 자동 숨김 - 스크롤 시에만 스크롤바 thumb 표시
+ useEffect(() => {
+ const scrollTimers = new WeakMap>();
+
+ const handleScroll = (e: Event) => {
+ const target = e.target === document
+ ? document.documentElement
+ : e.target as Element;
+ if (!target || !(target instanceof Element)) return;
+
+ target.classList.add('is-scrolling');
+
+ const existing = scrollTimers.get(target);
+ if (existing) clearTimeout(existing);
+
+ const timer = setTimeout(() => {
+ target.classList.remove('is-scrolling');
+ scrollTimers.delete(target);
+ }, 1500);
+
+ scrollTimers.set(target, timer);
+ };
+
+ document.addEventListener('scroll', handleScroll, { capture: true, passive: true });
+
+ return () => {
+ document.removeEventListener('scroll', handleScroll, { capture: true });
+ };
+ }, []);
+
// 토큰 갱신 함수
const refreshToken = useCallback(async (): Promise => {
try {
- console.log('[Notification] 토큰 갱신 시도...');
const response = await fetch('/api/auth/refresh', {
method: 'POST',
credentials: 'include',
});
if (response.ok) {
- console.log('[Notification] 토큰 갱신 성공');
return true;
} else {
console.warn('[Notification] 토큰 갱신 실패:', response.status);
@@ -295,10 +323,8 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
enabled: true,
interval: 30000, // 30초
onMenuUpdated: () => {
- console.log('[Menu] 메뉴가 업데이트되었습니다');
},
onSessionExpired: () => {
- console.log('[Menu] 세션 만료로 폴링 중지됨');
},
});
@@ -306,7 +332,6 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
useEffect(() => {
const justLoggedIn = sessionStorage.getItem('auth_just_logged_in');
if (justLoggedIn === 'true') {
- console.log('[Menu] 로그인 감지 - 폴링 재시작');
sessionStorage.removeItem('auth_just_logged_in');
restartAfterAuth();
}
diff --git a/src/lib/api/authenticated-fetch.ts b/src/lib/api/authenticated-fetch.ts
index 0439bab2..a84bc9d2 100644
--- a/src/lib/api/authenticated-fetch.ts
+++ b/src/lib/api/authenticated-fetch.ts
@@ -61,7 +61,6 @@ export async function authenticatedFetch(
}
// 4. 401 + refresh_token 있음 → 갱신 시도 (globalThis 캐시로 중복 방지)
- console.log(`🔄 [${caller}] Got 401, attempting token refresh...`);
const refreshResult = await refreshAccessToken(refreshToken, caller);
if (!refreshResult.success || !refreshResult.accessToken) {
@@ -70,7 +69,6 @@ export async function authenticatedFetch(
}
// 5. 새 토큰으로 재시도
- console.log(`✅ [${caller}] Token refreshed, retrying...`);
const retryHeaders = new Headers(options.headers || {});
retryHeaders.set('Authorization', `Bearer ${refreshResult.accessToken}`);
@@ -79,7 +77,6 @@ export async function authenticatedFetch(
headers: retryHeaders,
});
- console.log(`🔵 [${caller}] Retry status: ${retryResponse.status}`);
// 6. 재시도도 401 → 인증 실패
if (retryResponse.status === 401) {
diff --git a/src/lib/api/client.ts b/src/lib/api/client.ts
index f6b65adc..7f7203b1 100644
--- a/src/lib/api/client.ts
+++ b/src/lib/api/client.ts
@@ -208,7 +208,6 @@ export async function withTokenRefresh(
// If 401 and token refresh needed, try refreshing
if (apiError.status === 401 && apiError.needsTokenRefresh && maxRetries > 0) {
- console.log('🔄 Attempting token refresh...');
// Call refresh endpoint
const refreshResponse = await fetch('/api/auth/refresh', {
@@ -217,7 +216,6 @@ export async function withTokenRefresh(
});
if (refreshResponse.ok) {
- console.log('✅ Token refreshed, retrying API call');
// Retry the original API call
return withTokenRefresh(apiCall, maxRetries - 1);
} else {
diff --git a/src/lib/api/fetch-wrapper.ts b/src/lib/api/fetch-wrapper.ts
index 4a543b7e..af3b2b04 100644
--- a/src/lib/api/fetch-wrapper.ts
+++ b/src/lib/api/fetch-wrapper.ts
@@ -27,7 +27,6 @@ async function clearTokenCookies() {
cookieStore.delete('token_refreshed_at');
cookieStore.delete('is_authenticated');
- console.log('🗑️ [serverFetch] 토큰 쿠키 삭제 완료');
}
/**
@@ -58,7 +57,6 @@ async function setNewTokenCookies(tokens: {
path: '/',
maxAge: 60, // 1분 후 자동 삭제
});
- console.log('🔔 [setNewTokenCookies] token_refreshed_at 신호 쿠키 설정');
}
if (tokens.refreshToken) {
diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts
index d540f62c..a916cdf4 100644
--- a/src/lib/api/index.ts
+++ b/src/lib/api/index.ts
@@ -80,7 +80,6 @@ class ServerApiClient {
cookieStore.delete('refresh_token');
cookieStore.delete('token_refreshed_at');
cookieStore.delete('is_authenticated');
- console.log('🗑️ [ServerApiClient] 토큰 쿠키 삭제 완료');
}
/**
diff --git a/src/lib/api/items.ts b/src/lib/api/items.ts
index 4aa3830d..9a00d296 100644
--- a/src/lib/api/items.ts
+++ b/src/lib/api/items.ts
@@ -160,7 +160,6 @@ export async function fetchItems(
const result = await response.json();
// 디버깅: API 응답 구조 확인
- console.log('[fetchItems] API 응답:', JSON.stringify(result, null, 2).slice(0, 1000));
// 데이터 추출
let rawItems: ApiItemResponse[] = [];
@@ -180,19 +179,9 @@ export async function fetchItems(
rawItems = result;
}
- // 디버깅: rawItems 첫 번째 항목 확인
- if (rawItems.length > 0) {
- console.log('[fetchItems] rawItems[0]:', rawItems[0]);
- }
-
// snake_case → camelCase 변환
const transformed = rawItems.map(transformItemFromApi);
- // 디버깅: 변환된 첫 번째 항목 확인
- if (transformed.length > 0) {
- console.log('[fetchItems] transformed[0]:', transformed[0]);
- }
-
return transformed;
}
@@ -810,7 +799,6 @@ export async function fetchItemPrices(
}
const result = await response.json();
- console.log('[fetchItemPrices] API 응답:', result);
if (result.success && result.data) {
return result.data as Record;
diff --git a/src/lib/api/refresh-token.ts b/src/lib/api/refresh-token.ts
index 8e52416a..1e8aa7c2 100644
--- a/src/lib/api/refresh-token.ts
+++ b/src/lib/api/refresh-token.ts
@@ -51,11 +51,6 @@ const REFRESH_CACHE_TTL = 5000; // 5초
async function doRefreshToken(refreshToken: string): Promise {
try {
const refreshUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/refresh`;
- console.log('🔄 [RefreshToken] Refresh request:', {
- url: refreshUrl,
- hasApiKey: !!process.env.API_KEY,
- refreshTokenLength: refreshToken?.length,
- });
const response = await fetch(refreshUrl, {
method: 'POST',
@@ -80,7 +75,6 @@ async function doRefreshToken(refreshToken: string): Promise {
}
const data = await response.json();
- console.log('✅ [RefreshToken] Token refreshed successfully');
return {
success: true,
@@ -114,25 +108,21 @@ export async function refreshAccessToken(
// 1. 캐시된 성공 결과가 유효하면 즉시 반환
if (cache.result && cache.result.success && now - cache.timestamp < REFRESH_CACHE_TTL) {
- console.log(`🔵 [${caller}] Using cached refresh result (age: ${now - cache.timestamp}ms)`);
return cache.result;
}
// 2. 진행 중인 refresh가 있고, 아직 결과가 없으면 기다림 (실패한 결과는 캐시 안 함)
if (cache.promise && !cache.result && now - cache.timestamp < REFRESH_CACHE_TTL) {
- console.log(`🔵 [${caller}] Waiting for ongoing refresh...`);
return cache.promise;
}
// 2-1. 이전 refresh가 실패했으면 캐시 초기화
if (cache.result && !cache.result.success) {
- console.log(`🔄 [${caller}] Previous refresh failed, clearing cache...`);
cache.promise = null;
cache.result = null;
}
// 3. 새 refresh 시작
- console.log(`🔄 [${caller}] Starting new refresh request...`);
cache.timestamp = now;
cache.result = null;
diff --git a/src/lib/auth/logout.ts b/src/lib/auth/logout.ts
index 940de6f1..a185548c 100644
--- a/src/lib/auth/logout.ts
+++ b/src/lib/auth/logout.ts
@@ -52,12 +52,7 @@ export function clearSessionStorageCache(): void {
keysToRemove.forEach((key) => {
sessionStorage.removeItem(key);
- console.log(`[Logout] Cleared sessionStorage: ${key}`);
});
-
- if (keysToRemove.length > 0) {
- console.log(`[Logout] Cleared ${keysToRemove.length} sessionStorage items`);
- }
}
/**
@@ -70,7 +65,6 @@ export function clearLocalStorageCache(): void {
LOCAL_STORAGE_KEYS.forEach((key) => {
if (localStorage.getItem(key)) {
localStorage.removeItem(key);
- console.log(`[Logout] Cleared localStorage: ${key}`);
}
});
@@ -85,12 +79,7 @@ export function clearLocalStorageCache(): void {
keysToRemove.forEach((key) => {
localStorage.removeItem(key);
- console.log(`[Logout] Cleared localStorage: ${key}`);
});
-
- if (keysToRemove.length > 0) {
- console.log(`[Logout] Cleared ${keysToRemove.length} localStorage items`);
- }
}
/**
@@ -101,12 +90,10 @@ export function resetZustandStores(): void {
// masterDataStore 초기화
const masterDataStore = useMasterDataStore.getState();
masterDataStore.reset();
- console.log('[Logout] Reset masterDataStore');
// itemStore 초기화
const itemStore = useItemStore.getState();
itemStore.reset();
- console.log('[Logout] Reset itemStore');
} catch (error) {
console.error('[Logout] Failed to reset Zustand stores:', error);
}
@@ -130,7 +117,6 @@ export async function callLogoutAPI(): Promise {
return false;
}
- console.log('[Logout] Server logout successful');
return true;
} catch (error) {
console.error('[Logout] Server logout error:', error);
@@ -160,7 +146,6 @@ export async function performFullLogout(options?: {
}): Promise {
const { skipServerLogout = false, redirectTo = null } = options || {};
- console.log('[Logout] Starting full logout...');
try {
// 1. Zustand 스토어 초기화 (즉시 UI 반영)
@@ -178,11 +163,9 @@ export async function performFullLogout(options?: {
const fcm = await import('@/lib/capacitor/fcm');
if (fcm.isCapacitorNative()) {
await fcm.unregisterFCMToken();
- console.log('[Logout] FCM token unregistered');
}
} catch {
// Capacitor 모듈이 없는 환경 (웹) - 무시
- console.log('[Logout] Skipping FCM (not in native app)');
}
// 5. 서버 로그아웃 API 호출 (HttpOnly 쿠키 삭제)
@@ -190,7 +173,6 @@ export async function performFullLogout(options?: {
await callLogoutAPI();
}
- console.log('[Logout] Full logout completed successfully');
// 6. 리다이렉트 (선택적)
if (redirectTo && typeof window !== 'undefined') {
@@ -211,7 +193,6 @@ export async function performFullLogout(options?: {
*/
export function debugCacheStatus(): void {
if (typeof window === 'undefined') {
- console.log('[Debug] Server environment - no cache');
return;
}
@@ -221,7 +202,6 @@ export function debugCacheStatus(): void {
const sessionKeys = Object.keys(sessionStorage).filter((key) =>
SESSION_STORAGE_PREFIXES.some((prefix) => key.startsWith(prefix))
);
- console.log('sessionStorage:', sessionKeys);
// localStorage
const localKeys = Object.keys(localStorage).filter(
@@ -229,14 +209,11 @@ export function debugCacheStatus(): void {
LOCAL_STORAGE_KEYS.includes(key) ||
LOCAL_STORAGE_PREFIXES.some((prefix) => key.startsWith(prefix))
);
- console.log('localStorage:', localKeys);
// Zustand
const masterDataState = useMasterDataStore.getState();
- console.log('masterDataStore.pageConfigs:', Object.keys(masterDataState.pageConfigs));
const itemState = useItemStore.getState();
- console.log('itemStore.items:', itemState.items.length);
console.groupEnd();
}
\ No newline at end of file
diff --git a/src/lib/auth/token-refresh.ts b/src/lib/auth/token-refresh.ts
index 7dc994f3..29f7cacf 100644
--- a/src/lib/auth/token-refresh.ts
+++ b/src/lib/auth/token-refresh.ts
@@ -28,7 +28,6 @@ export async function refreshTokenClient(): Promise {
return false;
}
- console.log('✅ Token refreshed successfully');
return true;
} catch (error) {
diff --git a/src/lib/cache/TenantAwareCache.ts b/src/lib/cache/TenantAwareCache.ts
index b6ff6c99..0598c9f1 100644
--- a/src/lib/cache/TenantAwareCache.ts
+++ b/src/lib/cache/TenantAwareCache.ts
@@ -85,10 +85,8 @@ export class TenantAwareCache {
* @example
* const data = cache.get('itemMasters');
* if (data) {
- * console.log('캐시 히트:', data);
- * } else {
- * console.log('캐시 미스 - API 호출 필요');
- * }
+ * * } else {
+ * * }
*/
get(key: string): T | null {
const cached = this.storage.getItem(this.getKey(key));
@@ -185,9 +183,7 @@ export class TenantAwareCache {
* @example
* const meta = cache.getMetadata('itemMasters');
* if (meta) {
- * console.log('저장 시간:', new Date(meta.timestamp));
- * console.log('버전:', meta.version);
- * }
+ * * * }
*/
getMetadata(key: string): { tenantId: number; timestamp: number; version?: string } | null {
const cached = this.storage.getItem(this.getKey(key));
@@ -239,8 +235,7 @@ export class TenantAwareCache {
*
* @example
* const stats = cache.getStats();
- * console.log(`캐시 ${stats.count}개, 총 ${stats.totalSize} bytes`);
- */
+ * */
getStats(): { count: number; totalSize: number; keys: string[] } {
const prefix = `mes-${this.tenantId}-`;
const keys: string[] = [];
diff --git a/src/lib/capacitor/fcm.ts b/src/lib/capacitor/fcm.ts
index 1c645908..32011a35 100644
--- a/src/lib/capacitor/fcm.ts
+++ b/src/lib/capacitor/fcm.ts
@@ -118,32 +118,27 @@ export async function initializeFCM(
): Promise {
// 브라우저 환경 체크
if (typeof window === 'undefined') {
- console.log('[FCM] Not running in browser');
return false;
}
// Capacitor 전역 객체 체크
if (!window.Capacitor) {
- console.log('[FCM] Capacitor not available (web environment)');
return false;
}
// 네이티브 환경 체크
const platform = window.Capacitor.getPlatform();
if (platform !== 'ios' && platform !== 'android') {
- console.log('[FCM] Not running in native app (platform:', platform, ')');
return false;
}
// PushNotifications 플러그인 체크
const PushNotifications = window.Capacitor.Plugins?.PushNotifications;
if (!PushNotifications) {
- console.log('[FCM] PushNotifications plugin not available');
return false;
}
if (isInitialized) {
- console.log('[FCM] Already initialized');
return true;
}
@@ -153,7 +148,6 @@ export async function initializeFCM(
if (App) {
App.addListener('appStateChange', ({ isActive }) => {
isAppForeground = isActive;
- console.log('[FCM] App state:', isActive ? 'foreground' : 'background');
});
}
@@ -163,8 +157,6 @@ export async function initializeFCM(
// 리스너 등록 (register() 호출 전에 등록해야 함)
PushNotifications.addListener('registration', async (tokenData: unknown) => {
const token = (tokenData as { value: string })?.value;
- console.log('[FCM] 🔥 registration event fired');
- console.log('[FCM] Token received:', token?.substring(0, 20) + '...');
await handleTokenRegistration(token);
});
@@ -173,7 +165,6 @@ export async function initializeFCM(
});
PushNotifications.addListener('pushNotificationReceived', (notification: unknown) => {
- console.log('[FCM] Push received (foreground):', notification);
const notif = notification as { title?: string; body?: string; data?: PushNotificationData };
const fcmNotification: FCMNotification = {
@@ -192,7 +183,6 @@ export async function initializeFCM(
});
PushNotifications.addListener('pushNotificationActionPerformed', (action: unknown) => {
- console.log('[FCM] Push action performed:', action);
const actionData = action as { notification?: { data?: { url?: string } } };
const url = actionData.notification?.data?.url;
if (url && typeof url === 'string') {
@@ -203,10 +193,8 @@ export async function initializeFCM(
// 권한 요청
const perm = await PushNotifications.requestPermissions();
- console.log('[FCM] Push permission:', perm.receive);
if (perm.receive !== 'granted') {
- console.log('[FCM] Push permission not granted');
return false;
}
@@ -214,7 +202,6 @@ export async function initializeFCM(
await PushNotifications.register();
isInitialized = true;
- console.log('[FCM] Initialization completed');
return true;
} catch (error) {
@@ -232,7 +219,6 @@ async function handleTokenRegistration(newToken: string): Promise {
const oldToken = sessionStorage.getItem(CONFIG.fcmTokenKey);
if (oldToken === newToken) {
- console.log('[FCM] Token unchanged, skip');
return;
}
@@ -240,7 +226,6 @@ async function handleTokenRegistration(newToken: string): Promise {
if (success) {
sessionStorage.setItem(CONFIG.fcmTokenKey, newToken);
- console.log('[FCM] Token saved to sessionStorage');
}
}
@@ -266,7 +251,6 @@ async function registerTokenToServer(token: string): Promise {
});
if (response.ok) {
- console.log('[FCM] Token registered successfully');
return true;
}
@@ -305,7 +289,6 @@ export async function unregisterFCMToken(): Promise {
sessionStorage.removeItem(CONFIG.fcmTokenKey);
isInitialized = false;
- console.log('[FCM] Token unregistered');
return true;
}
diff --git a/src/lib/utils/excel-download.ts b/src/lib/utils/excel-download.ts
index 8feadf2f..97fb8ec9 100644
--- a/src/lib/utils/excel-download.ts
+++ b/src/lib/utils/excel-download.ts
@@ -153,7 +153,6 @@ export function downloadExcel>({
const finalFilename = generateFilename(filename, appendDate);
XLSX.writeFile(wb, finalFilename);
- console.log(`[Excel] 다운로드 완료: ${finalFilename} (${data.length}건)`);
} catch (error) {
console.error('[Excel] 다운로드 실패:', error);
throw new Error('엑셀 파일 생성에 실패했습니다.');
@@ -330,7 +329,6 @@ export function downloadExcelTemplate({
const finalFilename = `${filename}.xlsx`;
XLSX.writeFile(wb, finalFilename);
- console.log(`[Excel] 템플릿 다운로드 완료: ${finalFilename}`);
} catch (error) {
console.error('[Excel] 템플릿 다운로드 실패:', error);
throw new Error('엑셀 템플릿 생성에 실패했습니다.');
diff --git a/src/lib/utils/menuRefresh.ts b/src/lib/utils/menuRefresh.ts
index 61a9eaa1..7a1f0168 100644
--- a/src/lib/utils/menuRefresh.ts
+++ b/src/lib/utils/menuRefresh.ts
@@ -79,7 +79,6 @@ export async function refreshMenus(): Promise {
if (!response.ok) {
// 401 인증 오류 → 세션 만료로 판단
if (response.status === 401) {
- console.log('[Menu] 401 응답 - 세션 만료');
return { success: false, updated: false, sessionExpired: true };
}
return {
@@ -126,7 +125,6 @@ export async function refreshMenus(): Promise {
const { setMenuItems } = useMenuStore.getState();
setMenuItems(deserializeMenuItems(transformedMenus));
- console.log('[Menu] 메뉴 갱신 완료 - 변경 감지됨');
return { success: true, updated: true };
} catch (error) {
@@ -189,7 +187,6 @@ export async function forceRefreshMenus(): Promise {
const { setMenuItems } = useMenuStore.getState();
setMenuItems(deserializeMenuItems(transformedMenus));
- console.log('[Menu] 메뉴 강제 갱신 완료');
return { success: true, updated: true };
} catch (error) {
diff --git a/src/middleware.ts b/src/middleware.ts
index 9c91b27a..83672782 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -211,7 +211,6 @@ export async function middleware(request: NextRequest) {
if (isInternetExplorer(userAgent)) {
// unsupported-browser.html 페이지 자체는 제외 (무한 리다이렉트 방지)
if (!pathname.includes('unsupported-browser')) {
- console.log(`[IE Blocked] ${userAgent} attempted to access ${pathname}`);
return NextResponse.redirect(new URL('/unsupported-browser.html', request.url));
}
}
@@ -224,7 +223,6 @@ export async function middleware(request: NextRequest) {
// Bot Detection: Block bots from protected paths
if (isBotRequest && (isProtectedPath(pathname) || isProtectedPath(pathnameWithoutLocale))) {
- console.log(`[Bot Blocked] ${userAgent} attempted to access ${pathname}`);
return new NextResponse(
JSON.stringify({
@@ -257,7 +255,6 @@ export async function middleware(request: NextRequest) {
// 4.5️⃣ MVP: /signup 접근 차단 → /login 리다이렉트 (2025-12-04)
// 회원가입 기능은 운영 페이지로 이동 예정
if (pathnameWithoutLocale === '/signup' || pathnameWithoutLocale.startsWith('/signup/')) {
- console.log(`[Signup Blocked] Redirecting to /login from ${pathname}`);
return NextResponse.redirect(new URL('/login', request.url));
}
@@ -269,7 +266,6 @@ export async function middleware(request: NextRequest) {
// refresh_token만 있는 경우: /dashboard에서 PROXY가 갱신 처리
// 만약 refresh_token도 만료되었다면 /dashboard에서 API 실패 → /login으로 리다이렉트됨
if (isAuthenticated) {
- console.log(`[Already Authenticated] Redirecting to /dashboard from ${pathname}`);
return NextResponse.redirect(new URL(AUTH_CONFIG.redirects.afterLogin, request.url));
}
@@ -285,10 +281,6 @@ export async function middleware(request: NextRequest) {
// 7️⃣ 기본 정책: 모든 페이지는 인증 필요
// guestOnlyRoutes와 publicRoutes가 아닌 모든 경로는 보호됨
if (!isAuthenticated) {
- if (process.env.NODE_ENV === 'development') {
- console.log(`[Auth Required] Redirecting to /login from ${pathname}`);
- }
-
const url = new URL('/login', request.url);
// Open Redirect 방지: 내부 경로만 허용
const isInternalPath = pathname.startsWith('/') && !pathname.startsWith('//') && !pathname.includes('://');
@@ -298,12 +290,7 @@ export async function middleware(request: NextRequest) {
return NextResponse.redirect(url);
}
- // 8️⃣ 인증 모드 로깅 (디버깅용 - 개발환경만)
- if (process.env.NODE_ENV === 'development' && isAuthenticated) {
- console.log(`[Authenticated] Mode: ${authMode}, Path: ${pathname}`);
- }
-
- // 9️⃣ i18n 미들웨어 실행
+ // 8️⃣ i18n 미들웨어 실행
const intlResponse = intlMiddleware(request);
// 🔟 보안 헤더 추가
@@ -323,11 +310,6 @@ export async function middleware(request: NextRequest) {
"form-action 'self'",
].join('; '));
- // Bot 로깅 (모니터링용)
- if (isBotRequest) {
- console.log(`[Bot Allowed] ${userAgent} accessed ${pathname}`);
- }
-
return intlResponse;
}
diff --git a/src/stores/item-master/useItemMasterStore.ts b/src/stores/item-master/useItemMasterStore.ts
index 2f8ce512..c70712ce 100644
--- a/src/stores/item-master/useItemMasterStore.ts
+++ b/src/stores/item-master/useItemMasterStore.ts
@@ -124,7 +124,6 @@ export const useItemMasterStore = create()(
updatePage: async (id, updates) => {
try {
- console.log('[ItemMasterStore] updatePage 시작:', { id, updates });
// ✅ Phase 3: API 연동
const apiData = denormalizePageForRequest(updates);
@@ -147,7 +146,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updatePage 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updatePage 실패:', error);
set((state) => {
@@ -253,7 +251,6 @@ export const useItemMasterStore = create()(
updateSection: async (id, updates) => {
try {
- console.log('[ItemMasterStore] updateSection 시작:', { id, updates });
// ✅ Phase 3: API 연동
const apiData = denormalizeSectionForRequest(updates);
@@ -278,7 +275,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updateSection 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updateSection 실패:', error);
set((state) => {
@@ -425,7 +421,6 @@ export const useItemMasterStore = create()(
});
// TODO: API 호출하여 서버에도 순서 저장
- console.log('[ItemMasterStore] reorderSections 완료:', { pageId, dragSectionId, hoverSectionId });
} catch (error) {
console.error('[ItemMasterStore] reorderSections 실패:', error);
set((state) => {
@@ -513,7 +508,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updateField 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updateField 실패:', error);
set((state) => {
@@ -644,7 +638,6 @@ export const useItemMasterStore = create()(
});
// TODO: API 호출하여 서버에도 순서 저장
- console.log('[ItemMasterStore] reorderFields 완료:', { sectionId, dragFieldId, hoverFieldId });
} catch (error) {
console.error('[ItemMasterStore] reorderFields 실패:', error);
set((state) => {
@@ -827,7 +820,6 @@ export const useItemMasterStore = create()(
state.references.itemUnits.push(newUnit);
});
- console.log('[ItemMasterStore] addUnit 완료:', newUnit);
return newUnit;
} catch (error) {
console.error('[ItemMasterStore] addUnit 실패:', error);
@@ -866,7 +858,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updateUnit 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updateUnit 실패:', error);
throw error;
@@ -881,7 +872,6 @@ export const useItemMasterStore = create()(
state.references.itemUnits = state.references.itemUnits.filter((u) => u.id !== id);
});
- console.log('[ItemMasterStore] deleteUnit 완료:', id);
} catch (error) {
console.error('[ItemMasterStore] deleteUnit 실패:', error);
throw error;
@@ -923,7 +913,6 @@ export const useItemMasterStore = create()(
state.references.itemMaterials.push(newMaterial);
});
- console.log('[ItemMasterStore] addMaterial 완료:', newMaterial);
return newMaterial;
} catch (error) {
console.error('[ItemMasterStore] addMaterial 실패:', error);
@@ -968,7 +957,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updateMaterial 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updateMaterial 실패:', error);
throw error;
@@ -983,7 +971,6 @@ export const useItemMasterStore = create()(
state.references.itemMaterials = state.references.itemMaterials.filter((m) => m.id !== id);
});
- console.log('[ItemMasterStore] deleteMaterial 완료:', id);
} catch (error) {
console.error('[ItemMasterStore] deleteMaterial 실패:', error);
throw error;
@@ -1022,7 +1009,6 @@ export const useItemMasterStore = create()(
state.references.surfaceTreatments.push(newTreatment);
});
- console.log('[ItemMasterStore] addTreatment 완료:', newTreatment);
return newTreatment;
} catch (error) {
console.error('[ItemMasterStore] addTreatment 실패:', error);
@@ -1064,7 +1050,6 @@ export const useItemMasterStore = create()(
}
});
- console.log('[ItemMasterStore] updateTreatment 완료:', { id, updates });
} catch (error) {
console.error('[ItemMasterStore] updateTreatment 실패:', error);
throw error;
@@ -1079,7 +1064,6 @@ export const useItemMasterStore = create()(
state.references.surfaceTreatments = state.references.surfaceTreatments.filter((t) => t.id !== id);
});
- console.log('[ItemMasterStore] deleteTreatment 완료:', id);
} catch (error) {
console.error('[ItemMasterStore] deleteTreatment 실패:', error);
throw error;
@@ -1101,7 +1085,6 @@ export const useItemMasterStore = create()(
state.ids.independentSections.push(clonedSection.id);
});
- console.log('[ItemMasterStore] cloneSection 완료:', clonedSection);
return clonedSection;
} catch (error) {
console.error('[ItemMasterStore] cloneSection 실패:', error);
@@ -1137,12 +1120,6 @@ export const useItemMasterStore = create()(
state.ui.isLoading = false;
});
- console.log('[ItemMasterStore] initFromApi 완료:', {
- pages: Object.keys(normalized.entities.pages).length,
- sections: Object.keys(normalized.entities.sections).length,
- fields: Object.keys(normalized.entities.fields).length,
- bomItems: Object.keys(normalized.entities.bomItems).length,
- });
} catch (error) {
console.error('[ItemMasterStore] initFromApi 실패:', error);
set((state) => {
diff --git a/src/stores/masterDataStore.ts b/src/stores/masterDataStore.ts
index 46ab0544..1414165f 100644
--- a/src/stores/masterDataStore.ts
+++ b/src/stores/masterDataStore.ts
@@ -183,26 +183,22 @@ export const useMasterDataStore = create()(
// 🔒 이미 로딩 중이면 중복 요청 방지
if (loading[pageType]) {
- console.log(`⏳ [Already Loading] ${pageType}`);
return pageConfigs[pageType] || null;
}
// 🔒 최근에 에러가 발생했으면 재시도 방지 (캐시 무효화 전까지)
if (errors[pageType] && pageConfigs[pageType] === null) {
- console.log(`🚫 [Skip - Previous Error] ${pageType}: ${errors[pageType]}`);
return null;
}
// 1️⃣ 메모리 캐시 확인 (Zustand)
if (pageConfigs[pageType]) {
- console.log(`✅ [Cache Hit - Memory] ${pageType}`);
return pageConfigs[pageType];
}
// 2️⃣ sessionStorage 확인
const cachedConfig = getConfigFromSessionStorage(currentTenantId, pageType);
if (cachedConfig) {
- console.log(`✅ [Cache Hit - Session] ${pageType}`);
// 메모리에 저장
set(
@@ -220,7 +216,6 @@ export const useMasterDataStore = create()(
}
// 3️⃣ API 요청 (Redis/Database)
- console.log(`🌐 [API Request] ${pageType}`);
try {
get().setLoading(pageType, true);
@@ -261,7 +256,6 @@ export const useMasterDataStore = create()(
setConfigToSessionStorage(currentTenantId, pageType, config);
- console.log(`✅ [Config Loaded] ${pageType}`, config);
return config;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '페이지 구성 조회 실패';
@@ -302,7 +296,6 @@ export const useMasterDataStore = create()(
removeConfigFromSessionStorage(currentTenantId, pageType);
- console.log(`🗑️ [Cache Invalidated] ${pageType}`);
},
invalidateAllConfigs: () => {
@@ -324,7 +317,6 @@ export const useMasterDataStore = create()(
console.error('[Cache Invalidation Error]:', error);
});
- console.log('🗑️ [All Caches Invalidated]');
},
// ===== 페이지 타입 선택 =====
@@ -404,7 +396,6 @@ export const useMasterDataStore = create()(
window.sessionStorage.removeItem(key);
}
});
- console.log('[masterDataStore] Reset: cleared memory and sessionStorage cache');
}
},
}),
diff --git a/src/types/item-master.types.ts b/src/types/item-master.types.ts
index dbb3ac39..5384cf50 100644
--- a/src/types/item-master.types.ts
+++ b/src/types/item-master.types.ts
@@ -69,7 +69,7 @@ export interface ItemRevision {
revisionDate: string; // 수정일
revisionBy: string; // 수정자
revisionReason?: string; // 수정 사유
- previousData: any; // 이전 버전의 전체 데이터
+ previousData: Record; // 이전 버전의 전체 데이터
}
// 품목 마스터
diff --git a/src/types/item.ts b/src/types/item.ts
index b6b5fdda..aee81ceb 100644
--- a/src/types/item.ts
+++ b/src/types/item.ts
@@ -92,7 +92,7 @@ export interface ItemRevision {
revisionDate: string; // 수정일
revisionBy: string; // 수정자
revisionReason?: string; // 수정 사유
- previousData: any; // 이전 버전의 전체 데이터
+ previousData: Record; // 이전 버전의 전체 데이터
}
// ===== 품목 파일 정보 =====