test
프론트엔드와 백엔드 모두에서 테스트는 코드 품질을 유지하고 리팩터링에 대한 두려움을 줄여주는 핵심 도구입니다.
하지만 모든 테스트가 같은 목적을 가진 것은 아닙니다.
일반적으로 E2E → 통합 → 단위 → 정적 검사의 네 가지 수준으로 테스트를 구성하며, 이는 실행 순서가 아닌 테스트 범위의 단계적 구분을 의미합니다.
E2E (End-to-End) — Cypress, Playwright 등
실제 사용자 시나리오(로그인, 결제, 주문 등)를 브라우저 환경에서 재현하여 시스템 전체가 올바르게 동작하는지 검증합니다.
신뢰도는 높지만 속도가 느리고 유지 비용이 큽니다.
Integration Test (통합 테스트) — Jest, Mocha 등
여러 모듈이나 컴포넌트가 함께 잘 동작하는지를 검증합니다.
예를 들어 서비스 로직이 데이터베이스 계층과 정상적으로 연동되는지를 테스트합니다.
Unit Test (단위 테스트) — Jest, Vitest, JUnit 등
함수나 클래스 같은 코드의 최소 단위를 독립적으로 검증합니다.
작성 비용이 낮고 실행 속도가 빠르며, 리팩터링 안정성을 보장합니다.
Static Test (정적 검사) — TypeScript, ESLint, Prettier 등
코드를 실행하지 않고 문법, 타입, 규칙 위반 등을 사전에 탐지하는 테스트입니다.
개발 초기 단계에서 오류를 조기에 차단합니다.
정적 테스트는 코드를 실행하지 않고 문제를 사전에 탐지하는 가장 빠른 피드백 단계입니다. TypeScript, ESLint, Prettier 같은 도구를 활용하면 타입 불일치, 규칙 위반, 포매팅 문제를 컴파일 타임이나 커밋 시점에 즉시 발견할 수 있습니다.
이러한 도구들을 CI/CD 파이프라인에 통합하면 팀 내 코드 스타일을 일관성 있게 유지할 수 있으며, 개발자 간의 불필요한 리뷰 부담도 줄일 수 있습니다. 즉, 정적 검사는 개발 품질을 확보하는 1차 안전망이라 할 수 있습니다.
단위 테스트는 코드의 최소 단위(함수, 클래스 등)를 독립적으로 검증하는 테스트입니다.
버그를 조기에 발견하고 수정 비용을 절감하며, 코드 리팩터링에 대한 신뢰성을 높여줍니다.
핵심 조건: 독립성과 격리성
독립적이어야 한다
테스트는 서로 의존하지 않아야 합니다.
하나가 실패해도 다른 테스트 결과에 영향을 주지 않아야 합니다.
격리되어야 한다
테스트가 외부 의존성(API, DB, Storage 등)에 직접 접근하지 않도록 Mock, Stub, Fake 객체를 활용합니다.
이렇게 하면 순수한 로직만 검증할 수 있습니다.
공통 유틸 함수를 수정했을 때 여러 서비스가 동시에 영향을 받지 않도록 보장하는 것이 단위 테스트의 핵심 역할입니다. 변경사항에 따른 기존 기능에 미치는 영향을 즉시 확인 가능하고, 모든 개발자가 코드 작성과 동시에 작성합니다.
통합 테스트는 여러 단위가 서로 연동되어 정상적으로 동작하는지를 검증하는 단계입니다.
서비스 로직과 데이터베이스, API 모듈이 함께 올바른 결과를 만들어내는지를 테스트합니다.
단위 테스트가 “한 함수의 동작”을 검증한다면, 통합 테스트는 “함수들이 연결된 전체 흐름”이 기대한 대로 작동하는지를 확인합니다.
통합 테스트는 단위 테스트보다 느리지만, 시스템 결합부의 문제를 조기에 발견하는 데 탁월합니다. 대규모 프로젝트에서는 현실적인 품질 보증 수단으로 통합 테스트 비중이 가장 높습니다. 핵심 비즈니스 로직이 포함된 모듈이나 서비스 간의 상호작용이 중요한 지점에서 작성합니다.
E2E 테스트는 실제 사용자의 시나리오를 자동화하여 시스템 전체가 올바르게 동작하는지 검증하는 단계입니다. 프론트엔드, 백엔드, 데이터베이스, API 등 모든 계층을 한 번에 테스트합니다.
이 테스트는 신뢰도가 높지만, 실행 속도가 느리고 유지보수 비용이 큽니다. UI가 조금만 바뀌어도 테스트가 실패할 수 있기 때문입니다. 따라서 대부분의 프로젝트에서는 E2E 테스트를 핵심 시나리오(로그인, 회원가입, 결제 등) 중심으로만 구성합니다. 이는 제품 전체의 최종 안정성을 확인하는 데 초점을 둡니다. 배포 전 시스템이 사용자 관점에서 작동에 문제가 없는지 검증할 필요가 있을 떄 작성합니다.
| 구분 | 목적 | 범위 | 속도 | 유지비용 | 대표 도구 |
|---|---|---|---|---|---|
| 정적 테스트 | 코드 문법·타입 오류 사전 검출 | 코드 레벨 | 매우 빠름 | 낮음 | ESLint, TypeScript |
| 단위 테스트 | 함수·모듈 단위 검증 | 코드 단위 | 빠름 | 낮음 | Jest, Vitest |
| 통합 테스트 | 모듈 간 상호작용 검증 | 서비스 단위 | 보통 | 중간 | Jest, Mocha |
| E2E 테스트 | 사용자 시나리오 전체 검증 | 전체 시스템 | 느림 | 높음 | Cypress, Playwright |
모든 코드를 테스트 가능하게 구현하는 것이 목표로 하지만, 테스트 커버리지 100%보다 유의미한 테스트를 작성하는 것이 중요합니다. 테스트 불가능한 코드 (private 접근자, 강결합 코드)는 최소화하여, 신뢰성 높은 테스트 케이스를 작성해야합니다. 그리고 테스트가 성공하면 기능 구현이 완료된 것으로 판단합니다.