Vitest로 TDD 개발 환경 구축하기
들어가며
테스트는 이제 “하면 좋은 것”이 아니라
안 하면 불안한 것이 되었습니다.
하지만 여전히 많은 개발자들이 이렇게 느낍니다.
TDD는 이상적인데, 현실에서는 너무 느린 것 같다
Jest 설정은 점점 복잡해진다
Rollup 라이브러리에서는 테스트를 어디까지 해야 할지 모르겠다
이 글에서는 이런 고민을 전제로,
Vitest를 이용해 “현실적으로 돌아가는 TDD 개발 환경”을 구축하는 방법
을 Rollup 기반 TypeScript 라이브러리 기준으로 정리합니다.
1. 요즘에도 TDD는 쓰일까?
결론부터 말하면:
TDD의 “철학”은 여전히 유효하지만,
순수한 형태의 TDD만 고집하는 팀은 줄어들고 있다.
과거의 TDD
Red → Green → Refactor를 엄격히 반복
단위 테스트 중심
개발자 개인의 설계 도구
현재의 TDD
테스트를 설계의 기준으로 사용
단위 테스트 + 통합 테스트를 상황에 맞게 조합
CI/CD와 결합된 안전망 역할
즉,
TDD는 사라진 것이 아니라 “확장된 테스트 전략의 일부”가 되었습니다.
2. TDD / BDD / ATDD, 무엇이 다른가?
테스트 기반 개발 방식은 하나가 아닙니다.
| 구분 | TDD | BDD | ATDD |
| 중심 질문 | 이 코드는 어떻게 동작해야 하는가? | 사용자는 어떻게 행동하는가? | 언제 이 기능이 완료되었다고 말할 수 있는가? |
| 관점 | 코드/함수 | 행동/시나리오 | 요구사항/비즈니스 |
| 주 사용 주체 | 개발자 | 개발자 + 기획 | 개발자 + 기획 + QA |
| 주 사용 레벨 | 단위 테스트 | 통합/행위 테스트 | 인수/릴리즈 기준 |
요즘 실무에서 가장 흔한 패턴은 다음과 같습니다.
핵심 로직 → TDD
유스케이스 → BDD 스타일 테스트
릴리즈 기준 → ATDD 일부 적용
이 글에서는 그중에서도
라이브러리 개발에 가장 적합한 TDD에 집중합니다.
3. 테스트 환경을 구축했을 때의 진짜 장점
테스트의 가장 큰 가치는
“버그를 잡는다”가 아닙니다.
1️⃣ 리팩토링이 가능해진다
테스트는 안전망
내부 구현을 바꿔도 외부 동작을 고정할 수 있음
2️⃣ 버그를 조기에 차단한다
로컬/CI 단계에서 실패
운영 장애 비용 급감
3️⃣ 코드가 문서가 된다
it('returns normalized user object', () => {
expect(parseUser(input)).toEqual({...});
});
테스트는 실행 가능한 스펙입니다.
4️⃣ 협업과 리뷰가 쉬워진다
“이 코드의 의도가 무엇인지”를 테스트로 설명 가능
리뷰가 구현이 아닌 설계 중심으로 이동
4. 왜 요즘은 Jest보다 Vitest인가?
Vitest가 선호되는 이유는 단순합니다.
✅ 빠르다
Vite 기반
watch 모드에서 즉각적인 피드백
✅ ESM/TypeScript 친화적
별도의 복잡한 트랜스폼 설정이 거의 없음
Rollup/Vite 프로젝트와 설정 공유 가능
✅ TDD에 최적화된 개발 경험
vitest --watch기본 루프Jest와 거의 동일한 API (
expect,mock,spy)
즉,
“요즘 프론트엔드 빌드 환경에 맞게 다시 설계된 테스트 러너”
가 Vitest입니다.
5. Rollup 라이브러리에서의 테스트 원칙
이 글의 가장 중요한 전제입니다.
❌ 잘못된 접근
dist결과물을 테스트Rollup 설정에 테스트 로직을 포함
✅ 올바른 접근
테스트는 항상
src기준Rollup은 빌드 전용
테스트와 빌드는 책임 분리
라이브러리 테스트의 목적은
“번들이 잘 나오는지”가 아니라
“공개 API가 의도대로 동작하는지” 입니다.
6. Vitest + Rollup 기반 TDD 환경 구축
6-1. 디렉토리 구조
my-lib/
├─ src/
│ ├─ index.ts
│ └─ sum.ts
├─ test/
│ └─ sum.test.ts
├─ rollup.config.mjs
├─ vitest.config.ts
├─ tsconfig.json
├─ tsconfig.build.json
├─ tsconfig.test.json
└─ package.json
6-2. TypeScript 설정 분리
tsconfig.build.json (빌드용)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"declaration": true,
"sourceMap": true
},
"include": ["src"]
}
tsconfig.test.json (테스트용)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["vitest/globals", "node"]
},
"include": ["src", "test"]
}
6-3. Vitest 설정
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["test/**/*.test.ts"],
clearMocks: true,
restoreMocks: true
}
});
6-4. Rollup 설정 (빌드 전용)
// rollup.config.mjs
import typescript from "@rollup/plugin-typescript";
export default {
input: "src/index.ts",
output: [
{ file: "dist/index.esm.js", format: "esm", sourcemap: true },
{ file: "dist/index.cjs.js", format: "cjs", sourcemap: true }
],
plugins: [
typescript({ tsconfig: "./tsconfig.build.json" })
]
};
6-5. package.json 스크립트
{
"scripts": {
"dev:test": "vitest",
"test": "vitest run",
"typecheck": "tsc -p tsconfig.test.json",
"build": "rollup -c",
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
}
}
이제 개발 중에는:
npm run dev:test
→ Red / Green / Refactor 루프가 즉시 돌아갑니다.
7. “진짜 TDD답게” 사용하는 운영 팁
watch 모드는 항상 켜두기
테스트는 구현이 아닌 행동/계약을 고정
외부 의존(시간, 랜덤, 네트워크)은 mock으로 통제
커버리지는 목표가 아니라 빈 구간을 찾는 도구
마치며
Vitest로 TDD 환경을 구축한다는 것은
단순히 테스트 러너를 바꾸는 문제가 아닙니다.
“코드를 바꿔도 괜찮은 상태”를 만드는 일입니다.
Rollup 기반 라이브러리 개발에서
Vitest + TDD 조합은 다음을 가능하게 합니다.
자신 있는 리팩토링
안정적인 공개 API
CI/CD 자동화의 기반
협업 가능한 코드베이스
🧾 작성 참고
이 글은 ChatGPT의 도움을 받아 내용을 정리하였습니다.