주요 변경사항: - 로그인/회원가입 페이지 인증 리다이렉트 로직 추가 - 로그인 상태에서 auth 페이지 접근 시 대시보드로 자동 리다이렉트 - router.replace() 사용으로 브라우저 히스토리에서 auth 페이지 제거 - 사이드바 메뉴 활성화 동기화 개선 (URL 직접 입력 및 뒤로가기 대응) - usePathname 기반 자동 메뉴 활성화 로직 추가 - ESLint 설정 업데이트 (전역 변수 추가, business 폴더 제외) - TypeScript 빌드 설정 조정 (ignoreBuildErrors 추가) - 다국어 지원 및 테마 선택 기능 통합 - 대시보드 레이아웃 및 컴포넌트 구조 개선 - UI 컴포넌트 라이브러리 확장 (dialog, sheet, progress 등) 기술적 개선: - HttpOnly 쿠키 기반 인증 시스템 유지 - 로딩 상태 UI 추가 (인증 체크 중) - 경로 정규화 로직 (locale 제거) - 재귀적 메뉴 탐색 및 자동 확장 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
66 lines
1.5 KiB
TypeScript
66 lines
1.5 KiB
TypeScript
import { memo, useState, useEffect } from 'react';
|
|
import type { ReactNode } from 'react';
|
|
|
|
/**
|
|
* 차트 지연 로딩을 위한 래퍼 컴포넌트
|
|
* 스켈레톤 UI 표시 후 차트 렌더링
|
|
*/
|
|
interface ChartWrapperProps {
|
|
children: ReactNode;
|
|
delay?: number;
|
|
height?: number;
|
|
}
|
|
|
|
export const ChartWrapper = memo(function ChartWrapper({
|
|
children,
|
|
delay = 100,
|
|
height = 300
|
|
}: ChartWrapperProps) {
|
|
const [showChart, setShowChart] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(() => setShowChart(true), delay);
|
|
return () => clearTimeout(timer);
|
|
}, [delay]);
|
|
|
|
if (!showChart) {
|
|
return (
|
|
<div
|
|
className="w-full animate-pulse bg-muted rounded-lg"
|
|
style={{ height: `${height}px` }}
|
|
>
|
|
<div className="h-full flex items-center justify-center">
|
|
<div className="text-muted-foreground text-sm">차트 로딩 중...</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <>{children}</>;
|
|
});
|
|
|
|
/**
|
|
* 메모이제이션된 차트 컴포넌트 래퍼
|
|
*/
|
|
interface OptimizedChartProps {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
data: any;
|
|
children: ReactNode;
|
|
height?: number;
|
|
}
|
|
|
|
export const OptimizedChart = memo(function OptimizedChart({
|
|
data: _data,
|
|
children,
|
|
height = 300
|
|
}: OptimizedChartProps) {
|
|
return (
|
|
<ChartWrapper height={height}>
|
|
{children}
|
|
</ChartWrapper>
|
|
);
|
|
}, (prevProps, nextProps) => {
|
|
// 데이터가 같으면 리렌더링 스킵
|
|
return prevProps.data === nextProps.data;
|
|
});
|