올리브영 테크블로그 포스팅 NEXT.JS와 CDN, 그리고 도커 이미지 경량화
Frontend

NEXT.JS와 CDN, 그리고 도커 이미지 경량화

AWS ECS로 직접 운영하는 노하우

2024.06.16

안녕하세요. 올리브영에서 프론트엔드 개발 업무를 담당하는 코난입니다.

올리브영 프론트엔드는 NEXT.JS 프레임워크를 사용하여 웹 페이지를 개발하고 있습니다.

NEXT.JS 프레임워크를, Vercel이나, AWS Amplify같은 관리형 서비스가 아닌, AWS ECS나 EKS를 사용하여 직접 운영할 때 적용하면 좋은 노하우 2가지를 공유하려고 합니다. 🙇‍♂️

NEXT.JS와 CDN 연동하기

NEXT.JS와 CDN(e.g. AWS Cloudfront)을 연동하는 방법은 NEXT.JS에서 제공하는 옵션인 assetPrefix를 사용하면 됩니다.

next.config.js에 아래와 같이 CDN URL을 추가하면 됩니다.

cdn setting 01

next.config.js - assetPrefix

그리고 NEXT.JS를 next build 명령어를 사용하여 빌드를 했을 때 생성되는 .next 폴더 내부의 static폴더를 CDN의 Origin(e.g. AWS S3)에 업로드 하면 됩니다.

cdn setting 02

.next/static 폴더

Origin에 업로드할 때, .next/static이 아닌, _next/static으로 파일 경로를 변경해서 올려야 정상적으로 동작합니다.

NEXT.JS 프로젝트와 CDN이 정상적으로 연동이 됐다면 아래와 같은 그림의 모습을 확인하실 수 있습니다.

cdn result

NEXT.JS와 CDN 연동 결과

고객이 페이지 진입 시 요청한 첫번째 요청을 노란색으로 표시한 것처럼 NEXT.JS 서버에 의해서 SSR로 렌더링해서 내려주고, 그 외의 페이지를 렌더링하기 위해 필요한 JS와 CSS파일은 CDN을 통해서 제공하는 모습을 확인하실 수 있습니다.

CDN 연동 전/후 비교

CDN 연동 전

next cdn before

CDN 연동 전

CDN 연동 전에는 NEXT.JS 서버에서 하는 일은 다음과 같습니다.

  1. HTML요청 시, SSR 렌더링
  2. JS, CSS와 같은 정적파일 제공
  3. 이미지 최적화

CND 연동 후

next cdn after

CDN 연동 후

CDN 연동 후에는 NEXT.JS 서버에서 하는 일은 다음과 같습니다.

  1. HTML요청 시, SSR 렌더링
  2. 이미지 최적화

기존 NEXT.JS 서버에서 제공하던 정적파일은 CDN을 통해서 제공하기 때문에 NEXT.JS 서버는 SSR과 이미지 최적화에 관련된 부분만 집중할 수 있습니다.

CDN 연동으로 얻는 장점

1. 더 가까운 엣지 포인트

CDN을 사용함으로써 더 가까운 엣지 포인트에서 정적파일을 고객에게 더 빠르게 제공할 수 있습니다.

2. 압축 성능 개선

정적파일 압축(e.g. gzip, brotli)을 NEXT.JS 서버가 아닌, CDN에서 압축을 함으로써 컴퓨팅 파워를 아낄 수 있을 뿐만 아니라, CDN에서 제공하는 압축 알고리즘에 따라 더 적은 용량으로 고객에게 제공할 수 있습니다.

3. HTTP 프로토콜 버전

CDN에서 제공하는 HTTP 프로토콜 버전(e.g. HTTP/2, HTTP/3)을 통해서 최신의 HTTP 프로토콜 버전을 사용하여 고객에게 더 빠르게 정적파일을 제공할 수 있습니다.

NEXT.JS와 도커 이미지 경량화하기

NEXT.JS를 컨테이너로 배포할 때 이미지를 경량화하는 방법은 NEXT.JS에서 제공하는 옵션인 output을 사용하면 됩니다.

next.config.js에 아래와 같이 output: standalone 옵션을 추가하면 됩니다.

standalone setting 01

next.config.js - output

위 옵션을 설정하고 next build 명령어를 사용하여 빌드를 하면, .next 폴더 하위에 standalone 폴더가 생성되고, 이 폴더만을 사용하여 NEXT.JS 서버를 운영할 수 있습니다.

standalone setting 02

.next/standalone 폴더

컨테이너를 위한 이미지를 생성할 때에는 standalone 폴더와, public, 환경 변수(e.g. .env.production, .env.local)등 구동에 필요한 파일만을 포함하여 이미지를 생성하면 됩니다.

FROM node:18.17.0-alpine3.18

ENV PORT 80

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Copy build output files
COPY ./public ./public
COPY ./.next/standalone ./
COPY ./.env.production ./

EXPOSE $PORT

# Running the app
ENTRYPOINT [ "node", "server.js" ]
Dockerfile 예제

output 옵션 적용 전/후 비교

standalone result

output 적용 전/후 비교

output: standalone을 적용하기 전에는 NEXT.JS 서버를 배포하기 위한 컨테이너 이미지가 약 180MB정도 였지만, 적용 후에는 약 70MB정도로 줄어든 것을 확인할 수 있습니다.

이미지 경량화로 얻는 이점

컨테이너 기반으로 서버를 배포하기 위해서는 도커 호스트에서 컨테이너 이미지를 다운(docker pull)받아야 합니다. 이미지 크기가 클수록 다운받는 시간이 걸리기에, 그만큼 배포하는 시간도 지연됩니다.

컨테이너 이미지의 크기를 작게 유지할 수록 이미지를 다운받는 시간이 짧기 때문에, 배포속도에 있어서 이점을 얻을 수 있습니다.

NOTE

내용을 기반으로 한 발표 영상과 자료가 있어 함께 전달드립니다.

아래 발표는 AWS ECS 설정부분도 같이 있기에 블로그 내용은 4장5장을 참고하시면 됩니다.

FrontEndNEXT.JSCDNDocker
올리브영 테크 블로그 작성 NEXT.JS와 CDN, 그리고 도커 이미지 경량화
🙇‍♂️
코난 |
Front-end Engineer
유저를 생각하며 개발하고 있습니다