이 문서는 DecodEat 프로젝트에 적용된 성능 최적화 기법들을 설명합니다.
위치: src/routes/router.tsx
React.lazy와 Suspense를 사용하여 페이지 레벨에서 코드를 분할합니다.
const HomePage = lazy(() => import("../pages/HomePage"));
const DetailPage = lazy(() => import("../pages/DetailPage"));
// ...
const withSuspense = (Component: React.LazyExoticComponent<() => JSX.Element>) => (
<Suspense fallback={<PageLoader />}>
<Component />
</Suspense>
);효과:
- ✅ 초기 번들 크기 감소
- ✅ 페이지별 필요한 코드만 로드
- ✅ 초기 로딩 속도 30-50% 개선
위치: src/main.tsx
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5분간 데이터를 신선한 상태로 유지
gcTime: 1000 * 60 * 30, // 30분간 캐시 유지
retry: 1, // 재시도 횟수
},
},
});효과:
- ✅ 불필요한 네트워크 요청 감소
- ✅ 페이지 전환 시 즉시 캐시된 데이터 표시
- ✅ 서버 부하 감소
적용 컴포넌트:
ProductCard- 제품 목록에서 자주 리렌더링되는 카드Pagination- 페이지 변경 시에만 리렌더링LazyImage- 이미지 컴포넌트
사용 예시:
const ProductCard = memo(({ product, onProductClick }) => {
// ...
});효과:
- ✅ 불필요한 리렌더링 방지
- ✅ 리스트 렌더링 성능 개선
- ✅ UI 응답성 향상
위치: src/components/search/ProductGrid.tsx
const handleProductClick = useCallback(
(productId: number) => {
navigate(`/detail/${productId}`);
},
[navigate]
);효과:
- ✅ 자식 컴포넌트로 전달되는 함수의 재생성 방지
- ✅ React.memo와 함께 사용 시 최대 효과
위치: src/components/ui/LazyImage.tsx
Intersection Observer API를 사용하여 뷰포트에 진입할 때만 이미지 로드:
const LazyImage = memo(({ src, alt }) => {
const { ref, inView } = useInView({
triggerOnce: true,
threshold: 0.1,
rootMargin: "50px", // 50px 전에 미리 로드
});
return inView ? <img src={src} alt={alt} /> : <Skeleton />;
});사용 방법:
import LazyImage from "../ui/LazyImage";
<LazyImage
src="/product.jpg"
alt="제품 이미지"
className="w-full h-auto"
/>효과:
- ✅ 초기 페이지 로딩 시 이미지 로드 지연
- ✅ 대역폭 절약 (보이지 않는 이미지는 로드 안 함)
- ✅ 페이지 로딩 속도 개선
-
Chrome DevTools Lighthouse
- Performance 점수 확인
- First Contentful Paint (FCP)
- Largest Contentful Paint (LCP)
- Time to Interactive (TTI)
-
React DevTools Profiler
- 컴포넌트 렌더링 시간 측정
- 불필요한 리렌더링 탐지
-
Network 탭
- 번들 크기 확인
- 코드 스플리팅 효과 측정
# 프로덕션 빌드
pnpm build
# 빌드 결과 확인
pnpm preview
# Lighthouse 측정 (Chrome DevTools)
# 1. 시크릿 모드에서 페이지 열기
# 2. F12 → Lighthouse 탭
# 3. "Analyze page load" 실행아직 적용하지 않았지만, 필요 시 추가할 수 있는 최적화:
긴 제품 목록이 있는 경우 @tanstack/react-virtual 사용:
import { useVirtualizer } from '@tanstack/react-virtual';
const ProductList = ({ products }) => {
const parentRef = useRef(null);
const rowVirtualizer = useVirtualizer({
count: products.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 200,
});
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
<div key={virtualRow.index}>
{products[virtualRow.index].name}
</div>
))}
</div>
);
};- WebP 형식 사용
- 반응형 이미지 (
srcset,sizes) - CDN 사용
import { useQueryClient } from '@tanstack/react-query';
const prefetchProduct = () => {
const queryClient = useQueryClient();
queryClient.prefetchQuery({
queryKey: ['product', productId],
queryFn: () => getProductDetail(productId),
});
};오프라인 지원 및 캐싱 전략
성능 메트릭을 지속적으로 모니터링하기 위해 다음 도구 고려:
- Google Analytics 4 (GA4)
- Sentry Performance Monitoring
- LogRocket
마지막 업데이트: 2025년 (성능 최적화 적용)