Lighthouse 측정 기반

성능 최적화 작업 기록

LCP · INP · 접근성 문제를 진단하고 개선한 실제 작업 기록입니다.

LCP3.5s~2.5s
TTFB서버60–80% ↓
이미지JPG40–70% ↓
접근성미달AA 통과
렌더링
Next.jsApp Router

SSR → SSG 전환

매 요청마다 서버 렌더링 → 빌드 시 HTML 사전 생성으로 CDN 캐시 서빙

포트폴리오는 콘텐츠가 정적이어서 SSR의 이점이 없음에도 요청마다 서버가 HTML을 새로 생성하고 있었습니다.

항목변경 전변경 후
TTFB서버 응답 의존CDN 캐시
단축률60–80%
변경 전
// 기본 SSR — 설정 없음
변경 후
export const dynamic = "force-static";
LCP
Next.jsReactTailwind CSS

Hero 제목 opacity 애니메이션 제거

h1이 opacity:0 에서 시작해 브라우저가 LCP 측정을 지연

animate-fade-in-up은 opacity:0 에서 시작하고 delay-100은 120ms 딜레이를 추가합니다. 브라우저는 opacity가 0인 동안 해당 요소를 LCP 후보로 인식하지 않아 측정이 2.7s 이후로 밀렸습니다.

항목변경 전변경 후
LCP3.5s~2.5s
단축최대 1.0s
변경 전
<h1 className="animate-fade-in-up delay-100">Hyebin</h1>
변경 후
<h1 className="text-foreground">
  <span className="animate-fade-in-up delay-100 inline-block">Hyebin</span>
</h1>
LCP/FCP
Next.jsnext/image

프로젝트 이미지 최적화

unoptimized 옵션 제거로 WebP 자동 변환 및 크기 최적화 활성화

프로젝트 썸네일에 unoptimized prop이 설정되어 있어 Next.js의 WebP 변환, 크기 조정, 캐시 헤더가 전혀 동작하지 않았습니다.

항목변경 전변경 후
이미지 전송량JPG 원본WebP 변환
감소율40–70%
변경 전
<Image src={...} fill unoptimized sizes="..." />
변경 후
<Image src={...} fill sizes="(max-width: 640px) 100vw, 50vw" />
INP
Next.jsReact

검색 / 필터 응답성 개선

키 입력마다 즉시 필터링 → React가 여유 시간에 처리

검색어를 입력할 때마다 즉시 필터링 연산이 실행되어 메인 스레드를 블로킹했습니다.

항목변경 전변경 후
처리 방식동기 즉시 실행startTransition
체감타이핑 끊김즉각 반응
변경 전
setQuery(value);
변경 후
startTransition(() => setQuery(value));
const deferred = useDeferredValue(query);
INP
Next.jsReact

스크롤 리렌더링 최소화

스크롤 이벤트마다 setState → 결과가 바뀔 때만 업데이트

스크롤할 때마다 Nav 컴포넌트가 React 상태를 업데이트해 불필요한 리렌더링이 반복됐습니다.

항목변경 전변경 후
setState 빈도이벤트마다변화 시에만
리렌더매우 빈번필요한 경우만
변경 전
window.addEventListener("scroll", () => setScrolled(true));
변경 후
window.addEventListener("scroll", () => {
  const next = window.scrollY > 10;
  if (next !== scrolled) setScrolled(next);
});
INP
Next.jsnext/script

GTM / Analytics 로딩 지연

초기 로드 시 스크립트 실행 → 페이지 완전 로드 후 실행

GTM 스크립트가 페이지 초기 로딩 시 실행되어 첫 상호작용 응답 속도에 영향을 줬습니다.

항목변경 전변경 후
로딩 전략afterInteractivelazyOnload
초기 INP 영향메인 스레드 경합경합 없음
변경 전
<Script strategy="afterInteractive" src="gtm.js" />
변경 후
<Script strategy="lazyOnload" src="gtm.js" />
접근성
Next.jsTailwind CSS

색상 대비 WCAG AA 통과

--muted 값 상향으로 텍스트 대비율 기준 충족

어두운 배경(#0c0c0c)에 회색 텍스트(#6b7280)를 사용하고, 투명도까지 낮춰 기준치 미달이었습니다.

항목변경 전변경 후
text-muted 대비율~4.8:1~5.8:1
text-muted/80~3.8:1 ✗~4.6:1 ✓
기준불합격WCAG AA 통과
변경 전
--muted: #6b7280;   /* gray-500, 대비율 부족 */
변경 후
--muted: #9ca3af;   /* gray-400, ~5.8:1 → AA 통과 */