지난 편에서 “왜 테스트인가"를 이야기했다면, 이번엔 현실적인 질문입니다. “그래서 뭘, 얼마나 테스트해야 하죠?” 시간은 유한하고, 모든 걸 테스트할 수는 없으니까요.
이번 편의 질문은 하나예요. 답을 세 조각으로 나눠봤습니다.
- 테스트할 가치가 높은 코드를 고르는 기준
- 커버리지 숫자의 진짜 의미와 함정
- 우리 데모 앱에 적용할 테스트 전략

무엇을 먼저 테스트할까#
모든 코드가 같은 무게를 갖지는 않아요. 자주 바뀌고, 틀리면 크게 아픈 코드가 1순위입니다.
- 핵심 비즈니스 로직(계산·검증·상태 전이)
- 버그가 반복적으로 났던 곳
- 많은 곳에서 쓰이는 공용 유틸
반대로, 프레임워크가 보장하는 것(단순 렌더)이나 곧 사라질 코드는 후순위예요.
우리 데모 앱으로 말하면 — 검색 필터(filterByQuery)는 1순위, “제목이 렌더된다” 같은 건 스모크 테스트(연기가 나는지만 보는 것처럼, 일단 켜지고 뜨는지만 확인하는 최소한의 테스트) 하나면 충분합니다.
잠깐, 커버리지가 뭐죠?#
잠깐 용어부터 살펴볼까요? 커버리지(coverage)는 테스트를 돌렸을 때 실제로 실행된 코드의 비율입니다. 테스트 러너가 “당신의 테스트는 전체 코드의 몇 %를 지나갔습니다"라고 알려주는 숫자예요.
export function filterByQuery(users: User[], query: string) {
const k = query.trim().toLowerCase() // ① 실행됨
if (!k) return users // ② 조건 평가됨 (false)
return users.filter(/* ... */) // ③ 실행됨
}
// 테스트가 filterByQuery(users, 'alice') 하나만 호출했다면?
// 세 줄을 모두 '지나갔으니' 라인 커버리지는 100%.
// 하지만 ②가 true가 되는 "빈 검색어" 갈래는 한 번도 안 탔습니다.
같은 코드인데 “몇 %냐"는 세는 방법에 따라 달라지죠. 이 라인/브랜치 구분과 리포트 읽는 법은 추후 자세히 다루고, 오늘은 그보다 먼저 — 이 숫자를 어떻게 대해야 하는지부터 봅니다.
커버리지 100%의 함정#
커버리지는 “실행된 줄"을 셀 뿐, “제대로 검증했는지"는 모릅니다.
// 커버리지는 올라가지만 아무것도 검증하지 못하는 테스트
it('renders', () => {
render(<App />) // assert가 없다!
})이 테스트는 컴포넌트의 모든 렌더 경로를 ‘실행’하므로 커버리지를 꽤 올려줍니다. 하지만 화면이 통째로 깨져도 통과하죠.
비슷한 사촌이 둘 더 있습니다.
// 함정 2: 스냅샷 남발 — "뭔가 바뀌면 실패"일 뿐, 무엇이 옳은지는 모른다
it('matches snapshot', () => {
expect(render(<UserSearch />).container).toMatchSnapshot()
})
// 실패하면? 대부분 내용 확인 없이 스냅샷만 갱신하고 넘어가죠.
// 함정 3: 구현 박제 — 리팩터링만 해도 깨진다
it('calls setState twice', () => {
const spy = vi.spyOn(React, 'useState')
render(<UserSearch />)
expect(spy).toHaveBeenCalledTimes(2) // 사용자와 무관한 내부 사정
})(스냅샷 테스트도, vi.spyOn 같은 스파이도 나중에 각각 제대로 배웁니다. 아직 안 배운 도구라도 괜찮아요 — 지금은 “이런 식의 함정이 있다"는 것만 눈에 담아두면 됩니다.)
셋의 공통점: 커버리지는 올리지만 사용자를 지키지 못합니다. 숫자를 목표로 삼는 순간, 이런 테스트가 늘어납니다. 커버리지는 참고 지표지 목표가 아니에요.
이 시리즈의 전략#
우리는 아래(단위)부터 위(E2E)로 쌓되, 각 층에서 “사용자 관점의 검증"을 우선합니다.
| 층 | 대상 | 도구 | 분량 |
|---|---|---|---|
| 단위 | src/lib의 순수 함수 | Vitest | 많이 |
| 컴포넌트 | UserSearch 상호작용 | Testing Library + MSW | 핵심 흐름 |
| E2E | 검색 시나리오 + 접근성 | Playwright + axe | 적게, 굵게 |
커버리지는 ‘빠진 곳 찾기’용으로만 씁니다.
커버리지 100%를 달성한 팀이 정작 배포 후 장애를 겪는 건 드문 일이 아니에요. 숫자는 초록불인데 사용자는 빨간불인 거죠. 무엇을 검증했는가가 몇 %인가보다 중요합니다.
정리하며#
테스트는 양이 아니라 어디에 힘을 주느냐의 문제예요. 다음 편부터는 실제로 데모 앱을 세팅하고 첫 테스트를 초록불로 만들어봅니다.
“커버리지 100% 만들었어요!” 라는 PR을 열어보면 절반이 expect(1).toBe(1)인 경우가… 있습니다. 우리는 그러지 말기로 해요.
레벨업: 무엇을 먼저 테스트할지 고르고, 커버리지 숫자에 속지 않는 눈이 생겼습니다.
다음 편: 데모 앱에 Vitest 붙이기 — 첫 초록불까지
낯선 용어가 있었다면 — 용어집에 전부 한 줄씩 정리돼 있어요.
