[Next.js] 성능 최적화 시도

Su Min·2025년 2월 25일
1
post-thumbnail

🔗 시작하면서

포트폴리오 웹사이트를 만들면서 SEO최적화를 위해 기본적인 Metadata설정이나 sitemap, robots 설정을 했으나 LightHouse로 측정한 결과, 초기 진입 시 첫 번째 컨텐츠가 화면에 표시되는 LCP지연과 Total Blocking Time(TBT)이 발생하여 성능 점수가 낮게 측정되었다.🥲

성능도 초록색 빛으로 바꾸고싶은 마음에 여러가지 방법을 시도했다.

Next.js Image 활용

next/image는 이미지 크기 최적화, WepP와 같은 최적의 포맷으로 변환, lazy loading 등 자동으로 이미지 최적화를 수행한다.

경로 수정

내 프로젝트에서는 이미 next/image를 사용하였으며 아래와 같이 경로와 이미지 사이즈를 목데이터로 넘겨 렌더링되고 있었다.

export const Data = [
	{
    	...
        images: ['/images/portfolio1.png', '/images/portfolio2.png'],
    	size: 400,
    }
]

위처럼 절대경로를 사용하면 next/image가 빌드 시 최적화할 수 없고 클라이언트에서 이미지를 불러오면서 최적화가 진행된다. 또한 layout shift를 피하기 위해 width와 height를 지정해주어야한다.
반면, 정적 파일을 import하면 빌드 타임에 이미 최적화된 이미지가 생성되므로 성능적으로 더 유리하다.

import portfolio1 from '../../public/images/portfolio1.png'
import portfolio2 from '../../public/images/portfolio2.png'

export const Data = [
	{
    	...
		images: [portfolio1, portfolio2]
    }
]

위와 같이 상대경로로 이미지 경로를 주입하면 빌드 타임에 이미지를 생성하고 이미지의 width와 height를 자동으로 결정하여 수동으로 width와 height를 지정해주지 않아도 된다.

이미지 우선순위

현재 프로젝트에서는 LCP로 감지된 이미지가 없지만 나중을 대비해서 기록.

next/imagepriority속성은 true일 때 이미지에 우선 순위를 적용하여 사전로드한다. LCP 요소로 감지 된 이미지에서 priority 속성을 사용하면 자동으로 적용된 이미지 크기가 우선적으로 로드된다. 기본 값으로 loading="lazy" 속성이 있지만 priority 속성을 사용하면 즉시 다운로드되면서 lazy loading이 비활성화된다.
결론적으론 LCP에 감지된 이미지가 있다면 priority 속성을 통해 해결하자.

코드 스플리팅

코드 스플리팅이란 코드를 하나의 번들에서 여러 개의 청크로 나누어 필요할 때만 로드하는 방법이다. 한 번에 큰 용량의 번들을 로드하지 않고 필요한 코드만 로드해서 초기 로드 속도를 개선할 수 있다.

Next.js는 이미 페이지 단위로 코드 스플리팅이 적용된다. 하지만 단일페이지인 내 프로젝트는 초기 진입에 모든 번들을 다운로드하는데 여기에 SSR이 불가능한 컴포넌트를 동적 로드하면 클라이언트에서 추가적인 네트워크 요청이 발생하여 초기 렌더링 속도에 영향을 줄 수 있다. 그렇지만 모달과 같은 초기 화면에 영향을 주지 않는 컴포넌트라면 코드 스플리팅을 적용하여 초기 로딩속도를 개선할 수 있다!
내 프로젝트에서는 모달은 없지만 framer-motion, swiper와 같은 무거운 UI라이브러리를 사용하고 있다. 이 라이브러리를 사용하고 있는 컴포넌트는 SSR이 불가능하기 때문에 next/dynamic을 적용해주었다.

const MotionContainer = dynamic(() => import('./motion-container'), {
  ssr: false,
  loading: () => <div style={{ height: '400px' }} />,
})

const Main = () => {
  return (
    <section>
      <MotionContainer />
    </section>
  )
}

export default Main

위의 Main 컴포넌트 하위에 MotionContainer 컴포넌트는 LCP지연에 가장 큰 영향을 주는 컴포넌트이다. 컨텐츠가 framer-motion을 사용하고 있어 ssr이 불가능했기때문에 loading 옵션을 추가하여 layout shift를 방지하고 최소한의 요소를 먼저 그려놓게하여 초기 로딩 속도를 개선시킬 수 있었다.

🔗 성능 최적화 결과

  • 번들 사이즈: 657KB → 572KB (약 80KB, 13% 감소) next/bundle-analyzer 사용

  • 로드 속도: 944ms → 636ms (약 300ms, 30% 개선)

  • LCP 지연: 4,530ms → 2,100ms (약 2,400ms, 50% 개선)

  • LightHouse 성능 점수: 71점 → 90점 (약 20점 상승)

🔗 회고

프로젝트를 진행하며 검색 엔진 최적화(SEO)를 위해 다양한 방법을 찾아보던 중, 성능 최적화의 중요성을 직접 경험할 수 있었다. 번들 사이즈, 초기 렌더링 속도, 레이아웃 변화 등이 SEO와 성능에 큰 영향을 미친다는 점을 다시 한 번 깨달았고, 이를 개선하는 과정에서 보다 효율적인 웹 서비스를 구축하는 방법을 고민해볼 수 있었다.
이번 프로젝트에서는 비교적 작은 규모의 최적화 방법을 적용했지만, 향후 더 큰 규모의 프로젝트에서는 더욱 다양한 기법을 활용하여 성능을 고려한 서비스를 개발해보고 싶다.

profile
성장하는 과정에서 성취감을 통해 희열을 느낍니다⚡️

0개의 댓글

관련 채용 정보