Next.js standalone 배포 "sharp missing in production" 에러 해결하기

Next.js의 next/image 컴포넌트는 이미지 최적화를 위해 서버 측에서 sharp 모듈을 사용합니다. 하지만 Docker 기반의 production 환경에서는 종종 아래와 같은 오류가 발생합니다.

Error: 'sharp' is required to be installed in standalone mode for the image optimization to function correctly.

 

개발 환경에서는 멀쩡히 동작하던 기능이 운영 환경에서 갑자기 실패하는 이유는 무엇일까요? 이 글에서는 해당 오류의 원인과 해결 방법을 명확히 정리해 봅니다.


1. 개발 환경에서는 왜 문제가 없을까?

개발 중에는 sharp와 관련된 문제가 드러나지 않는 경우가 대부분입니다. 그 이유는 아래와 같습니다.

1) 개발 환경은 glibc 기반

대부분의 로컬 개발 환경(macOS, Ubuntu 등)은 glibc 기반입니다. 이 환경에서는 npm install 시 sharp가 자동으로 현재 시스템에 맞는 바이너리를 설치하고, 문제 없이 작동합니다.

2) sharp가 없어도 fallback 가능

만약 sharp가 설치되어 있지 않거나, 설치에 실패한 경우에도 Next.js는 자동으로 squoosh라는 WebAssembly 기반 이미지 최적화 도구를 fallback으로 사용합니다. 이 도구는 설치가 간편하고 플랫폼 제약이 없어, 개발 환경에서는 큰 문제 없이 작동하게 됩니다.

3) 그래서 문제를 인지하지 못함

이처럼 sharp가 정상 작동하거나 squoosh로 대체되기 때문에, 개발 중에는 이미지 최적화 기능이 잘 작동하는 것처럼 보입니다. 실제로는 fallback이 발생했음에도 이를 인지하지 못한 채 배포까지 진행하게 되는 경우가 많습니다.


2. 운영 환경에서는 왜 오류가 발생할까?

Next.js를 production에 배포할 때는 보통 다음과 같은 구성이 사용됩니다.

  • Docker + node:alpine 기반 이미지
  • Next.js standalone 모드 빌드
  • sharp 모듈 포함

이 환경에서는 sharp가 아래와 같은 이유로 실행에 실패할 수 있습니다.

1) Alpine 리눅스는 glibc가 아니다

Alpine은 이미지 크기를 줄이기 위해 musl libc를 사용합니다. 반면, sharp는 기본적으로 glibc 기반 환경에서 실행되는 바이너리를 제공합니다. 이 둘은 호환되지 않습니다.

2) standalone 빌드 특성

Next.js의 standalone 모드는 필요한 파일만 .next/standalone 디렉토리로 복사합니다. sharp도 포함되지만, 환경이 다르면 포함되어도 실행되지 않습니다.

3) fallback이 작동하지 않음

production에서는 sharp가 명시적으로 포함되어 있기 때문에 fallback이 일어나지 않습니다. 따라서 sharp가 실행되지 못하면 곧바로 오류가 발생합니다.


3. 운영 환경에서는 sharp를 써야 하는 이유

Next.js는 기본적으로 sharp를 우선 사용하고, 문제가 있을 때 squoosh로 대체합니다. 하지만 공식 문서에서도 운영 환경에서는 sharp 사용을 강력하게 권장하고 있습니다.

  • sharp는 C++ 기반 네이티브 모듈로 고속 이미지 처리가 가능하고, 리소스 사용량도 효율적입니다.
  • 반면 squoosh는 범용성과 안정성은 높지만, 이미지 처리 성능은 상대적으로 낮습니다.
  • 특히 서버 트래픽이 많거나 이미지 크기가 큰 환경에서는 sharp가 훨씬 유리합니다.

4. 해결 방법: sharp를 Alpine에 맞게 재빌드

Alpine 환경에서 sharp를 정상적으로 실행하려면 해당 환경에 맞는 바이너리를 직접 빌드해야 합니다. 다음 명령어를 통해 musl libc 기반으로 재컴파일합니다.

npm rebuild sharp --platform=linuxmusl --arch=x64

이 명령은 현재 컨테이너 환경에 맞는 .node 바이너리를 생성합니다.


5. Dockerfile 예시

FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json package-lock.json* ./
RUN npm ci
RUN npm rebuild sharp --platform=linuxmusl --arch=x64

sharp는 반드시 dependencies에 포함되어야 standalone 모드에서도 복사됩니다. devDependencies에만 있으면 누락될 수 있습니다.


6. 요약

  • 개발 환경에서는 sharp가 설치되거나, 실패해도 squoosh가 자동으로 대체해주기 때문에 문제가 드러나지 않는다.
  • 운영 환경에서는 sharp가 실행되지 못하면 fallback 없이 오류가 발생한다.
  • Alpine 리눅스는 musl libc 기반이므로, 기본 sharp 바이너리와 호환되지 않는다.
  • 해결 방법은 npm rebuild sharp --platform=linuxmusl 명령어로 환경에 맞는 바이너리를 재컴파일하는 것이다.

Next.js의 standalone + Docker 배포는 효율적이지만, 네이티브 모듈이 포함될 경우 실행 환경에 맞는 빌드 전략이 필요합니다. 특히 이미지 최적화를 안정적으로 운영하려면 sharp를 환경에 맞게 구성해주는 과정이 필요합니다.