구조기반 테스트로 코드 내부 흐름 검증하기
구조기반 테스트의 제어·데이터 흐름, 경로와 루프 검증, 코드 커버리지, 정적 분석과 자동화 도구를 실무 품질 관리 관점에서 정리한다.
2026-08-14 · 최초 발행 2025-05-23
코드 내부의 흐름을 테스트 대상으로 삼는 이유
구조기반 테스트(White Box Test)는 소프트웨어의 내부 구조와 동작 메커니즘을 파악한 뒤, 그 구조를 기준으로 테스트 케이스를 설계하는 방법이다. 글래스 박스 테스트 또는 투명 박스 테스트라는 이름으로도 불린다.
외부 입력과 결과만 보는 행위기반 테스트와 달리, 내부 로직과 코드 구조, 구현 방식을 확인한다. 개발자 관점에서 수행되며 주로 단위 테스트(Unit Test) 단계에서 활용된다. 따라서 소스 코드에 접근할 수 있고 그 구조를 이해해야 한다.
이 방식은 내부 동작의 정확성을 검증하고, 미실행 코드(Dead Code)나 비효율적인 구조를 찾는 데 쓰인다. 보안 취약점, 논리적 오류, 설계 결함을 일찍 발견하고 모든 코드 경로가 적어도 한 번 실행되도록 확인하는 것도 목표다.
제어 흐름과 데이터 흐름을 따라가는 테스트
제어 흐름 테스트(Control Flow Testing)
제어 흐름 테스트는 프로그램이 조건에 따라 어떤 순서로 실행되는지를 기준으로 케이스를 만든다. 문장 커버리지는 모든 실행 문장을 최소 한 번 실행하는지 확인하고, 결정 커버리지는 if·while 등의 결정점에서 true와 false 결과를 모두 다룬다. 조건 커버리지는 조건식 각각이 낼 수 있는 결과를 검증한다.
데이터 흐름 테스트(Data Flow Testing)
데이터 흐름 테스트는 변수의 정의와 사용 사이의 경로를 추적한다. 변수 선언부터 사용까지의 흐름을 살펴 초기화되지 않은 변수 사용이나 불필요한 변수 선언을 찾는다.
분기·경로·루프에 집중하는 방식
분기 테스트는 if-else, switch-case 등 조건문의 모든 분기가 실행되도록 케이스를 설계하고 분기 커버리지를 측정한다.
경로 테스트는 프로그램 안에서 가능한 실행 경로를 검증한다. 이론적으로는 가장 완전한 방식이지만, 경로 폭발(Path Explosion) 때문에 대규모 시스템의 모든 경로를 다루기는 어렵다. 실제 적용에서는 주요 경로나 위험도가 높은 경로를 우선 검증한다.
루프 테스트는 반복문 구조를 대상으로 한다. 단순 루프는 0, 1, 2, n-1, n, n+1회 반복을 확인하며, 중첩 루프는 안쪽 루프부터 바깥쪽으로 테스트한다. 연결 루프는 각 루프를 독립적으로 확인한 뒤 통합한다.
커버리지는 실행 범위를 보여주는 지표다
코드 커버리지는 테스트 중 실행된 코드의 비율을 나타내며, 구조기반 테스트의 효과를 판단하는 주요 지표다.
문장 커버리지는 가장 기본적인 방식으로, 테스트로 실행된 코드 라인 수를 전체 코드 라인 수로 나눈다. 예를 들어 100줄 중 80줄이 실행되면 문장 커버리지는 80%다.
결정 커버리지는 모든 조건문에서 true와 false 결과를 모두 테스트하며, 분기 커버리지(Branch Coverage)라고도 한다. if(a>b)라면 true인 경우와 false인 경우를 모두 다룬다.
조건 커버리지는 복합 조건문의 각 조건이 true와 false가 되는 경우를 확인한다. if(a>b && c<d)에서는 a>b와 c<d가 각각 true와 false가 되는 경우를 테스트한다.
다중 조건 커버리지는 복합 조건문의 가능한 조합 전체를 대상으로 한다. if(a>b && c<d)라면 (T,T), (T,F), (F,T), (F,F)를 모두 검증한다.
MC/DC(Modified Condition/Decision Coverage)는 항공 우주와 의료처럼 고신뢰성이 요구되는 시스템에서 쓰이는 커버리지다. 각 조건이 전체 결정 결과에 독립적으로 영향을 주는지를 테스트한다.
실행하지 않고 확인하는 정적 분석
정적 분석(Static Analysis)은 코드를 실행하지 않은 상태에서 구조를 분석하는 기법이다. 개발자가 직접 검토하는 코드 리뷰, SonarQube·PMD·FindBugs 등의 정적 분석 도구, 코드 복잡도·결합도·응집도를 측정하는 매트릭 분석, 컴파일 시점의 오류 검사가 여기에 포함된다.
구조기반 테스트와 정적 분석을 함께 사용하면 실행 경로의 검증과 코드 자체의 구조적 문제 확인을 보완할 수 있다.
분석부터 추가 테스트까지의 반복
구조기반 테스트는 소스 코드를 검토해 구조를 파악하는 일에서 시작한다. 이후 커버리지 목표를 정하고 내부 구조에 맞춰 테스트 케이스를 도출한다. 예를 들어 문장 커버리지 목표를 80%로 둘 수 있다.
테스트를 실행한 뒤 달성된 커버리지를 측정하고, 기준에 미치지 못한 영역에는 추가 테스트를 설계해 다시 실행한다. 구현이 바뀌면 그에 맞춰 테스트 케이스도 함께 수정해야 한다.
언어별 커버리지 도구와 분석 플랫폼
Java 코드 커버리지 측정에는 JaCoCo와 Cobertura를 사용할 수 있다. C/C++에서는 Gcov/LCOV, JavaScript에서는 Istanbul이 커버리지 도구로 제시된다. SonarQube는 정적 코드 분석과 커버리지를 통합하는 플랫폼이며, Veracode는 보안 중심의 정적·동적 분석 도구다.
고신뢰성 시스템에서의 적용 모습
한 은행의 온라인 뱅킹 시스템 개발에서는 각 모듈에 구조기반 테스트를 적용했다. 송금 모듈은 경계값, 음수값, 최대값 등 다양한 입력에 대해 모든 분기를 테스트했고, 계좌 정보 조회 모듈은 정상 경로와 예외 경로를 포함하는 경로 테스트를 수행했다.
중요 모듈에는 95%, 일반 모듈에는 80% 결정 커버리지 목표를 설정했다. Jenkins와 JaCoCo를 연동해 CI/CD 파이프라인에 넣고, 커버리지가 기준에 미달하면 빌드가 실패하도록 처리했다. 그 결과 초기 개발 단계에서 233개의 논리적 오류를 발견했고, 운영 환경 배포 후 심각한 결함 발생률은 62% 감소했다. 보안 취약점도 조기에 발견해 보안 사고 예방에 활용했다.
항공기 제어 소프트웨어에서는 안전 중요도에 따라 커버리지 요구사항을 달리 적용했다. 최고 안전 등급 모듈에는 MC/DC 커버리지 100%를, 일반 모듈에는 결정 커버리지 90% 이상을 요구했다.
정적 분석도 강화해 함수당 최대 15의 코드 복잡도(Cyclomatic Complexity) 제한을 두고 MISRA-C 코딩 표준 준수를 검증했다. 메모리 누수와 포인터 오류를 중점적으로 분석했으며, DO-178C 인증 요구사항을 충족하고 치명적 결함 0건으로 안전성을 확보했다. 개발 초기 단계에서 잠재적 위험 요소를 식별하고 제거하는 데도 사용됐다.
높은 커버리지가 대신하지 못하는 것
구조기반 테스트는 내부 로직의 취약점과 약점, 불필요하거나 최적화되지 않은 코드, 복잡한 조건과 경로를 찾는 데 유리하다. 높은 코드 커버리지는 신뢰성 향상에 기여하며 잠재적 보안 취약점도 조기에 발견할 수 있다.
반면 코드에 대한 깊은 이해와 테스터의 전문성이 필요하다. 대규모 시스템에서 모든 경로를 테스트하는 것은 현실적으로 어렵고, 구현 변경에 맞춰 테스트 케이스를 수정해야 한다. 사용자 요구사항과의 불일치는 구조기반 테스트만으로 발견하기 어렵고, 설계와 실행에는 많은 시간과 비용이 든다.
자동화와 보안 분석으로 확장되는 흐름
AI 기반 접근은 코드 구조를 분석해 테스트 케이스를 자동 생성하고, 머신러닝을 이용해 코드 결함을 예측하거나 우선순위를 정하는 방향을 다룬다.
구조기반 테스트와 행위기반 테스트를 통합하는 하이브리드 접근도 사용된다. 테스트 자동화 도구에서 두 방식을 함께 지원하는 형태다.
지속적 테스트(Continuous Testing)는 구조기반 테스트를 CI/CD 파이프라인에 완전히 통합하고, 코드 커버리지를 실시간으로 모니터링해 피드백하는 방식이다. 보안 중심 구조기반 테스트는 보안 취약점에 특화된 코드 패턴을 인식하고 OWASP Top 10 취약점 자동 탐지 기능을 강화하는 방향을 포함한다.
구조기반 테스트는 코드 내부의 논리 오류와 미실행 코드, 보안 취약점을 찾는 데 효과적이지만 사용자 요구사항의 충족 여부까지 보장하지는 않는다. 행위기반 테스트와 조합해 적용할 때 내부 구현과 외부 요구사항을 함께 검증할 수 있다.