/개발/Node.js error:0308010C digital envelope unsupported 해결 런북
개발Node.jsERR_OSSL_EVP_UNSUPPORTED

Node.js error:0308010C digital envelope unsupported 해결 런북

Node 17/18/20 업그레이드 후 error:0308010C digital envelope routines unsupported와 ERR_OSSL_EVP_UNSUPPORTED로 webpack 빌드가 깨졌나요? 30초 진단표와 --openssl-legacy-provider 우회부터 webpack 5 근본 해결까지 복붙 런북으로 끝냅니다.

Node.js error:0308010C digital envelope unsupported 해결 런북

어제까지 되던 빌드가 Node만 올렸더니 깨졌다

코드는 한 줄도 안 건드렸는데 갑자기 빌드가 터진 상황, 십중팔구 아래 에러 원문일 겁니다. 일단 로그에서 이 세 형제 중 하나를 확인하세요.

TEXT
Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    ...
  opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
  library: 'digital envelope routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_EVP_UNSUPPORTED'

이 에러의 트리거는 대부분 하나입니다: 로컬에서 nvm으로 Node를 올렸거나, CI 러너의 기본 Node 이미지가 올라갔거나(예: actions/setup-node의 default, node:lts Docker 태그), 팀원이 .nvmrc를 바꿨거나. 즉 코드가 아니라 런타임 환경이 바뀐 것이 원인입니다.

적용 범위: Node.js 17 이상(17/18/20/22), webpack 4 기반 빌드 파이프라인 — react-scripts(CRA) 4.x, @vue/cli-service 4.x, gatsby 구버전, 오래된 storybook 등. OpenSSL 3.0을 내장한 모든 OS(Windows/macOS/Linux)에서 동일하게 발생합니다.

이 글은 개념 강의가 아니라 복붙 런북입니다. 30초 진단표로 원인을 특정하고, 5분 우회(경로 A)와 근본 해결(경로 B)을 바로 적용하세요.

30초 진단표: 에러 문자열 → 원인 → 즉시 조치

로그에서 잡힌 문자열을 왼쪽 열에서 찾아 오른쪽 조치로 바로 이동하세요.

에러 문자열Node 버전유력 원인즉시 조치(경로 A/B)
error:0308010C:digital envelope routines::unsupported17+ (17에서 기본값 전환)OpenSSL 3.0이 legacy 해시(MD4 등) 기본 비활성화 → webpack4 청크 해시 계산 실패A: NODE_OPTIONS=--openssl-legacy-provider / B: webpack 5·빌드툴 업그레이드
ERR_OSSL_EVP_UNSUPPORTED17+위와 동일 원인의 Node 에러 코드 형태A 동일 / B 동일
digital envelope routines::initialization error (03000086)17+동일 원인, opensslErrorStack에 함께 찍히는 하위 에러A 동일 / B 동일

핵심 분기점은 Node 17입니다. Node 16까지는 OpenSSL 1.1.1을 번들했지만, Node 17부터 OpenSSL 3.0이 기본값이 되었고 18/20/22 LTS도 이를 그대로 이어받았습니다. OpenSSL 3.0은 보안상 낡은 legacy provider 알고리즘을 기본으로 끄기 때문에, 이를 내부적으로 쓰던 구버전 webpack에서 위 에러가 뜹니다.

원인은 한 줄로 끝납니다: OpenSSL 3.0이 MD4 기반 등 legacy 해시 알고리즘을 기본 비활성화 → webpack4의 청크 해시(파일명 hash) 계산이 실패. 그 이상의 배경 지식은 해결에 필요 없습니다.

ESM/CommonJS 계열 에러(Cannot use import statement outside a module, require is not defined)는 원인이 완전히 다릅니다. 그쪽이면 **'Cannot use import statement outside a module' 해결 런북**을 보세요 — 이 글은 OpenSSL 해시 계열만 다룹니다.

경로 A — 임시 우회 (5분 컷)

지금 당장 빌드를 돌려야 할 때 쓰는 우회책입니다. --openssl-legacy-provider 플래그로 OpenSSL의 legacy provider를 다시 켜 줍니다.

방법 1) package.json 스크립트에 플래그 직접 삽입

react-scripts 계열은 CLI 인자로 바로 넣을 수 있습니다.

JSON
{
  "scripts": {
    "start": "react-scripts --openssl-legacy-provider start",
    "build": "react-scripts --openssl-legacy-provider build"
  }
}

Vue CLI라면 서비스 명령 앞에 붙입니다.

JSON
{
  "scripts": {
    "serve": "vue-cli-service --openssl-legacy-provider serve",
    "build": "vue-cli-service --openssl-legacy-provider build"
  }
}

