Next/Image 커스텀로더로 서버 부하 줄이기

2024. 10. 28. 16:25·프로젝트 개선
목차
  1. 문제점 발견: CDN 서버를 먼저 거치게 하고 싶은데 자꾸 Next 서버로 가버린다
  2. 해결책: 커스텀 이미지로더
  3. 추가 문제점: Next 서버없이 이미지 최적화를 할수가 없다,,?
  4. 해결책: webpack 커스텀 로더로 직접 리사이징
  5. 개선 결과
  6. 추가 팁
  7. Full code

안녕하세요 :) 오늘은 nextjs 서버의 이미지 최적화 기능을 직접 구현하여 프론트엔드 서버 부하를 줄이게 된 경험을 공유드리고자 합니다.

 

nextjs 프레임워크를 사용하여 개발을 해보신 적이 있다면 next/image를 사용해보신적이 있으실겁니다.

본론에 들어가기 앞서 next/image를 사용할 때 사용자에게 어떤 방식으로 이미지를 반환하는지 알려드리겠습니다.

1. 브라우저가 www.your-site-url/_next/media/test.png?width=60라는 url로 이미지 요청을 한다.
2. nextjs 서버에서 위 그림과 같이 이미지를 최적화하여 반환해준다.

 

이 방식으로 사용자는 최적화된 이미지를 받을 수 있다는 장점이 있습니다. 하지만 이미지 처리를 위한 서버의 CPU 사용량과 서버 부하가 커질 수 있다는 단점이 있습니다.

 

저희는 이를 해결하기 위해 먼저 CDN 서버를 맨 앞에 두고 정적 자원에 대한 요청을 캐싱하기로 했습니다. 물론 기존 Nextjs 서버에서도 캐싱이 가능했지만 CDN 서버는 EC2 서버보다 비용면에서 훨씬 저렴하고 속도 또한 빠르기 때문이죠.

문제점 발견: CDN 서버를 먼저 거치게 하고 싶은데 자꾸 Next 서버로 가버린다

하지만 한가지 골칫덩이가 있었는데요. 바로 Next/Image의 빌드 방식입니다.

import ImgSrc from '@/static/a/b/test.png';

<Image src={ImgSrc}/>

위와 같은 코드가 있을 때 우리는 빌드 후 아래와 같은 결과물이 나오기를 기대합니다. 그래야 s3 버킷으로 가서 원하는 경로의 이미지를 가져올 수 있기 때문이죠.

<img src="/static/a/b/test.png"/>

하지만, 실제 결과물은 아래와 같습니다.

<img src="/_next/static/media/test.008d7aa.png"/>

파일이 어떤 경로에 있었던 것과 관계없이 /_next/static/media/로 시작하는 경로에 test.008d7aa.png와 같이 파일명에도 hash 값을 붙이게 됩니다. 기존의 경로와 파일명이 완전히 다르게 바뀌는 셈이죠. 어떤 경로를 지정하더라도 nextjs 서버를 결국 거치게 되는 문제가 있었습니다.

 

그래서 제가 받게된 임무는 모든 프로젝트에서 Next/Image를 걷어내고 커스텀 이미지 컴포넌트를 만드는 것이었습니다. 하지만 변경해야할 코드가 너무 많다는 점이 우려되었습니다. 혹시 NextJS에서 제공해주는 방법이 있지않을까 찾아보았습니다. 이런 상황을 과연 고려하지 않았을까 싶었습니다. 🤔

해결책: 커스텀 이미지로더

예상은 틀리지 않았습니다. 이러한 문제를 해결하기 위해 NextJS에서는 커스텀 이미지 로더라는 것을 제공합니다.

import ImgSrc from '@/static/a/b/test.png';

const CustomImageLoader = (src) => {
    const newSrc = `src 가공로직`
    return newSrc;
}

<Image src={ImgSrc} loader={CustomImageLoader}/>

위처럼 커스텀 로더 함수를 이미지에 추가해주면 빌드 후 아래와 같은 결과물이 나오게 됩니다. 우리가 원하는 것이죠.

<img src="/static/a/b/test.png"/>

하나하나 작성하지 않고 적용하려면 next.config.js 파일에 아래와 같이 추가해주면 됩니다.

//custom-image-loader는 따로 작성 -> 글 하단에 링크 첨부
{
  images: {
                  loader: 'custom',
                  loaderFile: './custom-image-loader.js',
           }
}

추가 문제점: Next 서버없이 이미지 최적화를 할수가 없다,,?

Next 서버를 거쳤을 때의 이점은 이미지를 리사이징해서 반환해주었다는 것입니다. 예컨대 아래와 같은 이미지가 있습니다.

import ImgSrc from '@/static/a/b/test.png';

<Image src={ImgSrc} width={750}>

기존처럼 next 서버를 거치게 되면 경로는 아래와 같을 것입니다.

<img src="/_next/static/media/test.008d7aa.webp?width=750"/>

