- common-codes.ts에서 'use server' 제거, 타입/유틸리티만 유지 - useCommonCodes 훅 생성 (클라이언트 /api/proxy/ 패턴, 5분 캐시) - ItemListClient 탭/통계카드를 common_codes 기반 동적 생성으로 전환 - OrderSalesDetailEdit 서버액션 → useCommonCodes 훅 전환 - order-management actions.ts 서버액션 내 공통코드 직접 조회로 변경
124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
import type { CommonCode, CommonCodeOption } from '@/lib/api/common-codes';
|
|
import { toCommonCodeOptions } from '@/lib/api/common-codes';
|
|
|
|
// ========================================
|
|
// 메모리 캐시 (앱 전체 공유)
|
|
// ========================================
|
|
|
|
const cache = new Map<string, { data: CommonCode[]; timestamp: number }>();
|
|
const CACHE_TTL = 5 * 60 * 1000; // 5분
|
|
|
|
function getCached(group: string): CommonCode[] | null {
|
|
const entry = cache.get(group);
|
|
if (!entry) return null;
|
|
if (Date.now() - entry.timestamp > CACHE_TTL) {
|
|
cache.delete(group);
|
|
return null;
|
|
}
|
|
return entry.data;
|
|
}
|
|
|
|
function setCache(group: string, data: CommonCode[]) {
|
|
cache.set(group, { data, timestamp: Date.now() });
|
|
}
|
|
|
|
// ========================================
|
|
// 공통 코드 fetch (클라이언트 사이드)
|
|
// ========================================
|
|
|
|
async function fetchCommonCodes(group: string): Promise<CommonCode[]> {
|
|
const response = await fetch(`/api/proxy/settings/common/${group}`);
|
|
if (!response.ok) {
|
|
throw new Error(`공통코드 조회 실패 (${group}): ${response.status}`);
|
|
}
|
|
const json = await response.json();
|
|
// API 응답: { success: true, data: [...] } 또는 직접 배열
|
|
return json.data ?? json;
|
|
}
|
|
|
|
// ========================================
|
|
// useCommonCodes 훅
|
|
// ========================================
|
|
|
|
interface UseCommonCodesResult {
|
|
/** 공통 코드 배열 */
|
|
codes: CommonCode[];
|
|
/** Select/ComboBox용 옵션 배열 */
|
|
options: CommonCodeOption[];
|
|
/** 로딩 상태 */
|
|
isLoading: boolean;
|
|
/** 에러 메시지 */
|
|
error: string | null;
|
|
/** 캐시 무시하고 재조회 */
|
|
refetch: () => void;
|
|
}
|
|
|
|
/**
|
|
* 공통 코드 조회 훅
|
|
*
|
|
* @param group - 코드 그룹명 (예: 'item_type', 'order_status')
|
|
* @returns codes, options, isLoading, error, refetch
|
|
*
|
|
* @example
|
|
* const { codes, options, isLoading } = useCommonCodes('item_type');
|
|
* // codes: [{ id, code, name, ... }, ...]
|
|
* // options: [{ value: 'FG', label: '완제품' }, ...]
|
|
*/
|
|
export function useCommonCodes(group: string): UseCommonCodesResult {
|
|
const [codes, setCodes] = useState<CommonCode[]>(() => getCached(group) ?? []);
|
|
const [isLoading, setIsLoading] = useState(!getCached(group));
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
const mountedRef = useRef(true);
|
|
|
|
useEffect(() => {
|
|
mountedRef.current = true;
|
|
return () => { mountedRef.current = false; };
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// 캐시 히트 시 fetch 스킵 (refreshKey=0일 때만)
|
|
const cached = getCached(group);
|
|
if (cached && refreshKey === 0) {
|
|
setCodes(cached);
|
|
setIsLoading(false);
|
|
setError(null);
|
|
return;
|
|
}
|
|
|
|
let cancelled = false;
|
|
setIsLoading(true);
|
|
|
|
fetchCommonCodes(group)
|
|
.then((data) => {
|
|
if (cancelled || !mountedRef.current) return;
|
|
setCache(group, data);
|
|
setCodes(data);
|
|
setError(null);
|
|
})
|
|
.catch((err) => {
|
|
if (cancelled || !mountedRef.current) return;
|
|
console.error(`공통코드 조회 오류 (${group}):`, err);
|
|
setError(err.message || '공통코드를 불러오는데 실패했습니다.');
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled && mountedRef.current) {
|
|
setIsLoading(false);
|
|
}
|
|
});
|
|
|
|
return () => { cancelled = true; };
|
|
}, [group, refreshKey]);
|
|
|
|
const refetch = useCallback(() => {
|
|
cache.delete(group);
|
|
setRefreshKey((k) => k + 1);
|
|
}, [group]);
|
|
|
|
const options = toCommonCodeOptions(codes);
|
|
|
|
return { codes, options, isLoading, error, refetch };
|
|
} |