[Next.js] 성능 최적화

김수연·2025년 5월 8일
1

Next.js

목록 보기
2/2

1. 성능 최적화가 왜 필요한가

성능 최적화는 단순히 로딩 속도를 높이기 위한 기술적인 선택이 아니다. 이는 사용자 경험, 검색 엔진 최적화(SEO), 전환율, 유지 보수성과도 직결된다.

웹사이트의 성능은 사용자의 이탈률에 직접적인 영향을 미친다. 일반적으로 페이지 로딩이 3초를 초과하면 사용자의 약 절반이 이탈한다는 통계가 있다. 또한 구글은 Core Web Vitals라는 지표를 기준으로 검색 랭킹을 결정하고 있으며, 퍼포먼스가 좋은 웹사이트는 검색 결과 상위에 노출될 가능성이 높다.

이러한 이유로 Next.js 기반 프로젝트에서는 성능 최적화가 선택이 아닌 필수적인 요소로 간주된다.


2. 최적화 방식

1. 이미지 최적화

Next.js의 next/image 컴포넌트는 이미지에 대한 자동 최적화 기능을 제공한다.

이미지를 WebP 포맷으로 변환하고, 사이즈를 조절하며, lazy loading 기능을 기본으로 제공한다.

장점

  • LCP(Largest Contentful Paint) 개선
  • 불필요한 데이터 전송 방지
  • 다양한 해상도 대응 가능

2. 웹폰트 최적화

Next.js의 next/font 기능은 웹폰트를 로컬에 다운로드하고, 필요한 글꼴만 선택적으로 불러올 수 있도록 한다.
또한, next/font/google을 사용하여 구글 웹폰트를 최적화하거나, next/font/local을 통해 로컬 폰트를 활용할 수 있어 성능을 더욱 향상시킬 수 있다.

이로 인해 폰트 로딩 지연이나 레이아웃 이동(CLS)을 줄일 수 있다.

장점

  • 텍스트 렌더링 속도 향상
  • CLS 문제 최소화
  • 퍼포먼스 점수 개선

3. 코드 스플리팅과 동적 로딩

Next.js의 dynamic() 함수는 컴포넌트를 필요할 때만 불러오도록 해준다.

초기 렌더링 시 필요한 코드만 불러오기 때문에 초기 번들 사이즈가 줄어든다.

장점

  • 초기 로딩 시간 단축
  • 사용자 요청 시점에 컴포넌트 로딩 가능
  • 페이지 전환 성능 개선

4. 페이지 렌더링 전략 활용

Next.js는 세 가지 렌더링 전략을 제공한다: SSG, SSR, ISR

  • SSG: 정적 페이지를 빌드 타임에 미리 생성
  • SSR: 요청 시마다 서버에서 페이지 생성
  • ISR: 일정 주기마다 정적 페이지를 갱신

장점

  • 콘텐츠 성격에 따라 유연한 적용 가능
  • 정적 페이지는 빠르고 안정적
  • SSR은 실시간성이 필요한 페이지에 적합

5. 데이터 패칭과 캐싱 (SWR 등)

SWR은 클라이언트에서 데이터를 요청하고 캐싱하는 React 훅 라이브러리다.

Stale-While-Revalidate 전략을 통해 오래된 데이터라도 우선 보여주고, 백그라운드에서 최신 데이터를 갱신한다.

장점

  • 빠른 응답성 확보
  • 중복 API 요청 방지
  • 사용자 경험 향상

6. 번들 사이즈 분석 및 최적화

@next/bundle-analyzer를 활용하면 페이지별 번들 사이즈를 시각화할 수 있다.

이를 통해 불필요한 의존성, 공통 코드 중복 등을 식별하고 제거할 수 있다.

장점

  • 초기 로딩 시간 감소
  • 코드 분리 및 경량화 가능
  • 유지보수성 향상

Next.js의 <Link> 컴포넌트는 브라우저가 idle 상태일 때 링크 대상의 JS 코드를 백그라운드에서 미리 로드한다.

이를 통해 클릭 시 페이지 전환 속도를 줄일 수 있다.

장점

  • 사용자 체감 속도 향상
  • 전환 지연 최소화
  • UX 개선

