Skip to content

테스트 전략

들어가며

당신의 코드는 정말 "문제없는가요"? 코드를 수정할 때마다 수동으로 클릭해서 잘 작동하는지 확인하는 방식은 프로젝트가 작을 때는 그럭저럭 가능합니다. 하지만 코드가 수만 줄로 늘어나고 팀이 10여 명으로 확장되면 "수동으로 클릭해보기"는 재앙이 됩니다.

이 장에서는 소프트웨어 테스트의 핵심 전략을 이해하고, 테스트 피라미드부터 TDD까지 체계적인 품질 보장 사고를 확립합니다.

이 글에서 무엇을 배울 수 있을까요?

내용핵심 개념
1장테스트 피라미드테스트의 계층과 비율
2장단위 테스트 실전좋은 테스트를 작성하는 방법
3장TDD 주도 개발레드-그린-리팩터링 순환
4장테스트 전략 선택다양한 상황의 방안

이 장을 마치면 프로젝트에 적합한 테스트 전략을 선택하고, 가치 있는 테스트를 작성하며, TDD를 통해 코드 설계 품질을 향상시킬 수 있게 됩니다.


0. 전경도: 왜 자동화 테스트가 필요한가요?

당신이 건축 엔지니어라고 상상해 보세요. 도면을 수정할 때마다 직접 모든 층에 올라가 구조가 안전한지 확인하지 않을 것입니다 — 자동화된 검사 시스템에 의존할 것입니다. 소프트웨어 테스트는 코드 세계의 "구조 검사 시스템"입니다.

자동화 테스트의 가치

  • 회귀 보호: A 기능을 수정할 때 B, C, D 기능에 영향이 없는지 자동 검출
  • 리팩터링 자신감: 테스트 커버리지가 있는 코드는 리팩터링 시 마음이 편안함
  • 살아있는 문서: 좋은 테스트는 최고의 사용 설명서
  • 빠른 피드백: 몇 초 안에 코드가 올바른지 알 수 있음, 배포 후에야 문제를 발견하는 것이 아님

1. 테스트 피라미드: 테스트의 계층과 비율

1.1 3계층 피라미드

Mike Cohn이 제안한 테스트 피라미드는 테스트 전략의 고전적인 모델입니다. 이 모델은 서로 다른 유형의 테스트가 서로 다른 수량 비율을 가져야 한다는 것을 알려줍니다.

아래의 인터랙티브 컴포넌트를 통해 피라미드의 각 층을 클릭하여 각 테스트 계층의 특징을 이해해 보세요:

Interactive test pyramid - click each layer for details
🖥️E2E tests
🔗Integration tests
🧪Unit tests
Higher: slower, more expensive, closer to usersLower: faster, more numerous, closer to code

1.2 왜 피라미드 모형인가?

피라미드 형태는 하나의 핵심 트레이드오프를 반영합니다: 속도와 현실성의 교환.

  • 하층(단위 테스트): 속도가 매우 빠르고, 수량이 가장 많으며, 비용이 가장 낮지만 개별 부품만 검증
  • 중층(통합 테스트): 속도가 적절하고, 수량이 적당하며, 부품 간의 협력을 검증
  • 상층(E2E 테스트): 실제 사용자에게 가장 가깝지만, 속도가 느리고 유지보수 비용이 높으며 환경 문제로 실패하기 쉬움

안티 패턴: 아이스크림 콘 — 프로젝트에서 E2E 테스트가 가장 많고 단위 테스트가 가장 적다면, 거꾸로 된 "아이스크림 콘" 모양입니다. 이는 테스트 스위트가 느리게 실행되고, 자주 실패하며, 유지보수 비용이 극도로 높다는 것을 의미합니다.


2. 단위 테스트 실전

2.1 좋은 단위 테스트란?

좋은 단위 테스트는 FIRST 원칙을 따릅니다:

