SSR → SSG 전환
매 요청마다 서버 렌더링 → 빌드 시 HTML 사전 생성으로 CDN 캐시 서빙
포트폴리오는 콘텐츠가 정적이어서 SSR의 이점이 없음에도 요청마다 서버가 HTML을 새로 생성하고 있었습니다.
// 기본 SSR — 설정 없음export const dynamic = "force-static";Lighthouse 측정 기반
LCP · INP · 접근성 문제를 진단하고 개선한 실제 작업 기록입니다.
매 요청마다 서버 렌더링 → 빌드 시 HTML 사전 생성으로 CDN 캐시 서빙
포트폴리오는 콘텐츠가 정적이어서 SSR의 이점이 없음에도 요청마다 서버가 HTML을 새로 생성하고 있었습니다.
// 기본 SSR — 설정 없음export const dynamic = "force-static";h1이 opacity:0 에서 시작해 브라우저가 LCP 측정을 지연
animate-fade-in-up은 opacity:0 에서 시작하고 delay-100은 120ms 딜레이를 추가합니다. 브라우저는 opacity가 0인 동안 해당 요소를 LCP 후보로 인식하지 않아 측정이 2.7s 이후로 밀렸습니다.
<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>unoptimized 옵션 제거로 WebP 자동 변환 및 크기 최적화 활성화
프로젝트 썸네일에 unoptimized prop이 설정되어 있어 Next.js의 WebP 변환, 크기 조정, 캐시 헤더가 전혀 동작하지 않았습니다.
<Image src={...} fill unoptimized sizes="..." /><Image src={...} fill sizes="(max-width: 640px) 100vw, 50vw" />키 입력마다 즉시 필터링 → React가 여유 시간에 처리
검색어를 입력할 때마다 즉시 필터링 연산이 실행되어 메인 스레드를 블로킹했습니다.
setQuery(value);startTransition(() => setQuery(value));
const deferred = useDeferredValue(query);스크롤 이벤트마다 setState → 결과가 바뀔 때만 업데이트
스크롤할 때마다 Nav 컴포넌트가 React 상태를 업데이트해 불필요한 리렌더링이 반복됐습니다.
window.addEventListener("scroll", () => setScrolled(true));window.addEventListener("scroll", () => {
const next = window.scrollY > 10;
if (next !== scrolled) setScrolled(next);
});초기 로드 시 스크립트 실행 → 페이지 완전 로드 후 실행
GTM 스크립트가 페이지 초기 로딩 시 실행되어 첫 상호작용 응답 속도에 영향을 줬습니다.
<Script strategy="afterInteractive" src="gtm.js" /><Script strategy="lazyOnload" src="gtm.js" />--muted 값 상향으로 텍스트 대비율 기준 충족
어두운 배경(#0c0c0c)에 회색 텍스트(#6b7280)를 사용하고, 투명도까지 낮춰 기준치 미달이었습니다.
--muted: #6b7280; /* gray-500, 대비율 부족 */--muted: #9ca3af; /* gray-400, ~5.8:1 → AA 통과 */