경로에 ?width=750 라는 파라미터를 붙여주게 되면 next 서버에서 이미지를 750으로 잘라서 반환해줍니다. 하지만 이제 우리는 Next 서버를 거치지 않기로 했으니 이 역할을 해줄수가 없게된 것이죠.

해결책: webpack 커스텀 로더로 직접 리사이징

이 문제를 해결하기 위해 저는 두가지 방법을 고민했습니다. 첫번째는 aws의 람다 함수를 사용하는 것입니다. nextjs 서버가 해주던 것처럼 이미지 요청이 들어오면 그때그때 이미지를 최적화해서 반환해주는 것이죠. 하지만 이 방식은 비용이 많이 든다는 단점이 있었습니다.

인프라팀과의 논의 끝에 미리 이미지를 잘라놓는 방법을 택했습니다.

 

빌드 타임에 이미지 최적화를 해주는 webpack 커스텀로더를 추가하는 것입니다. 프로젝트에서 사용된 이미지 파일을 하나하나 돌면서 저장해놓는것이죠. 모든 이미지를 자르는 것은 아닙니다. 아래처럼 예상 breakpoint를 잡아놓고 이미지의 width 속성을 받아 자를 필요가 있는 이미지만 잘라놓습니다.

const SCREEN_BREAK_POINT = {
    sm: 640,
    md: 768,
    lg: 1024,
    xl: 1280,
};

예를 들어 가로가 900인 이미지가 있다면 lg나 xl은 필요가없겠죠. sm과 md 사이즈로만 미리 준비해놓는 것입니다. 결과물은 optimized라는 폴더에 저장이 되고 사용하는 쪽에서 필요한 사이즈의 이미지를 요청하게 됩니다.

import ImgSrc from '@/static/a/b/test.png';

<Image src={ImgSrc}/ width={750}>

이런 이미지가 있다면 아래 이미지 중 test.md.webp를 요청하면 되겠지요?

test.sm.webp //640
test.md.webp //768
test.webp //원본

개선 결과

이미지 로더를 적용한 이후 인프라 비용을 대폭 감소(CPU 사용량 50%, 서버 outbound 25% 감소) 시킬 수 있었습니다.

 

추가 팁

Next/Image를 사용하시는 분들은 아마 두가지 방식으로 이미지를 처리하실텐데요.

import ImgSrc from '@/static/a/b/test.png';

// 첫번째
<Image src={ImgSrc} width={750}>

// 두번째
<Image src={ImgSrc} fill />

반응형 구현을 할때 두번째 방식을 많이 사용하실겁니다. 하지만 두번째 방식은 width 값을 몰라서 가장 큰 이미지를 불러오기 때문에 최적화면에서 좋지 않습니다. 그러니 되도록 첫번째 방식을 추천드립니다 :)

Full code

현재 위의 커스텀 이미지로더와 웹팩 리사이징 로더는 nextjs-image-loader라는 이름으로 배포되어있는 상태입니다.
full code가 궁금하시다면 이곳을 참고해주세요 :)

'프로젝트 개선' 카테고리의 다른 글

requestAnimationFrame으로 드로잉 성능 최적화하기  (0) 2025.05.29
Swagger에서 타입 자동 생성하기: 직접 만든 OpenAPI Generator  (0) 2025.03.20
거대한 컴포넌트, 복합 컴포넌트 패턴으로 깔끔하게 정리하기  (0) 2025.03.10
tailwind css에서 classname 정렬하기 (tagged template literals)  (1) 2024.10.30
라이브러리 크기가 클 때 성능 개선하기 (코드 스플리팅, 부분 import)  (2) 2024.10.28
  1. 문제점 발견: CDN 서버를 먼저 거치게 하고 싶은데 자꾸 Next 서버로 가버린다
  2. 해결책: 커스텀 이미지로더
  3. 추가 문제점: Next 서버없이 이미지 최적화를 할수가 없다,,?
  4. 해결책: webpack 커스텀 로더로 직접 리사이징
  5. 개선 결과
  6. 추가 팁
  7. Full code
'프로젝트 개선' 카테고리의 다른 글
  • Swagger에서 타입 자동 생성하기: 직접 만든 OpenAPI Generator
  • 거대한 컴포넌트, 복합 컴포넌트 패턴으로 깔끔하게 정리하기
  • tailwind css에서 classname 정렬하기 (tagged template literals)
  • 라이브러리 크기가 클 때 성능 개선하기 (코드 스플리팅, 부분 import)
권끼리마끼리
권끼리마끼리
  • 권끼리마끼리
    끼리마끼리의 개발노트
    권끼리마끼리
    • 분류 전체보기 (60)
      • 트러블슈팅 (5)
      • 프로젝트 개선 (6)
      • 강의노트 (19)
      • 웹 접근성 (27)
      • 웹개념 파보기 (3)
  • hELLO· Designed By정상우.v4.10.0
권끼리마끼리
Next/Image 커스텀로더로 서버 부하 줄이기
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.