원칙의미설명
Fast빠름밀리초 단위로 완료, 개발자가 자주 실행하고 싶어함
Independent독립적테스트 간에 상호 의존 없이 개별 실행 가능
Repeatable반복 가능어떤 환경에서 실행해도 결과가 동일
Self-validating자가 검증결과가 명확한 통과/실패, 인간의 판단 불필요
Timely적시성코드를 작성하면서 (또는 그 전에) 테스트를 작성

2.2 테스트의 구조: AAA 패턴

모든 테스트는 명확한 3단 구조를 가져야 합니다:

javascript
test('세금 포함 가격을 올바르게 계산해야 함', () => {
  // Arrange (준비) — 테스트 데이터 설정
  const price = 100
  const taxRate = 0.13

  // Act (실행) — 테스트 대상 함수 호출
  const result = calculateTotalWithTax(price, taxRate)

  // Assert (단언) — 결과 검증
  expect(result).toBe(113)
})

2.3 무엇을 테스트할 것인가? 무엇을 테스트하지 않을 것인가?

테스트해야 할 것:

  • 핵심 비즈니스 로직 (가격 계산, 권한 판단, 데이터 변환)
  • 경계 조건 (빈 값, 0, 음수, 매우 큰 수)
  • 오류 처리 경로

테스트하지 않아도 되는 것:

  • 서드파티 라이브러리의 내부 구현
  • 단순한 getter/setter
  • 프레임워크 자체의 기능 (예: Vue의 반응형 시스템)

3. TDD: 테스트 주도 개발

3.1 레드-그린-리팩터링 순환

TDD(Test-Driven Development)의 핵심은 간단한 순환입니다: 먼저 테스트를 작성하고, 그 다음에 구현을 작성하고, 마지막으로 리팩터링합니다.

아래의 인터랙티브 컴포넌트를 통해 TDD의 전체 순환을 직접 체험해 보세요:

TDD red-green-refactor cycle - click “Next” to advance
🔴Red
🟢Green
🔵Refactor
Step 1 / 5🔴 Red - write a failing test first
Requirement: implement add(a, b). The first TDD step is not implementation, but writing a test.
add.test.js
test('add(1, 2) should return 3', () => {
  expect(add(1, 2)).toBe(3)
})
❌ Test failed - add is not defined

3.2 TDD의 세 가지 규칙

  1. 실패하는 테스트를 통과시키기 위한 목적이 아니면 어떤 프로덕션 코드도 작성하지 않는다
  2. 테스트 코드는 테스트가 실패할 정도로만 작성한다 (컴파일 오류도 실패로 간주)
  3. 프로덕션 코드는 테스트를 통과할 정도로만 작성한다

3.3 TDD의 진정한 가치

TDD의 가치는 단순히 "테스트를 먼저 작성하는 것"에 있지 않고, 인터페이스 설계를 강제로 고민하게 만든다는 점에 있습니다. 테스트를 먼저 작성할 때, 당신은 "사용자"의 관점에서 생각하게 됩니다: 이 함수는 어떤 매개변수를 받아야 하는가? 어떤 결과를 반환해야 하는가? 이는 자연스럽게 더 나은 API 설계로 이어집니다.

TDD는 은탄환이 아닙니다

TDD는 로직이 밀집된 코드(알고리즘, 비즈니스 규칙, 데이터 변환)에 적합하지만, UI 레이아웃, 탐색적 프로토타입 등의 상황에서는 강제 TDD가 오히려 속도를 늦출 수 있습니다. 핵심은 그 사상을 이해하고 유연하게 활용하는 것입니다.


4. 테스트 전략 선택

4.1 다양한 프로젝트의 테스트 초점

프로젝트 유형테스트 초점추천 비율
유틸리티 라이브러리/SDK단위 테스트 위주90% 단위 + 10% 통합
API 서비스통합 테스트 위주30% 단위 + 60% 통합 + 10% E2E
웹 애플리케이션균형 분포50% 단위 + 30% 통합 + 20% E2E
MVP/프로토타입핵심 경로 E2E소수의 핵심 테스트만

