← 글 목록

notes

React에서 JavaScript heap out of memory — cross-env로 팀에 고정하기

FATAL ERROR: heap out of memory. 셸에만 NODE_OPTIONS를 넣지 말고, cross-env로 package.json에 고정해 Windows·Linux·CI까지 같이 가져가자.

무슨 일이었나

레거시를 버리고 TypeScript + React로 옮기던 때였다. 필요한 패키지를 잔뜩 깔고 start를 돌렸더니 바로 죽었다.

FATAL ERROR: Ineffective mark-compacts near heap limit
Allocation failed - JavaScript heap out of memory

처음엔 “내가 깐 라이브러리 중 하나가 망가졌나” 싶었다. 하나씩 넣었다 뺐다 하다 plotly.js를 넣는 순간 재현됐다. 그런데 plotly “버그”라기보다, 번들러(당시 Webpack)가 트랜스파일·번들하는 과정에서 Node(V8) 힙 한도를 넘긴 것에 가까웠다.

빌드 프로세스가 V8 Old Space 한도를 넘기면 heap OOM

빠른 우회: NODE_OPTIONS

구글링하면 답이 단순하다. Node에 old space 상한을 키운다.

PowerShell

$env:NODE_OPTIONS="--max-old-space-size=8192"

bash / zsh

export NODE_OPTIONS="--max-old-space-size=8192"

8192는 MB 단위다 (대략 8GB). 노트북/CI 램에 맞게 4096·6144부터 올려도 된다.

여기까지는 “나 혼자 터미널에서”는 해결된다. 문제는 그다음이다.

  • VS Code / 터미널을 다시 열면 설정이 사라진다.
  • 메인에 “알아서 export 하세요”를 올리는 건 팀에 무책임하다.
  • Windows 문법과 bash 문법이 달라서, OS별로 스크립트를 갈라 쓰기 쉽다.

팀에 고정: package.json + cross-env

셸에 붙이지 말고, 실행 스크립트에 환경을 박아 둔다. OS 차이를 흡수하려면 cross-env가 여전히 실용적이다.

npm i -D cross-env
{
  "scripts": {
    "start": "cross-env NODE_OPTIONS=--max-old-space-size=8192 webpack-dev-server --mode development",
    "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 webpack --mode production"
  }
}

Vite / Next / CRA 계열도 원리는 같다. 앞에 cross-env NODE_OPTIONS=--max-old-space-size=8192만 붙이면 된다.

셸별 문법 차이를 cross-env가 한 줄로 맞춤

왜 cross-env인가

Unix만 쓰는 팀이면 NODE_OPTIONS=... command 문법이 통할 때도 있다. 그래도 Windows 동료·Git Bash·PowerShell이 섞이면 cross-env가 싸게 먹힌다.

해결 사다리 (어디까지 올릴까)

임시 셸 → 스크립트 고정 → 근본 최적화
  • 셸에만 — 재현 확인용.
  • package.json + cross-env — 팀·CI 기본선.
  • 근본 — 힙만 계속 키우면 러너/노트북에서 또 터진다.
  • 무거운 차트·에디터는 동적 import()
  • 개발 빌드에서 불필요한 source map 끄기
  • 모노레포면 패키지 단위로 빌드 쪼개기
  • CI는 메모리 큰 러너 또는 Node 옵션을 워크플로에 명시

메모

  • increase-memory-limit 같은 옛 글로벌 패치 패키지에 기대지 말자. 스크립트에 NODE_OPTIONS를 직접 쓰는 편이 투명하다.
  • 에러 문구의 mark-compacts near heap limit는 GC가 한도 근처에서 허우적대다 실패한 신호다. “React 버그”로 단정하지 말 것.
  • Docker / GitHub Actions에서도 동일하게 NODE_OPTIONS 또는 cross-env가 들어간 script를 호출하면 된다. 로컬만 고치고 CI는 방치하면 같은 이슈가 파이프라인에서 재발한다.
  • 값을 무작정 16384로 올리면 스왑만 늘고 체감이 나빠질 수 있다. 재현되는 최소치부터 올리는 게 낫다.

체크리스트

  • 로컬에서 OOM 재현 확인
  • cross-env + NODE_OPTIONS=--max-old-space-size=… 를 start/build에 반영
  • Windows / macOS / Linux 중 최소 두 환경에서 npm start 확인
  • CI 워크플로가 같은 script를 쓰는지 확인
  • 여전히 빡세면 무거운 의존성·번들 설정을 손본다 (한도만 키우지 않는다)

한 줄 요약

heap out of memory는 대개 “그 라이브러리 한 줄”이 아니라 빌드 프로세스 메모리 한도 문제다. 셸에 임시로 넣지 말고, cross-env로 npm script에 고정해 팀에 공유하자.