사람들이 작성한 리뷰들을 모아서 볼 수 있는 커뮤니티
라는 공간이 있다. 리뷰에는 최대 5장의 사진을 첨부할 수 있는데, 초기에 10개의 리뷰를 부러오도록 설정했기 때문에 최대 50장의 이미지가 한번에 로드되는 구조였다.
그 결과, 한번에 많은 이미지를 받아오면서 아래와 같이 뚝뚝 끊겨서 로드 되는 현상이 발생했다. 이는 사용자 경험에 좋지 않은 영향을 주었고, 반드시 해결해야 할 문제였다.
적용 전 | 적용 후 |
---|---|
![]() | ![]() |
원본 이미지 포맷 그대로 사용
이미지가 .jpeg
같은 원본 확장자로 저장되고, 최적화 없이 그대로 불러와 로드 시간이 길어졌다.
불필요한 이미지 로드
10개의 리뷰 데이터를 한번에 불러왔지만 실제로 화면에는 모든 리뷰 데이터가 동시에 표시되지 않는다. 따라서 화면에 보이지 않는 이미지까지 모두 요청하면서 비효율적인 로딩이 발생하고 있었다.
이로인한 네트워크 과부하
위와 같은 이유로 인해 네트워크 요청을 보면 10개의 리뷰 이미지 로드에 31.8MB 가 소모되었고 다운로드 시간만 24초나 걸렸다.
10장의 이미지가 이정도라면 50장의 이미지를 한번에 로드하면 성능 저하가 훨씬 심해졌을 거라는 생각이 들었다.
Next.js의 공식 문서에 따르면 이미지는 웹사이트의 페이지 속도 및 LCP(최대 콘텐츠 표시 시간)에 큰 영향을 미친다. 이를 개선하기 위해 Next.js의 <Image>
컴포넌트를 사용하면 자동으로 이미지 최적화가 가능하다.
기존 <img>
태그를 <Image>
컴포넌트로 변경하는 것이 핵심이다. 만약 S3 와 같은 외부에서 이미지를 받아온다면 next.config.js에서 원격 이미지 도메인을 허용해야 한다.
formats 를 보면 이미지를 먼저 avif 형식으로 반환하고 avif 형식을 지원하지 않는다면 webp 형식으로 변경해준다.
const nextConfig = {
swcMinify: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 's3-my bucket-images.s3.ap-northeast-2.amazonaws.com',
pathname: '/upload/**',
},
],
minimumCacheTTL: 60 * 60 * 24, // 1일
formats: ['image/avif', 'image/webp'],
},
그 다음, img 태그 대신 <Image>
컴포넌트로 변경해주었다.
<Slider {...settings}>
{images.map((img, idx) => (
<div key={img} className={styles.slide}>
<Image src={img} alt={`img_${idx}`} width={350} height={280} />
{/*<img src={img} alt={`img_${idx}`} width={350} height={280} />*/} ======> 기존 img 태그
</div>
))}
</Slider>
변경 후 네트워크 탭을 열어 살펴봤다. lazy loading
이 적용되어 초기에 10장 불러오던 이미지를 스크린에 보이는 이미지만 로드 하고 있었다.
Next/Image를 적용함으로써 10개의 이미지를 로드하던 기존 방식에서 스크린 내에 노출되는 이미지 6개만 로드하도록 변경 되었다. 이를 통해 기존보다 페이지 로드가 훨씬 빨라졌다.
얼마나 빨라졌는지 수치화 해서 살펴보면 똑같이 10장을 받아온다고 가정했을때 31.8MB 였던 용량이 271.3kB 으로 엄청나게 줄어들었다. 따라서 로드 시간도 24.17초에서 251ms 로 단축되었다🫢
또한 적용 결과, 기존 jpeg 였던 확장자가 avif 포맷
으로 변경되었다. 용량이 작은 포맷으로 변경되었기 때문에 총 용량 크기가 약 99.14% 감소율을 보였다. 정말 엄청난 차이이다. 또한 Next/Image 컴포넌트를 사용하면 최적화된 이미지들은 캐시되어 이후 요청 시 빠르게 제공해준다. 캐시가 만료된 후에 요청이 들어오면 오래된 이미지를 우선 제공하고, 백그라운드에서 이미지 최적화 작업을 다시 진행한다.
next.config.js 설정에서 minimumCacheTTL
옵션을 주어 컨트롤 하거나 CDN 에서 응답한 이미지의 Cache-Control 헤더 중에 더 큰 것으로 정의된다고 한다!
Next.js는 프레임워크 차원에서 최적화 기능을 제공하기 때문에, 손쉽게 성능을 향상시킬 수 있었다.
다음에 리액트로 작업할때도 이미지 최적화를 할때 lazy loading 을 적용해보거나 이번 경험을 떠올리면서 포맷, 캐시 등 여러 테스트를 진행해볼 수 있을 것 같다.