본문으로 바로가기

배경: FSD(Feature-Sliced Design) + Barrel Files

초기 설계는 Feature-Sliced Design (FSD)를 따랐습니다. Feature 단위로 독립성과 가독성을 확보하기 위해, 권장된 가이드에 따라 Barrel Files를 도입했습니다. 하지만 Feature 단위에서 내보내는 Public Index의 양이 많아지면서 필요 없는 코드까지 묶여서 파일이 무거워졌습니다.

결국: 아래와 같이 퍼포먼스에 이슈가 생겼고 Barrel Files를 걷어내기로 했습니다.

  • 초기 로딩 부하 증가
  • SEO Core Web Vitals 점수 하락

문제 진단: 구조적 성능 병목

  1. 과적축된 정적 Public API
  2. SSR이 필요하지 않은 컴포넌트에 Code Splitting이 되어 있지 않아 초기 렌더링 부하

평균 LCP 2.1초, Performance Score 75.88로 UX 관점에서 ‘명백한 이탈 위험 구간’이었습니다.


해결 전략: 구조 레벨부터 리셋

1. Barrel Files 제거 → 명시적 Import

모든 컴포넌트를 개별 import로 변경해, 필요할 때만 필요한 코드만 가져오도록 구조 재설계.

// Before
import { Dialog, Tooltip, Dropdown } from '@/components/common';

// After
import Dialog from '@/components/common/Dialog';
import Tooltip from '@/components/common/Tooltip';
import Dropdown from '@/components/common/Dropdown';
  • 모노레포 패키지의 경우 필요 컴포넌트만 번들링을 통한 트리쉐이킹 100% 활성화
  • 모노레포 앱의 경우 필요한 컴포넌트를 직접 import 하도록 변경
  • 코드 의존성 명확화

 

2. 초기 렌더링과 무관한 컴포넌트는 Dynamic Import + SSR False

import dynamic from 'next/dynamic';

const Dialog = dynamic(() => import('@/components/common/Dialog'), { ssr: false });

 

  • Dialog, Modal, Tooltip 등 인터랙티브 컴포넌트 및 번들사이즈에 방해되는 컴포넌트는 꼭 필요하지 않다면 초기 렌더링 제외
  • HTML 크기 축소 → Time to First Byte(TTFB) 개선
  • Core Web Vitals(LCP, FCP) 직접 개선
  • 데이터가 무거운데 자주 방문하는 페이지의 데이터의 경우 FetchPolicy를 'cache-first'로 두어 FCP 개선 (단, 인증을 확인 하기 위해 별도 인증 정보 호출을 'network-only'로 통신)

성과

항목 개선 전 개선 후  변화율
평균 LCP 2.10초 0.68초 ▲ 약 67.6% 개선
Performance Score 75.88 90.12 ▲ 약 18.8% 개선
페이지당 평균 파일 크기 7.97 kB 6.15 kB ▼ 22.8% 감소
페이지당 First Load JS 745.88 kB 583.27 kB ▼ 21.8% 감소

✅ LCP 0.7초 이하
✅ Performance 90점 이상
✅ 체감 로딩 속도 “즉시 반응” 수준