nextjs15에서 이미지가 많은 페이지의 성능 개선하기

MM·2025년 4월 4일

PerformanceHigher

목록 보기
1/4
post-thumbnail

라이트하우스 점수 개선

다른건 다 좋은데 LCP점수가 엄청 낮다.

우선 LCP 초가 2.5초 이하여야 좋다고 함.

1. 첫장 이미지 priority주기

svg는 priority를 주면 안 된다!

첫장 이미지는 모두 priority를 줬더니 오히려 lcp점수가 떨어졌다.
무슨 일인가 했더니 svg는 priority를 주면 안 된다고!

priority가 정확하게 하는 일

priority는 html head에 해당 링크를 추가해서 preload하라는 뜻!
-> 즉, priority 이미지가 많아질수록 다른 리소스 다운이 밀린다!

무엇보다 svg는 벡터니까 브라우저 렌더링 엔진이 처리하기 때문에 js엔진이 굳이굳이 빠르게 처리하려고 하지 않아도 됨.
= 성능에 영향 없음!

참고로, 리소스 다운받을 때도 priority queue를 쓴다고 함..

fetchpriority도 있다

priority는 Nextjs에서 head태그에 올리는 것.
fetchpriority는 HTML에서 브라우저가 자원 요청시 우선권을 주는 것.
중복해서 줄 수 있다고 한다. 둘 다 주자.

2. 돔 구조 개선

수정 전

 <Image
        src="/images/main/gradient_page1.png"
        alt=""
        width={1700}
        height={751}
        className="absolute top-[30.15vw]"
/>

돔트리 더러워지는게 싫어서 그냥 width, height를 줬는데..
width, height가 이미지 크기를 미리 잡아주는 거라고 생각했는데, cls방지용으로 차지할 비율만 고지하는 거라고 한다...
근데 그럼 props명을 width height라고 하면 안되는거 아님???

그래서 공식문서를 읽어봤다.

HTML의 width 속성과 유사하게 CSS에 의해 제어되는 이미지의 렌더링된 크기는 결정하지 않습니다.

뭔소리냐

HTML 속성으로서의 width 와 CSS 스타일로서의 width 는 다르다

HTML의 width -> 브라우저에 제안하는 기본적인 픽셀 단위 크기
CSS의 width -> 레이아웃 흐름에 따라 최종 렌더링 크기를 결정하는 것.

width/height 속성을 HTML로 넘기지만, 실제 렌더링 크기는 CSS 스타일에 따라 바뀔 수 있다.
= width로 얼마를 줘봤자 내부 다른 컴포넌트때문에 눌리거나 부모 컴포넌트가 작아서 눌릴 수 있다.

-> 그러니까, 혹시 모를 언쟁을 방지하기 위해 굳이 html의 width속성으로 표현했다는 것.
그냥 ratio라고 해..!!!

이유1. relative부모(기준점)가 없는 absolute는 레이아웃 계산을 흐트러뜨린다!!

기준점이 있어야 브라우저가 해당 이미지를 preload할 수 있음!

그래서 부모 relative를 줬다.
z축 관리용 absolute로도 한번 더 감쌈.

<div className="absolute [top:30.15vw] w-[100vw] aspect-[1700/751]">
  <div className="relative w-full h-full">
          <Image
            src="/images/main/gradient_page1.png"
            alt="bg gradation"
            fill
            priority
            sizes="100vw"
            className="object-cover"
          />
  </div>
</div>

LCP를 3.8초에서 2.3초까지 줄임!

더 개선방안을 찾아보다가..

이유2. top과 같은 상대 위치는 브라우저가 뷰포트 크기를 계산한 뒤에 확정 가능하므로 LCP를 악화시킨다

브라우저에서 상대위치는 cssom트리 파싱하고 레이아웃 한 이후에나 정확한 위치를 알 수 있다
-> 정확한 위치를 알 때까지 페인트를 미루기 때문에, LCP가 늦어진다

라고 해서, 정적 위치인 mt나 pt로 바꾸면 더 점수가 좋아질까? 싶어 시도했더니

오히려 점수가 나빠졌다..

속도 우선순위

  • relative 없는 absolute -> 기준점이 없어서 브라우저가 레이아웃까지 끝내고 나중에 처리
  • relative 있는 absolute -> 기준점이 있어서 브라우저가 cssom 트리 파싱 시점에 위치 결정!
  • 정적 위치 + vw -> 뷰포트 크기를 알아야 하므로 레이아웃까지 끝내고 위치가 정해짐

-> 그러니까, 정적위치top가 아니라 vw때문에 나빠진 것.
정적위치 + px같은 정적값이라면 내가 의도한 대로 속도를 높일 수 있겠지만..
지금과 같은 반응형에서는 레이아웃 단위를 vw로 통일해야 한다.

svg는 또 다르다!!

  1. 브라우저는 SVG를 렌더할 때 DOM 요소로 인식
  • 따라서 정적 css 속성을 레이아웃 딜레이 없이 GPU에서 바로 처리!
  • 즉, svg는 vw 쓰더라도 레이아웃 연산이 부담 없다!
  • 즉, absolute + fill보다 정적 vw 위치가 더 빠름!
  1. svg를 fill로 넣으면 크기 맞춤/비율 맞춤 등 렌더링 시 계산 추가됨..!

-> 결론적으로 svg에서는 정적(margin, vw..)를 줬더니 성능이 더 올랐다!

그럼 모든 이미지에 transform을 넣어서 GPU가속을 쓰게하면 성능이 무지 좋아지지 않을까?

gpu에 너무 많이 올리면 메모리 과부하가 발생한다고...!
특히 모바일 저사양 기기일수록 gpu 레이어의 관리비용이 올라간다!

그럼 LCP에 사용되는 것들에만 넣어보자

테일윈드에 플러그인으로 추가해줬다.

 plugins: [
    plugin(function ({addUtilities}) {
      addUtilities(
        {
          '.gpu-hint': {
            transform: 'translateZ(0)',
            willChange: 'transform',
          },
        },
        {respectPrefix: false, respectImportant: false},
      );
    }),
  ],

-> 효과가 있다! 점수가 올라감!


3. 라이트하우스 제안사항 반영

dynamic import

이전에 다른 프로젝트에서 했던 것.
page1, page2 외에는 모두 dynamic import해줬다.

best practice는 올랐는데 LCP는 떨어졌다..

동적 임포트로 분리된 파일은 chunk로 나뉘는데, 이렇게 되면 minify 최적화에서 누락될 수도 있다고.
-> 그러니까 당연한 현상이라는 것..

커다란 이미지 webp로 대체

제일 효과가 좋았음... 역시 webp가 최고다.
압축도 추가로 해줘서 이미지 크기를 확 줄였다.

next.config.ts 에 디바이스 최대값 설정

자꾸 같은 이미지에 대해 경고를 띄워서 뭔가 했는데, 100vw로 주면 3000넘는 해상도를 기준으로 삼아서 가져온다고.

//디바이스값을 정의해주자
  deviceSizes: [320, 640, 750, 828, 1080, 1280, 1960],

결론

할 수 있는만큼 했다!!!
또 줄일만한 사항 생각나면 또 줄이러 올 예정.

profile
중요한 건 꺾여도 그냥 하는 마음

0개의 댓글