3. 예시


아래는 몇 가지 최적화 방식을 사용하여 위에 보이는 예시 페이지의 최적화 전과 후를 비교한 것이다. 퍼포먼스 비교에는 Lighthouse를 사용했다.

1) 최적화 전 코드

// app/page.tsx
'use client';
import { useEffect } from 'react';

export default function Home() {
  return (
    <main>
      <h1>기본 페이지</h1>
      <img src="/large-image.jpg" alt="big image" width="1200" height="800" />
      <p style={{ fontFamily: 'Open Sans' }}>웹폰트 텍스트</p>
      <HeavyComponent />
    </main>
  );
}

function HeavyComponent() {
  useEffect(() => {
    const now = performance.now();
    while (performance.now() - now < 1000) {} // 1초 동안 CPU 점유
  }, []);

  return <div>무거운 컴포넌트 (성능 테스트용)</div>;
}

문제점 요약

항목설명
이미지 최적화 미적용<img> 태그를 그대로 사용하여 이미지 포맷 변환, lazy loading, LCP 개선 등이 적용되지 않음
웹폰트 최적화 미적용style 속성으로 외부 폰트를 적용하려 시도했지만, 실제로는 폰트 로딩 지연과 CLS 발생 가능성 존재
동적 로딩 미적용HeavyComponent가 초기 렌더링 시 바로 로딩되어 첫 화면이 느려짐
layout.tsx 없음폰트를 전역으로 적용하거나 최적화하지 않았음

결과적으로 페이지 로딩 시 무거운 컴포넌트가 즉시 렌더링되며, 이미지와 폰트도 최적화되지 않아 LCP 등의 웹 성능 지표가 저하될 수밖에 없었다.


2) 최적화 후 코드

// app/page.tsx
'use client';
import dynamic from 'next/dynamic';
import Image from 'next/image';

const HeavyComponent = dynamic(() => import('../components/HeavyComponent'), {
  loading: () => <p>로딩 중...</p>,
  ssr: false,
});

export default function Home() {
  return (
    <main>
      <h1>기본 페이지</h1>

      <Imagesrc="/large-image.jpg"
        alt="big image"
        width={1200}
        height={800}
        priority
      />
      <p>웹폰트 텍스트</p>
      <HeavyComponent />
    </main>
  );
}
// app/layout.tsx
import { Open_Sans } from 'next/font/google';

const openSans = Open_Sans({ subsets: ['latin'], display: 'swap' });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body className={openSans.className}>{children}</body>
    </html>
  );
}
// components/HeavyComponent.tsx
'use client';
import { useEffect } from 'react';

export default function HeavyComponent() {
  useEffect(() => {
    const now = performance.now();
    while (performance.now() - now < 1000) {} // 1초 버벅임
  }, []);

  return <div>무거운 컴포넌트 (성능 테스트용)</div>;
}

적용된 최적화 요약

최적화 기법적용 방식기대 효과
이미지 최적화next/image 사용, priority 속성 설정LCP 개선, 이미지 사이즈 자동 조절 및 포맷 최적화
웹폰트 최적화next/font/google 사용, display: swapCLS 방지, 빠른 텍스트 렌더링
동적 컴포넌트 로딩next/dynamic + ssr: false + 로딩 상태 제공초기 렌더링 지연 방지, 사용자 체감 속도 향상

결과적으로 초기 화면은 이미지와 텍스트만 빠르게 렌더링되며, HeavyComponent는 클라이언트에서 필요 시점에 로딩되기 때문에 전체적인 성능과 사용자 경험이 개선되었다.


마치며

동일한 페이지 구성이라도 어떤 컴포넌트를 언제 불러오고, 어떤 방식으로 리소스를 관리하느냐에 따라 성능 지표는 큰 차이를 보인다. 위와 같은 방식으로 최적화를 적용하면 LCP, CLS, TTI 같은 핵심 Web Vitals 항목에서 좋은 점수를 얻을 수 있으며, 결과적으로 SEO와 사용자 만족도 모두 향상될 수 있다.

profile
프론트엔드 공부하는 학생입니다

0개의 댓글