단 하나의 동적 아이콘 임포트가 16GB RAM을 탑재한 Windows Subsystem for Linux 2(WSL2) 환경의 개발 서버를 마비시켰습니다. 이로 인해 vmmemWSL 프로세스가 가용 메모리를 모두 점유하며 전체 Linux 창이 멈춰버리는 현상이 발생했습니다. 이 충돌은 Turbopack을 사용하는 Next.js 16 프로젝트를 실행하던 중 발생했으며, .wslconfig 파일을 통해 VM의 RAM 사용량을 제한했음에도 불구하고 일어났습니다.

작은 임포트가 괴물이 될 수 있는 이유

개발자는 동적 엔트리 포인트를 사용하여 런타임에 아이콘을 해석(resolve)하려고 시도했습니다. 이 임포트는 약 9,000개의 모듈을 포함하는 아이콘 패키지를 불러왔습니다. Next.js 16 개발 서버의 핵심인 Rust 기반 번들러 Turbopack은 접촉하는 모든 패키지에 대해 전체 모듈 맵(module map)을 구축합니다. 개발 모드에서 이 맵은 메모리에 상주하며 파일이 변경될 때마다 업데이트됩니다. 전체 아이콘 패키지를 로드하면서 Turbopack은 방대한 양의 RAM을 할당해야 했고, 이는 곧 .wslconfig 파일로 설정된 엄격한 제한치에 도달하게 만들었습니다. 한계치에 도달하자 WSL VM은 응답을 멈췄습니다. Ctrl + C도 작동하지 않았으며, 유일한 해결 방법은 Windows 호스트를 강제로 종료하는 것뿐이었습니다.

동일한 코드의 프로덕션 빌드는 성공했습니다. 번들러가 그래프를 한 번만 컴파일하고, 에셋을 생성한 뒤 종료되기 때문입니다. 반면, 개발 서버는 핫 리로딩(hot-reloading)을 지원하기 위해 그래프를 메모리에 계속 유지합니다. 따라서 프로덕션 빌드가 성공했다고 해서 개발 환경이 동일한 임포트 패턴을 견뎌낼 수 있다는 보장은 없습니다.

더 넓은 관점에서의 문제점

Windows 내부에서 Linux 기반 툴체인을 사용하는 개발자들은 리소스 사용을 격리하기 위해 WSL2에 의존합니다. 단 하나의 임포트가 VM의 메모리를 고갈시키면 호스트 전체가 느려지거나 응답하지 않을 수 있으며, 이는 동일한 머신에서 실행 중인 다른 컨테이너나 애플리케이션에도 영향을 미칩니다. 또한 이번 사례는 일반적인 Node.js 메모리 튜닝 플래그와 Turbopack 아키텍처 간의 불일치를 보여줍니다. Turbopack은 V8이 아닌 Rust에서 실행되므로, Node의 --max-old-space-size 플래그를 늘려도 RAM 소비를 억제하는 데 아무런 도움이 되지 않습니다.

실제로 무엇이 잘못되었나

  • 동적 엔트리 포인트: 임포트 문이 번들러에게 전체 아이콘 패키지를 하나의 지연 로딩(lazily-loaded) 모듈로 취급하도록 요청했습니다. 이에 Turbopack은 모듈 맵을 구축하기 위해 모든 파일을 즉시(eagerly) 파싱하며 대응했습니다.
  • 개발 서버 그래프: 일회성 프로덕션 컴파일과 달리, 개발 서버는 파일 변경 시 즉각적인 피드백을 제공하기 위해 전체 의존성 그래프를 RAM에 유지합니다.
  • 메모리 제한: .wslconfig 파일이 VM의 메모리를 호스트 RAM의 일부로 제한했습니다. Turbopack의 요구량이 이 제한을 초과하자 VM이 멈춰버렸습니다.

메모리를 절반으로 줄이는 해결책

개발자는 세 가지 실질적인 변경을 적용하여 RAM 사용량을 3.6GB에서 1.87GB로 줄이고 안정성을 회복했습니다.

  1. 작은 에셋에 동적 엔트리 포인트 사용 중단 – 필요한 아이콘만 임포트하십시오. 예: import { SearchIcon } from 'icon-pack/search'. 아주 적은 수의 아이콘만 필요하다면 인라인 SVG를 사용하는 것이 훨씬 더 가볍습니다.
  2. next.config.ts에서 optimizePackageImports 활성화 – 이 옵션은 Turbopack이 패키지 루트 대신 구체적인 파일 경로로 임포트를 해석하도록 하여, 패키지 트리 전체를 로드하는 것을 방지합니다.
  3. Node 힙 플래그에 의존하지 말 것 – Turbopack의 메모리 사용량은 Rust 런타임에 의해 제어되므로, --max-old-space-size는 이 문제에 아무런 영향을 주지 않습니다.

.wslconfig 제한을 유지하는 것은 여전히 권장됩니다. 엄격한 제한을 두면 VM이 Windows 호스트를 다운시키는 대신 일시 중지될 수 있어, 모든 것이 멈추기 전에 개입할 기회를 얻을 수 있습니다.

워크플로우에서 주의 깊게 살펴볼 점

  • 메모리 대시보드: WSL 내부의 htop이나 Windows 작업 관리자와 같은 도구를 사용하여 vmmemWSL이 급증하는 시점을 확인할 수 있습니다. 사용량이 설정된 제한치에 근접하면 알림을 설정하십시오.
  • 패키지 크기 감사: 라이브러리를 추가하기 전에 해당 라이브러리가 얼마나 많은 모듈을 포함하고 있는지 확인하십시오. 대규모 아이콘 팩, 유틸리티 모음 또는 컴포넌트 라이브러리는 개발 그래프를 조용히 비대하게 만들 수 있습니다.
  • 선택적 임포트: 와일드카드 임포트나 동적 임포트보다는 명명된 임포트(named imports)나 직접적인 파일 경로를 선호하십시오. 특히 번들러가 모든 것을 메모리에 유지하는 개발 환경에서는 더욱 중요합니다.
  • 프로덕션과 개발 환경의 일치성: 프로덕션 빌드 성공을 별도의 검증 단계로 간주하십시오. 핫 리로딩 중에만 나타나는 문제를 포착하기 위해 메모리 모니터를 켜둔 상태로 개발 서버를 실행해 보십시오.

요약

단 하나의 동적으로 임포트된 아이콘 패키지가 호스트 리소스를 의도적으로 제한한 상황에서도 WSL2 기반 Next.js 개발 서버를 무력화할 만큼의 RAM을 소비할 수 있습니다. 광범위한 동적 임포트를 피하고, 패키지 수준의 임포트 최적화를 활성화하며, 메모리 사용량을 모니터링함으로써 개발자는 Windows 내부의 Linux 환경을 쾌적하게 유지하고 강제 종료 사태를 방지할 수 있습니다.