'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(); 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 { 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(() => getCached(group) ?? []); const [isLoading, setIsLoading] = useState(!getCached(group)); const [error, setError] = useState(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 }; }