4.2 자주 사용하는 테스트 도구

도구유형적용 시나리오
Vitest단위/통합Vite 프로젝트 최우선, Jest API 호환
Jest단위/통합Node.js 생태계에서 가장 인기
PlaywrightE2E크로스 브라우저, Microsoft 제작
CypressE2E개발 경험이 좋고, 디버깅 편리
Testing Library컴포넌트 테스트사용자 관점으로 UI 컴포넌트 테스트

5. AI 활용: 대형 언어 모델로 테스트 효율 향상

대형 언어 모델은 테스트 분야에서 매우 강력한 능력을 발휘합니다 — 테스트 케이스를 생성하고, 경계 조건을 발견하며, 완전한 테스트 코드를 작성하는 데 도움을 줄 수 있습니다.

5.1 단위 테스트 생성

프롬프트:

다음 함수에 대한 단위 테스트를 Vitest 프레임워크로 작성해 주세요:
1. AAA 패턴 (Arrange-Act-Assert)을 따를 것
2. 정상 경로, 경계 조건, 오류 경로를 모두 커버할 것
3. 각 테스트 케이스에 명확한 한국어 설명을 포함할 것

[함수 코드를 붙여넣으세요]

5.2 경계 조건 발견

프롬프트:

다음 함수를 분석하여 가능한 모든 경계 조건과 극단적 입력 시나리오를 나열해 주세요.
포함: 빈 값, 0, 음수, 매우 큰 수, 특수 문자, 동시성 상황 등.
각 시나리오에 대해 예상 동작과 가능한 위험을 설명해 주세요.

[함수 코드를 붙여넣으세요]

5.3 요구사항에서 테스트 생성 (TDD 보조)

프롬프트:

장바구니 모듈을 구현하려고 합니다. 요구사항:
- 상품 추가, 상품 삭제, 수량 변경
- 총액 자동 계산 (할인 포함)
- 재고 부족 시 오류 표시

TDD 방식에 따라 먼저 테스트 케이스를 작성해 주세요 (구현은 제외),
Vitest를 사용하여 모든 핵심 시나리오를 커버해 주세요.

AI 사용 제안

AI가 생성한 테스트의 단언이 의미 있는지 확인하세요 — expect(true).toBe(true) 같은 무의미한 테스트를 피해야 합니다. 좋은 테스트는 코드에 오류가 있을 때 실제로 실패해야 합니다.


6. 요약

  1. 테스트 피라미드: 하층은 많고 상층은 적게, 속도와 현실성의 균형
  2. 단위 테스트: FIRST 원칙과 AAA 패턴을 따르고, 핵심 로직을 테스트
  3. TDD: 레드-그린-리팩터링 순환으로 테스트가 설계를 주도
  4. 전략 선택: 프로젝트 유형과 단계에 따라 적절한 테스트 비율 선택

핵심 성찰

테스트는 부담이 아니라 가속기입니다. 단기적으로 보면 테스트를 작성하는 데 확실히 시간이 더 걸립니다. 장기적으로 보면 수많은 수동 검증, 회귀 버그 조사, 심야 긴급 수정의 시간을 절약해 줍니다. 좋은 테스트가 있으면 이렇게 말할 수 있는 자신감이 생깁니다: "마음 놓고 수정하세요. 테스트가 문제가 있는지 알려줄 것입니다."


추가 읽기

  • 고전 서적: Kent Beck의 《테스트 주도 개발》은 TDD의 원조입니다.
  • 실용 가이드: 작은 프로젝트에 Vitest로 테스트를 작성해 보고, 처음부터 끝까지의 테스트 과정을 경험해 보세요.
  • 테스트 패턴: Mock, Stub, Spy의 차이와 사용 시나리오를 이해하세요.
  • 지속적 통합: 테스트를 CI/CD 파이프라인에 통합하여, 매번 커밋 시 자동 실행되도록 하세요.