테스트 주도 개발(TDD)을 하면서 "어디서부터 시작해야 할까?", "어떤 테스트를 먼저 작성해야 할까?"와 같은 고민을 해본 적 있으신가요? TDD의 개념은 좋지만 막상 적용하려니 어려움을 느끼는 프론트 개발자분들을 위해, Robert C. Martin, 이 제시한 변환 우선순위 전제(Transformation Priority Premise, TPP)를 간단히 소개하고자 합니다.
TPP란 무엇일까요?
TPP는 TDD 주기 동안 코드를 변경하는 작업을 단순하고 점진적으로 수행하는 것을 목표로 합니다. TDD 과정에서 막히거나 오랜 시간이 걸리는 상황을 방지하고, 코드의 복잡성을 최소화하는 데 초점을 맞춥니다. 쉽게 말해, 가장 간단한 변화부터 시작해서 점진적으로 복잡도를 높여가는 방식입니다.
TPP의 핵심 원칙
- 단순한 변환 우선: 복잡한 변화를 한 번에 적용하기보다는, 작고 간단한 변화를 먼저 적용합니다.
- 점진적인 일반화: 테스트를 추가하면서 코드를 더욱 일반적인 형태로 발전시켜 나갑니다.
- 필수 복잡성만 추가: 불필요한 복잡성을 제거하고, 꼭 필요한 기능만을 추가하여 코드의 유지보수성을 높입니다.
TPP 목록
Robert C. Martin 은 TPP 목록이 언어에 따라 달라질 수 있다고 언급했습니다.
요는 간단한 케이스에서 복잡한 케이스로 넘어가는 겁니다
- 빈 함수에서 시작 👉 null 반환 으로 변환
- null 반환 👉 상수로 변경
- 단순 상수 👉 상수+ ( 조금 더 복잡한 상수로 )
- 복잡한 상수 👉 변수 혹은 매개변수 도입
- 구문 👉 복수의 구문 추가
- 조건문 도입
- 배열 사용
- 배열 👉 컨테이너 사용 ( 데이터를 저장하고 관리하는 더 특화된 자료구조로의 전환 )
- 꼬리 재귀 도입 ( 마지막 명령어(일반적으로 반환 값)가 재귀 호출 )
- if 👉 while 루프로 변경
- 일반 재귀로 변경
- 표현식 👉 함수형 프로그래밍 적용 또는 알고리즘으로 바꾸기
- 변수 👉 할당 ( variable → assignment )
- 추가 케이스 처리 및 최적화
아래 함수를 예로 들어 설명하겠습니다 ( 모든 케이스에 맞는 예시는 준비를 못했습니다 😣)
// 1. ({} → nil) - 가장 기본적인 상태, 아무것도 반환하지 않음
function sum() {
}
// 2. (nil → constant) - 상수 반환
function sum() {
return 0;
}
// 3. (constant → variable) - 변수 도입
function sum(number) {
let result = number;
return result;
}
// 4. (statement → statements) - 여러 구문 추가
function sum(number) {
let result = number;
result = Math.abs(result); // 절대값 처리
return result;
}
// 5. (unconditional → if) - 조건문 추가
function sum(number) {
let result = 0;
if (number) {
result = number;
}
return result;
}
// 6. (variable → array) - 배열 처리
function sum(numbers) {
if (!numbers) return 0;
return numbers[0];
}
// 7. (array → container) - 배열을 더 복잡한 자료구조로 처리
function sum(numbers) {
if (!numbers) return 0;
const numberSet = new Set(numbers);
return Array.from(numberSet)[0];
}
// 8. (statement → recursion) - 재귀 도입
function sum(numbers) {
if (!numbers || numbers.length === 0) return 0;
return numbers[0] + sum(numbers.slice(1));
}
// 9. (if → while) - 반복문으로 변환
function sum(numbers) {
if (!numbers) return 0;
let result = 0;
let i = 0;
while (i < numbers.length) {
result += numbers[i];
i++;
}
return result;
}
// 10. (expression → function) - 함수형 프로그래밍 적용
function sum(numbers) {
if (!numbers) return 0;
return numbers.reduce((acc, curr) => acc + curr, 0);
}
// 11. (variable → assignment) - 더 복잡한 할당과 처리
function sum(numbers, options = {}) {
if (!numbers) return 0;
let result = 0;
const { skipNegatives = false, maxValue = Infinity } = options;
numbers.forEach(num => {
if (skipNegatives && num < 0) return;
result += Math.min(num, maxValue);
});
return result;
}
TPP, 왜 사용해야 할까요?
- TDD 사이클 단축: 작고 간단한 변화에 집중하여 빠르게 테스트를 통과시키고 다음 단계로 넘어갈 수 있습니다.
- 코드 복잡성 감소: 점진적인 변화를 통해 불필요한 복잡성을 줄이고, 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.
- 학습 곡선 완화: TDD를 처음 접하는 개발자도 쉽게 TDD에 익숙해질 수 있도록 도와줍니다.
- 교착 상태 방지: 너무 큰 변화를 한 번에 시도하여 TDD 사이클이 막히는 상황을 방지합니다.
TPP 적용 시 주의사항
- 절대적인 규칙은 아님: TPP 목록은 가이드라인일 뿐, 프로젝트의 특성과 상황에 따라 유연하게 적용해야 합니다.
- 지속적인 리팩토링: TPP를 통해 작성된 코드는 지속적인 리팩토링을 통해 더욱 깔끔하고 효율적으로 개선할 수 있습니다.
- 팀과의 공유: TPP를 팀 내에 공유하고, 함께 사용하는 규칙을 정하는 것이 좋습니다.
'Test > vitest' 카테고리의 다른 글
[vitest] 테스트 코드 작성 시 검증(Assertion) 전략 비교 - 엄격한 비교 vs 유연한 비교 (0) | 2025.02.22 |
---|---|
[vitest] Framer Motion 컴포넌트 테스트 구현하기 - Vitest와 Proxy 패턴 활용 ( feat. react ) (0) | 2025.02.17 |
[Vitest] 1. 테스트란 무엇인가? (0) | 2024.08.25 |