Vitest, Jest, 단위 테스트, Vite 마이그레이션 | 기존 설정을 언제 옮길까?
Vitest와 Jest를 속도·설정 공유·API 호환·CI·레거시 조건으로 비교한다. Vite 앱은 vite.config 통합이 유리하고, Webpack·커스텀 러너·수천 스냅샷 레거시는 Jest 유지가 합리적이다. 이전 체크리스트, 시나리오별 선택, FAQ와 공식 문서 링크를 정리한 프론트엔드 단위 테스트·TypeScript·React 개발자 가이드.
Vite로 올린 프로젝트에서 Jest 설정 파일이 세 개 이상 쌓이면, 테스트 한 줄 고치는 시간보다 설정 맞추는 시간이 더 길어진다. Vitest(Vitest)는 Vite 설정을 그대로 쓰고 Jest와 비슷한 API를 제공한다. 그래서 단위 테스트 런너 선택은 기능 목록보다 빌드 도구와의 정렬과 마이그레이션 비용에서 갈린다.
아래는 속도·설정·호환 API·CI·레거시 조건별로 두 도구를 표로 나눠 본 선택 가이드다. 이미 Jest만 쓰는 모노레포와, Vite 기반 프론트만 있는 팀을 각각 전제로 둔다.
“어느 쪽이 더 좋다”가 아니라 지금 쓰는 번들러와 얼마나 같은 파이프라인을 타느냐가 1순위다. Vite 앱에 Jest를 억지로 붙이면 babel-jest, 경로 별칭, CSS 모듈 mock을 이중으로 관리하게 된다.
단위 테스트 러너 선택은 기능 목록보다 빌드 도구와의 정렬이 먼저다
기능·속도 비교표 | API 호환과 실행 방식
공식 문서 기준으로 자주 묻는 항목만 압축했다. 벤치마크 수치는 저장소·파일 수·모킹 밀도에 따라 달라지므로, 여기서는 구조적 차이 위주로 적는다.
항목
Vitest
Jest
변환 엔진
Vite(esbuild/롤업 파이프라인)
babel-jest 또는 ts-jest 등 별도
설정 파일
vite.config에 test 블록 통합 가능
jest.config.js 등 독립 설정
워치 모드
Vite HMR 계열, 변경 파일 재실행이 빠름
안정적이나 변환 캐시 구조가 다름
전역 API
globals 옵션 또는 import { describe, it, expect }
기본 전역 describe/it/expect
스냅샷
지원
지원(성숙도·플러그인 풍부)
커버리지
v8 / istanbul 선택
istanbul 계열이 흔함
브라우저 모드
공식 browser mode 제공
jsdom/happy-dom 위주, 실브라우저 별도
Jest 호환
expect·mock API 상당 부분 호환
원본
커뮤니티 연식
상대적으로 짧음, Vite 팀 인접
장기 표준, 예제·이슈 해결 글 많음
속도 체감은 “콜드 스타트 + 워치 재실행”에서 Vitest 쪽이 유리한 경우가 많다. 반대로 이미 수천 개 스펙이 Jest 플러그인에 묶여 있으면 이전 도중 깨지는 mock·트랜스폼이 더 비싸다. 숫자 한 방의 벤치보다 팀의 실패 비용이 먼저다.
설정 차이 | vite.config 한 파일 vs jest.config 분리
Vite 프로젝트에 Vitest를 붙일 때는 보통 패키지 설치 후 vite.config.ts에 test 필드를 추가한다. 경로 별칭(@/)이 앱과 테스트에서 같아져서 “테스트만 경로가 안 된다” 류의 장애가 줄어든다.
Jest 쪽은 앱이 Vite여도 moduleNameMapper, transform, testEnvironment를 따로 맞춘다. CSS·SVG import mock, TypeScript 경로, ESM 지원 여부를 각각 손봐야 한다. Vite와 Webpack 비교에서 보듯 번들러가 갈리면 테스트 변환 파이프라인도 갈린다.
실무에서 자주 쓰는 최소 골격만 적으면 다음과 같다. (명령·옵션은 공식 문서 기준으로 프로젝트에 맞게 조정한다.)
둘 다 동작한다. 차이는 “앱 설정과 테스트 설정이 같은 파일을 보느냐”다. 프론트 팀이 Vite 플러그인을 자주 바꾸면 Vitest가 유지보수 면에서 유리하고, 백엔드·모바일·여러 패키지가 Jest 전역 설정 하나로 묶여 있으면 분리 설정의 이점이 작아진다.
설정 파일이 앱 빌드와 같으면 경로 별칭·플러그인 불일치가 줄어든다
API 호환 | describe·mock·스냅샷에서 깨지는 지점
Vitest는 Jest 호환을 목표로 설계됐다. describe, it, expect, vi.fn 대 jest.fn 같은 대응표가 공식 마이그레이션 가이드에 있다. 그래도 이전 시 자주 깨지는 지점은 고정돼 있다.
jest.* 전역: Vitest는 vi 네임스페이스를 권장한다. 호환 모드로 넘길 수 있지만 플러그인·타입이 꼬이면 여기서 먼저 터진다.
수동 mock 경로:__mocks__ 규칙과 자동 mock 동작이 100% 같지 않을 수 있다. 네트워크·스토리지 mock이 많은 코드베이스일수록 한 파일씩 확인한다.
타이머: fake timers API 이름·기본값이 버전마다 미묘히 다르다. 애니메이션·디바운스 테스트는 이전 직후 우선 실행한다.
트랜스폼 특수 케이스: GraphQL 문서 로더, raw 파일 import, WASM 등은 Jest 쪽에 커스텀 transformer가 남아 있는 경우가 많다.
마이그레이션 순서는 보통 순수 유틸 → 훅 → 컴포넌트 → 네트워크 mock 의존 스펙이다. 한 번에 전 레포를 바꾸기보다 패키지 단위로 vitest 스크립트를 병행 실행하는 편이 안전하다. 단위 테스트 패턴 자체는 Vitest React 튜토리얼과 같은 구조를 쓰면 팀 내 문서 비용을 줄일 수 있다.
마이그레이션 체크 한 줄:npx vitest run이 초록이어도 커버리지 리포트 형식·CI 아티팩트 경로·스냅샷 디렉터리 규칙이 다르면 파이프라인에서 실패한다. 로컬 통과 ≠ CI 통과로 보고 아티팩트 단계까지 같이 옮긴다.
CI와 로컬 루프 | 분 단위 절감이 의미가 있을 때
로컬에서 저장할 때마다 테스트가 2~3초 안에 돌아오면 개발 루프가 바뀐다. Vitest의 워치·스레드 풀은 이 구간을 노린다. CI에서는 이야기가 조금 다르다.
CI는 콜드 캐시, 의존성 설치, 병렬 잡 수, 샤딩 전략이 총시간을 좌우한다. Jest도 --maxWorkers와 캐시 디렉터리로 충분히 빠르게 만들 수 있다. 따라서 “CI가 느려서 Vitest로 간다”는 이유만으로는 약하다. 더 설득력 있는 이유는 다음이다.
로컬·CI가 같은 Vite 플러그인 세트를 쓰게 만들어 환경 차이를 줄인다.
모노레포에서 패키지별 Vite 라이브러리 빌드와 테스트 설정을 공유한다.
타입스크립트·JSX 변환을 이중 유지보수하지 않는다.
깃허브 액션 기준으로는 캐시 키에 lockfile과 node_modules뿐 아니라 테스트 캐시 경로를 넣을지 먼저 정한다. Vitest·Jest 모두 캐시 무효화가 잦으면 속도 이점이 사라진다. 실패 로그를 슬랙으로 보내는 팀이면 러너 교체 주간에 알림 노이즈가 늘 수 있으니, 브랜치 보호 규칙에 “테스트 잡 이름 변경”을 미리 반영한다.
Jest를 유지하는 편이 나은 경우
유행 때문에 옮기다가 이틀을 설정에 쓰는 팀도 있다. 아래면 Jest 유지가 합리적이다.
조건
설명
커스텀 러너·리포터
사내 리포트 포맷, 사내 인증 연동 러너가 Jest 전용
React Native·특수 프리셋
프리셋·트랜스폼 문서가 Jest 중심인 스택
수천 개 스냅샷 + 엄격 리뷰 문화
스냅샷 경로·직렬화 차이로 리뷰 소음이 폭증할 수 있음
Webpack·Babel 중심 모노레포
앱 자체가 Vite가 아니면 Vitest 이점이 절반 이하
분기 중 대규모 기능 출시
테스트 인프라 교체는 릴리스 피크와 겹치지 않게
반대로 신규 Vite 앱, 그린필드 디자인 시스템, 스토리북과 단위 테스트를 같이 키우는 패키지는 Vitest를 기본값으로 두는 팀이 늘었다. 선택 기준 문서를 위키 한 페이지로 남겨 두면 “왜 우리만 Jest냐” 논쟁을 반복하지 않는다.
CI 총시간보다 로컬 루프와 설정 단일화가 이전 결정에 더 크게 작용하는 경우가 많다
이전 체크리스트 | 이틀 안에 끝내는 최소 순서
Vite 앱 하나를 Jest에서 Vitest로 옮길 때 쓰는 최소 순서다. 규모가 크면 패키지 단위로 잘라 같은 순서를 반복한다.
의존성 추가:vitest, 환경에 맞게 jsdom 또는 happy-dom, 리액트면 Testing Library 유지.
설정 이전: jest.config의 testMatch·setup·alias를 vite test 블록으로 옮긴다.
스크립트 병행:test:jest와 test:vitest를 잠시 공존시킨다. 한 번에 package.json의 test를 바꾸지 않는다.
API 치환:jest.fn → vi.fn, jest.mock → vi.mock. 자동 codemod가 있으면 1차 적용 후 수동 수정.
깨진 스펙만 격리: 타이머·ESM·CSS 모듈 실패 파일을 이슈로 끊고, 통과 파일부터 CI 잡에 넣는다.
커버리지·스냅샷 경로 확인: 리포트 업로드 경로와 스냅샷 업데이트 PR 규칙을 팀에 공지한다.
Jest 제거: 전 스펙 통과·CI 7일 안정 후 jest 관련 패키지와 설정을 삭제한다.
한글 주석·에러 메시지 정책이 있는 팀이면 expect 실패 메시지 스냅샷에 한글이 섞여 있는지 한 번 더 본다. 인코딩 이슈는 드물지만, Windows·macOS 혼합 환경에서 줄바꿈 스냅샷이 갈리는 문제는 여전하다. 가능하면 스냅샷보다 명시적 assertion 비중을 높이는 편이 리뷰가 가볍다.
E2E는 이 과정에서 건드리지 않는 것이 좋다. 단위 러너를 바꾸는 주에 플레이라이트 설정을 같이 건드리면 원인 분리가 안 된다. E2E는 기존 가이드 흐름을 유지하고, 단위만 교체한다.
시나리오별 선택 | 1인·스타트업·레거시 팀
1인·소규모 제품. Vite + React(또는 Vue)로 시작한다면 Vitest를 기본으로 둔다. 설정 파일이 하나 줄고, 로컬 피드백이 빨라 혼자 유지보수할 때 이득이 크다. 패키지 매니저는 팀 표준을 따르면 되고, 테스트 러너와는 독립이다.
시리즈 A 전후 스타트업. 이미 Jest 800개 이상이면 “전부 이전”보다 신규 패키지만 Vitest가 현실적이다. 디자인 시스템·유틸 패키지부터 옮기고 앱 본체는 출시 이후 창에 옮긴다. 채용 공고에 “Jest 필수”가 박혀 있어도 Vitest 경험은 대부분 바로 전이된다.
레거시 Webpack 모노레포. 번들러 이전 로드맵이 없다면 Jest 유지가 맞다. 번들러를 Vite로 옮기는 분기와 테스트 러너 교체를 같은 분기에 넣지 마라. 장애 원인 추적이 불가능에 가까워진다.
백엔드 Node 서비스. 프레임워크가 Fastify·Hono 등이어도 단위 테스트는 둘 다 가능하다. 프론트와 모노레포를 공유하면 Vitest로 맞추고, 백엔드만 별도 레포면 팀 익숙한 쪽을 고르면 된다. “백엔드라서 무조건 Jest” 규칙은 2026년 기준으로는 약하다.
간단한 유틸·순수 함수 스펙은 상당수 그대로 통과합니다. 다만 jest.fn·수동 mock·커스텀 transformer·일부 타이머 API는 수정이 필요합니다. 공식 마이그레이션 가이드의 API 대응표를 기준으로 한 패키지씩 확인하는 편이 안전합니다.
Next.js 프로젝트에도 Vitest가 맞나요?
가능합니다. 다만 Next 공식 예시와 플러그인 문서에는 Jest 조합이 아직 많이 남아 있습니다. App Router·서버 컴포넌트 mock 난이도는 러너보다 아키텍처 문제에 가깝습니다. 팀 내 Next 예제가 Jest로 통일돼 있으면 유지 비용이 더 낮을 수 있습니다.
jsdom과 happy-dom 중 무엇을 쓰나요?
둘 다 DOM을 Node 안에서 흉내 냅니다. happy-dom이 더 빠른 경우가 많고, jsdom은 호환 사례가 많습니다. 브라우저 전용 API를 깊게 쓰는 컴포넌트는 실브라우저(E2E 또는 Vitest browser mode)를 검토하세요. 단위 테스트만으로 레이아웃·포커스 트랩을 단정하기 어렵습니다.
커버리지 숫자를 Jest와 Vitest에서 같게 맞출 수 있나요?
완전 일치는 기대하지 않는 것이 좋습니다. 계측 엔진(v8 vs istanbul)·제외 패턴·소스맵 처리가 다르면 분기 커버리지가 어긋납니다. 팀 게이트는 “이전 직전 Jest 숫자”가 아니라 “새 러너 기준으로 합의된 임계값”으로 재설정하세요.
모노레포에서 패키지마다 러너를 섞어도 되나요?
됩니다. 신규 패키지 Vitest·레거시 패키지 Jest 병행은 흔한 전략입니다. 다만 루트 test 스크립트와 CI 잡 이름을 패키지별로 명확히 나누고, 공통 Testing Library 버전은 맞춰 두는 것이 좋습니다. 같은 PR에서 두 러너 설정 문법을 섞어 리뷰하면 피로도가 큽니다.
E2E까지 Vitest browser mode로 통일할 수 있나요?
브라우저에서 단위·통합에 가까운 검증은 가능합니다. 다만 다중 탭·실제 네트워크·결제 샌드박스·시각 회귀는 플레이라이트 같은 E2E 도구가 여전히 본업입니다. 단위 러너와 E2E를 한 도구로 억지 통일하면 실패 분류가 어려워집니다.