Post

Next.js 성능 최적화

Next.js 성능 최적화

프론트엔드 성능 최적화는 웹 애플리케이션의 로딩 속도, 반응성, 사용자 경험(UX)을 개선하는 핵심 요소이다.

이번 글에서는 실제 프로젝트에서 적용한 프론트엔드 성능 최적화 기법을 정리한다.


1. 이미지 최적화

📌 웹사이트의 성능 저하 원인 중 하나는 크기가 큰 이미지

  • 고해상도 이미지는 로드 속도를 늦추고 네트워크 비용을 증가
  • 최적화된 이미지 제공을 통해 로딩 시간을 줄일 수 있음

최적화 방법

  • WebP, AVIF 같은 최신 이미지 포맷 사용
  • Next.js next/image 컴포넌트 활용 (자동 최적화, Lazy Loading 지원)
  • CDN(Content Delivery Network) 사용으로 전송 속도 개선
  • 반응형 이미지 (srcset, sizes) 활용하여 적절한 해상도로 제공

💡 Next.js의 next/image를 활용한 이미지 최적화 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Image from "next/image";

const OptimizedImage = () => {
  return (
    <Image
      src="/example.jpg"
      alt="최적화된 이미지"
      width={800}
      height={600}
      quality={80}
      priority
    />
  );
};

export default OptimizedImage;

✔️ quality={80}: 품질을 유지하면서 파일 크기 감소

✔️ priority: 중요한 이미지를 우선 로드


2. 코드 스플리팅(Code Splitting)과 Lazy Loading

📌 모든 코드를 한 번에 로드하면 초기 로딩 속도가 느려짐

  • 사용자가 필요할 때만 해당 코드를 불러오면 성능 개선 가능

최적화 방법

  • React.lazy + Suspense를 사용하여 동적 임포트
  • 라우트 기반 코드 스플리팅 (React Router, Next.js Dynamic Import)
  • 모달, 차트, 비디오 등 무거운 컴포넌트는 Lazy Loading 적용

💡 React.lazy + Suspense를 활용한 동적 로딩 예제

1
2
3
4
5
6
7
8
9
import { lazy, Suspense } from "react";

const HeavyComponent = lazy(() => import("./HeavyComponent"));

const App = () => (
  <Suspense fallback={<div>Loading...</div>}>
    <HeavyComponent />
  </Suspense>
);

✔️ lazy()를 사용해 필요할 때만 컴포넌트를 로드

✔️ Suspensefallback 속성을 이용해 로딩 UI 제공

💡 Next.js dynamic()을 활용한 코드 스플리팅

1
2
3
4
5
6
7
8
9
import dynamic from "next/dynamic";

const DynamicChart = dynamic(() => import("./Chart"), { ssr: false });

const Dashboard = () => {
  return <DynamicChart />;
};

export default Dashboard;

✔️ 서버 사이드 렌더링(SSR) 비활성화하여 클라이언트에서만 로드

✔️ 초기 번들 크기 감소


3. 메모이제이션을 활용한 렌더링 최적화

📌 불필요한 렌더링을 줄이는 것이 성능 최적화의 핵심

  • 리렌더링이 빈번한 경우 성능 저하 발생
  • useMemo, useCallback을 활용해 메모이제이션 적용

최적화 방법

  • useMemo를 사용하여 계산 비용이 큰 연산을 캐싱
  • useCallback을 사용하여 불필요한 함수 재생성 방지
  • React.memo를 사용하여 동일한 props가 전달되면 렌더링 방지

💡 useMemo를 활용한 최적화 예제

1
2
3
4
5
6
7
8
9
10
import { useMemo } from "react";

const ExpensiveCalculation = ({ value }) => {
  const result = useMemo(() => {
    console.log("Expensive Calculation...");
    return value * 2;
  }, [value]);

  return <div>Result: {result}</div>;
};

✔️ useMemo를 활용하여 value가 변경될 때만 연산 수행

💡 useCallback을 활용한 최적화 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useState, useCallback } from "react";

const Button = ({ onClick }) => {
  return <button onClick={onClick}>Click Me</button>;
};

const App = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);

  return (
    <>
      <p>Count: {count}</p>
      <Button onClick={handleClick} />
    </>
  );
};

✔️ useCallback을 사용하여 handleClick 함수가 매번 새로 생성되지 않도록 최적화

💡 React.memo를 활용한 최적화 예제

1
2
3
4
5
6
import React from "react";

const ExpensiveComponent = React.memo(({ value }) => {
  console.log("Rendering...");
  return <div>Value: {value}</div>;
});

✔️ 동일한 props가 전달되면 리렌더링을 방지


4. 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG) 활용

📌 클라이언트에서 모든 데이터를 렌더링하면 성능 저하 발생

  • SSR을 사용하면 서버에서 미리 렌더링 후 전달하여 초기 로딩 속도를 개선
  • SSG는 빌드 시 HTML을 생성하여 정적 파일로 제공

최적화 방법

  • Next.js getServerSideProps를 사용하여 서버에서 데이터를 미리 가져오기
  • 정적 페이지는 getStaticProps를 사용하여 SSG 적용

💡 Next.js getServerSideProps를 활용한 SSR 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export async function getServerSideProps() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();

  return {
    props: { data }
  };
}

const Page = ({ data }) => {
  return <div>{data.title}</div>;
};

export default Page;

✔️ 서버에서 데이터를 가져와 HTML을 완성한 후 클라이언트에 전달

💡 Next.js getStaticProps를 활용한 SSG 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export async function getStaticProps() {
  const res = await fetch("https://api.example.com/data");
  const data = await res.json();

  return {
    props: { data }
  };
}

const StaticPage = ({ data }) => {
  return <div>{data.title}</div>;
};

export default StaticPage;

✔️ 정적 HTML을 빌드 시 생성하여 빠른 로딩 제공


🔹 결론

프론트엔드 성능 최적화는 사용자 경험과 SEO 성능을 높이는데 필수적이다.

✔️ 이미지 최적화(WebP, Lazy Loading, CDN)로 로딩 속도 개선

✔️ 코드 스플리팅과 Lazy Loading으로 불필요한 로드 방지

✔️ 메모이제이션(useMemo, useCallback)을 통해 렌더링 최적화

✔️ SSR과 SSG를 활용하여 빠른 페이지 로딩 제공

💡 이러한 기법들을 적용하면 Lighthouse 성능 점수가 크게 향상되며, 실제 사용자 경험도 개선된다. 🚀

This post is licensed under CC BY 4.0 by the author.