방법 2) cross-env로 NODE_OPTIONS 주입 (OS 공통, 권장)

CLI가 플래그를 안 받거나 OS를 가리지 않고 하나로 통일하고 싶다면 cross-env + NODE_OPTIONS가 가장 안전합니다.

Bash
npm i -D cross-env
JSON
{
  "scripts": {
    "start": "cross-env NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
    "build": "cross-env NODE_OPTIONS=--openssl-legacy-provider react-scripts build"
  }
}

cross-env를 쓰면 Windows의 set, Unix의 export 문법 차이를 신경 쓸 필요가 없습니다. 팀 프로젝트라면 이 방식을 추천합니다.

방법 3) 셸에서 임시 환경변수 (한 번만 돌릴 때)

macOS / Linux (bash·zsh)

Bash
export NODE_OPTIONS=--openssl-legacy-provider
npm run build

Windows CMD

CMD
set NODE_OPTIONS=--openssl-legacy-provider
npm run build

Windows PowerShell

POWERSHELL
$env:NODE_OPTIONS = "--openssl-legacy-provider"
npm run build

예상 정상 결과: 이전에 ERR_OSSL_EVP_UNSUPPORTED에서 즉시 죽던 빌드가 정상적으로 컴파일을 진행하고 Compiled successfully 또는 번들 산출물이 생성됩니다.

예상과 다를 때 분기:

  • 여전히 같은 에러 → node -v로 실제 실행 Node가 17+인지 확인. 셸 세션이 갈아엎여 환경변수가 안 먹었을 수 있습니다.
  • --openssl-legacy-provider is not allowed in NODE_OPTIONSNode 22+ 일부 상황에서 플래그가 거부될 수 있습니다. 이 경우 경로 B(업그레이드)가 사실상 강제입니다.
  • 에러 문자열이 ERR_REQUIRE_ESM·import 관련으로 바뀌었다면 OpenSSL 문제는 해결됐고 별개의 ESM 이슈입니다.

방법 4) Dockerfile ENV

컨테이너 빌드라면 이미지 레벨에서 박아 넣습니다(단, 4~5번은 임시 이미지에만 — 뒤 주의사항 참고).

Dockerfile
FROM node:20-alpine
WORKDIR /app
ENV NODE_OPTIONS=--openssl-legacy-provider
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

방법 5) GitHub Actions env 블록

CI에서 깨졌다면 워크플로에 환경변수를 주입합니다. step 레벨(권장)과 job 레벨 둘 다 가능합니다.

YAML
name: build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    # job 레벨: 이 job의 모든 step에 적용
    env:
      NODE_OPTIONS: --openssl-legacy-provider
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      # step 레벨: 이 step에만 국한하고 싶을 때
      - run: npm run build
        env:
          NODE_OPTIONS: --openssl-legacy-provider

setup-nodenode-version을 고정하지 않으면 러너 기본값이 올라갈 때 또 깨집니다. 버전 고정 요령은 **GitHub Actions Node 빌드 환경변수 설정 글**에서 캐시·매트릭스 빌드까지 함께 정리했습니다.

경로 B — 근본 해결: legacy provider 없이 빌드하기

우회는 "보안적으로 꺼진 알고리즘을 되살리는" 것이므로 영구책이 될 수 없습니다. 이번 스프린트에 아래로 넘어가세요.

1단계 — 현재 webpack 버전 확인

Bash
npm ls webpack

예상 정상 결과(해결 대상): [email protected]가 찍히면 이 에러의 근본 원인입니다.

[email protected]가 이미 잡히는데도 에러가 난다면, 중첩 의존성에 webpack4가 물려 있을 수 있으니 npm ls webpack --all로 트리 전체를 확인하세요.

2단계 — 빌드 툴 업그레이드 대응표

webpack 5는 대부분 빌드 툴을 올리면 자동으로 따라옵니다. 직접 webpack만 올리기보다 상위 툴을 올리는 게 안전합니다.

빌드 툴webpack 5 지원 시작조치 명령
react-scripts (CRA)5.0.0부터npm i react-scripts@5
@vue/cli-service5.0.0부터npm i -D @vue/cli-service@^5
gatsby최신 메이저npm i gatsby@latest
순수 webpack 프로젝트5.xnpm i -D webpack@5 webpack-cli@latest
Bash
# CRA 예시
npm i react-scripts@5
npm dedupe

3단계 — 우회 옵션 제거 후 검증

업그레이드가 끝나면 경로 A에서 넣었던 플래그·환경변수를 모두 제거하고 깨끗한 상태에서 빌드가 통과하는지 확인합니다.

