로컬에서는 멀쩡하던 프로젝트가 배포 직전 로그도 없이 멈춰버렸다. 로그가 없어 막막한 상황에서 CRACO와 분석 도구를 활용해 원인을 찾아내고 전체 번들 구조를 개선한 트러블 슈팅 과정을 정리했다.
1. 'Creating an optimized production build...'
배포를 위해 npm run build를 실행했지만 터미널은 Creating an optimized production build... 메시지 이후 어떠한 응답도 없었다.
- 에러 로그 없는 무한 대기 상태
- 메모리 부족이 원인이라 판단하여 $env:NODE_OPTIONS="--max_old_space_size=4096" 명령어로 힙 메모리를 강제 확장했으나 빌드는 여전히 기약이 없었다.
2. 가설 검증
로그가 없는 상황에서 세운 가설은 "웹팩이 너무 거대한 코드를 압축하느라 연산 과부하에 걸렸다"는 것이었다. 이를 확인하기 위해 CRACO를 도입하고 최적화 기능을 강제로 끄는 실험을 진행했다.
- 압축 안 해도 좋으니 일단 결과물만 내놓도록 진행
- craco.config.js에서 webpackConfig.optimization.minimize = false 설정 및 상세 로그(--verbose) 활성화했다.
- 드디어 빌드가 성공하며 결과가 나타났다.
Compiled successfully. 7.54 MB build\static\js\main.488a8588.js "The bundle size is significantly larger than recommended." - 7.54MB라는 큰 덩어리가 원인이었다. 이 거대한 파일을 압축하려니 웹팩이 견디지 못하고 멈췄던 것이다.
3. 중복 라이브러리(DND) 제거
이제 7.54MB의 무게를 줄이기 위해 package.json을 조사하여 빌드 엔진에 부하를 주는 요소를 찾아냈다.
- 드래그 앤 드롭 구현을 위해 react-beautiful-dnd와 react-dnd가 중복 설치되어 있었다.
- 두 라이브러리는 내부 엔진이 다르고 덩치가 매우 크다. 이 거대 모듈들의 그래프를 동시에 분석하는 것 자체가 웹팩에는 엄청난 짐이었다.
- 사용하지 않던 react-dnd 및 관련 패키지(dnd-core 등)를 삭제했다.
4. 블랙박스 해체
라이브러리 중복 제거 후에도 여전히 용량은 컸다. CRACO로 주입한 webpack-bundle-analyzer를 통해 번들 내부를 시각화해보았다.
① Concatenated Modules
처음 열어본 분석 표에서는 원인을 알 수 없는 거대한 초록색 박스 하나가 화면의 90% 이상을 차지하고 있었다.

index.js + 202 modules (concatenated)라는 이름의 거대한 블랙박스가 전체를 점유하고 있는 모습
하지만 웹팩의 모듈 연결 최적화로 인해 내부 구조를 알 수 없었다.
② 블랙박스 해체
범인의 정체를 밝히기 위해 craco.config.js에서 concatenateModules: false 설정을 적용하여 뭉쳐진 모듈을 강제로 쪼개 보았다.

블랙박스가 수만 개의 작은 조각으로 쪼개지며 react-icons의 20여 개 하위 카테고리(pi, tb, md, si 등)가 화면 전체를 뒤덮고 있는 실체가 드러났다.
서브 패스 임포트 방식을 사용 중이었으나 너무 많은 아이콘 카테고리를 혼용하면서 웹팩이 읽어 들여야 할 데이터량이 기하급수적으로 늘어난 것이 빌드 지연의 진짜 주범이었다.
5. 최종 해결
원인을 완벽히 파악했기에 해결은 명확했다.
- 아이콘 세트 단일화 - 파편화된 아이콘 카테고리를 최대한 스타일이 유사한 세트(fa, md 위주)로 압축하여 웹팩의 분석 부하를 최소화했다.
- 깨끗한 빌드 환경 - 꼬인 설정이 남은 .cache 폴더를 강제 삭제하여 빌드 환경을 깨끗이 유지했다.
Remove-Item -Path "node_modules/.cache", "build" -Recurse -Force
6. 최종 성과
최종적으로 빌드 시간은 초 단위로 단축되었으며 번들 용량은 96% 이상 감소했다.
| 단계 | 빌드 상태 | 번들 용량 (Gzip) | 빌드 소요 시간 |
| 최초 시도 | 무한 대기 | 측정 불가 | - |
| 압축 해제 후 | 빌드 성공 | 7.54 MB | (확인용) |
| 최종 최적화 | 완전 정상화 | 283.41 KB | 약 40초 (30~50초 내외) |

거대했던 아이콘 덩어리들이 사라지고 필수 라이브러리들만 남은 번들 구조
7. 마치며
이번 트러블슈팅을 통해 얻은 가장 큰 교훈은 "주기적인 빌드가 가장 빠른 해결책"이라는 점이다.
① 개발 모드의 주의점
평소 사용하는 npm start는 개발 속도를 높이기 위해 많은 과정을 생략한다. 그래서 실제 배포용 파일을 만들 때 일어나는 치명적인 문제들을 미리 보여주지 않았다. "개발 서버에선 잘 돌아가는데?"라는 안심이 배포 직전의 시한폭탄이 될 수 있음을 느꼈다.
② 주기적인 빌드
코드 한 줄, 라이브러리 하나가 추가될 때마다 프로젝트의 몸무게는 조금씩 늘어난다. 새로운 도구를 들여오거나 큰 리팩토링을 마친 후에는 반드시 습관적인 빌드를 통해 용량과 속도를 체크해야 한다. 주기적인 빌드 체크야말로 예기치 못한 비대함을 조기에 발견하는 예방법일 것이다.
'Troubleshooting' 카테고리의 다른 글
| [Kubernetes] Pod 재스케줄링으로 발생한 504 Gateway Timeout (1) | 2026.06.17 |
|---|---|
| [MyBatis] N+1 Query 문제 해결: JOIN 대신 Batch Select 전략으로 리뷰 조회 성능 개선하기 (0) | 2026.03.05 |
| [MyBatis/Oracle] ORA-17004: 열 유형이 부적합합니다 (TypeException) 해결 방법 (0) | 2026.03.04 |
| [Elasticsearch] AWS t3.micro에서 Elasticsearch 검색 쿼리 최적화하기 (0) | 2026.01.21 |