
모듈 격리 제거와 정교한 상태 클린업 안전 가드로 이뤄낸 초고속 모노레포 테스트 빌드 최적화
테스트 코드를 짤수록 피드백 루프가 느려지는 프론트엔드 모노레포의 역설을 해결하기 위해 미리디 엔진팀이 고군분투한 생생한 실전 기록입니다. 단순히 패키지를 쪼개는 임시방편을 넘어, CPU/힙 프로파일러로 찾아낸 핵심 병목인 모듈 격리 환경을 최적화하여 빌드 성능을 무려 78%나 개선했습니다. 지속 가능한 속도를 위해 AI 개발 에이전트와도 협업할 수 있도록 시스템 가이드를 정립한 모범 사례를 소개합니다.
프로젝트 규모가 커짐에 따라 테스트 러너의 실행 속도가 기하급수적으로 느려져 피드백 루프가 깨진 프론트엔드 팀에게 추천합니다. 전역 상태와 캐시 오염을 동반하는 비격리 테스트를 안전하게 제어하고 자동화된 도구로 지켜내는 세련된 실전 아키텍처를 배울 수 있습니다.
미리캔버스 모노레포에서 테스트 커버리지 100% 달성 및 TDD 도입으로 테스트 코드가 수만 개로 늘어나면서, CI 유닛 테스트 단계의 대기 시간이 16분까지 늘어나는 문제가 발생했습니다. 원인을 분석한 결과 테스트 본문 실행이 아닌, Vitest의 기본 격리(isolation) 환경으로 인해 대규모 모듈 그래프(약 3,200개 모듈)를 테스트 파일마다 매번 새로 평가하는 모듈 로딩 단계가 병목이었습니다.
유닛 테스트 잡에 고성능 8코어 러너를 재배치하고, 상태 정리가 검증된 안전한 파일만 비격리('isolate: false') 프로젝트로 분류하여 모듈 캐시를 공유하도록 처리했습니다. 또한 모듈 캐시 오염을 유발하여 비격리 전환을 방해하던 'vi.mock' 사용처를 런타임에 안전하게 프로퍼티만 가로채는 'vi.spyOn'으로 대거 교체하고, 규칙이 유지될 수 있도록 린트 룰 및 런타임 가드 등의 4단계 안전장치를 도입했습니다.
세 차례에 걸친 단계별 개선을 통해 유닛 테스트 잡 수행 시간을 16분 15초에서 3분 38초로 약 78% 단축시켰습니다. 특히 비격리 프로젝트는 테스트 파일 개수가 200개에서 400개로 두 배 늘어나도 실행 시간이 거의 증가하지 않는 강력한 확장성을 입증했습니다.
Trade-off
테스트 파일 간 격리를 해제하면서 전역 변수나 DOM 상태가 다음 테스트로 이어져 발생하는 flaky 테스트의 위험도가 크게 증가했습니다. 이를 방지하기 위해 테스트 작성 시 엄격한 리소스 정리(cleanup) 규칙을 지켜야 하며, 파일명 규칙 관리와 비격리 테스트용 가드 등의 도구 유지보수 비용 및 AI 에이전트용 전용 스킬 문서 관리 공수가 추가되었습니다.
각 테스트 파일이 서로 영향을 주지 않도록 독립된 샌드박스 환경에서 실행시키는 Vitest의 기본 동작 방식입니다. 다만 모노레포 등 의존성 그래프가 방대한 프로젝트에서는 매 파일마다 모듈을 새로 로드하는 엄청난 성능 낭비가 발생합니다.
vi.mock은 모듈의 전체 export 내용을 모듈 로딩 시점에 가로채서 다른 객체로 대체하며 캐시를 오염시키는 반면, vi.spyOn은 모듈은 그대로 둔 채 이미 로드된 객체의 런타임 특정 메서드만 감시하거나 임시 교체합니다.
TypeScript 5.2에 추가된 explicit resource management 기능으로, 변수 선언 시 using 키워드를 사용하면 해당 스코프를 빠져나갈 때 객체의 Symbol.dispose 메서드가 자동으로 실행되어 자원을 반환합니다.