Bash
# 1) package.json에서 --openssl-legacy-provider / NODE_OPTIONS 제거
# 2) 캐시·모듈 초기화
rm -rf node_modules package-lock.json
npm install
# 3) 환경변수 없는 상태에서 빌드
unset NODE_OPTIONS   # Windows PowerShell: Remove-Item Env:\NODE_OPTIONS
npm run build

예상 정상 결과: NODE_OPTIONS 없이도 Compiled successfully. 이제 legacy provider에 의존하지 않습니다.

예상과 다를 때 분기:

  • 여전히 ERR_OSSL_EVP_UNSUPPORTEDnpm ls webpack --all로 webpack4가 남아 있는지 재확인. 서드파티 플러그인이 물고 있을 수 있습니다.
  • 다른 빌드 에러로 바뀜(예: 폴리필 누락 Buffer/process) → 이는 webpack 5의 정상적인 breaking change입니다. resolve.fallback 또는 node-polyfill-webpack-plugin으로 개별 대응하세요. OpenSSL 문제는 이미 해결된 상태입니다.

CRA를 붙잡을지, Vite로 넘어갈지

react-scripts(CRA)는 유지보수가 사실상 정체되어, 신규 프로젝트에서는 Vite 이전이 대세가 되었습니다. webpack 4→5 마이그레이션 비용이 크다면, 같은 노력으로 Vite로 넘어가는 선택지도 함께 저울질할 가치가 있습니다. Vite는 esbuild/Rollup 기반이라 이 OpenSSL 이슈 자체가 발생하지 않습니다.

결론 + 트러블슈팅 체크리스트

우회는 지금, 근본 해결은 이번 스프린트. 아래 순서대로 밟으면 됩니다.

  • 로그에서 0308010C / ERR_OSSL_EVP_UNSUPPORTED / initialization error 확인
  • node -v로 17+ 여부 확인 (원인 특정)
  • 급하면: cross-env NODE_OPTIONS=--openssl-legacy-provider로 우회
  • 우회 플래그는 CI에 영구 박제 금지 — 임시 조치임을 커밋 메시지·PR에 명시
  • npm ls webpack로 webpack4 확인
  • react-scripts@5 / @vue/cli-service@5 / gatsby@latest로 업그레이드
  • 우회 옵션 제거 후 NODE_OPTIONS 없이 빌드 통과 검증
  • Node 버전을 .nvmrc·setup-node·Docker 태그에 고정해 재발 방지

주의: --openssl-legacy-provider는 OpenSSL 3.0이 보안상 비활성화한 알고리즘을 되살리는 플래그입니다. 개발 편의를 위한 임시 조치일 뿐이며, 프로덕션 CI 파이프라인에 영구히 남기지 마세요. 근본 해결(webpack 5) 후에는 반드시 걷어내는 것을 전제로 사용해야 합니다.

재발 방지의 핵심은 Node 버전 고정입니다. 로컬은 .nvmrc/Volta, CI는 setup-nodenode-version을 명시해 러너 기본값 변경에 흔들리지 않게 하세요 — 구체적인 방법은 **Node 버전 관리(nvm/Volta) 가이드**에 정리해 두었습니다.

참고: 공식 문서

이 글에서 다루는 동작·설정·에러의 1차 출처는 다음 공식 문서입니다. 버전별 옵션과 정확한 동작은 여기서 확인하세요.

자주 묻는 질문 (FAQ)

Q. --openssl-legacy-provider를 그냥 CI에 계속 둬도 되나요? A. 권장하지 않습니다. 보안상 비활성화된 legacy 알고리즘을 되살리는 플래그라 임시 우회용입니다. webpack 5로 업그레이드해 플래그 없이 빌드되도록 한 뒤 제거하는 것이 정석입니다.

Q. Node 20/22 LTS로 올렸는데 플래그가 아예 안 먹히거나 거부됩니다. A. 최신 Node에서는 NODE_OPTIONS의 legacy provider 플래그가 제한될 수 있습니다. 이 경우 우회가 불가능하므로 경로 B(webpack 5·빌드 툴 업그레이드)가 사실상 강제입니다. 마이그레이션 비용이 크면 Vite 이전도 검토하세요.

Q. webpack을 5로 올렸는데도 같은 에러가 납니다. A. npm ls webpack --all로 의존성 트리를 확인하세요. 서드파티 플러그인이나 중첩 의존성에 webpack4가 남아 있으면 에러가 계속됩니다. 해당 패키지를 최신 버전으로 올리거나 npm dedupe로 중복을 정리하세요.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

초안 · AI (Content Reviewer)·검토 · Nodelog 편집자·발행 ·

댓글

첫 번째 댓글을 남겨보세요.