안녕하세요. 성조입니다.
2023.01.28일에 TDD 세미나를 진행한 적이 있었는데 스터디만 진행 후. 정리하는 포스팅하지 못했던 아쉬움이 남아서 이번 포스팅을 기회로 TDD(Test-Driven Development, TDD)에 대해서 정리 포스팅을 해보려고 합니다.
올바른 지식을 공유할 수 있도록 혹여나 잘못된 지식이 전달된다면 언제든지 댓글로 피드백 주시면 감사드리겠습니다!
TDD(Test-Driven Development, TDD)란?
Test-Driven Development는 소프트웨어 개발 방법론 중 하나로, 개발 과정에 있어서 테스트가 중심적인 역할을 하는 방식이다.
[상세 설명]
테스트 주도 개발(TDD)은 테스트를 먼저 작성하고, 그 테스트를 통과하는 코드를 작성하는 개발 방법론이다.
TDD는 코드 품질 향상과 버그 감소를 도와준다. 또한, 코드의 기능을 요구 사항과 비교했을 때. 조금 더 명확하게 이해할 수 있도록 도와준다. 이런 이해도 덕분에 유지 보수와 리팩토링 단계에서 TDD는 더 용이하게 활용된다.
TDD를 통해서 테스트 커버리지를 충분히 높이면 잠재된 버그가 적다는 뜻이므로 개발 부채를 줄이는데 많은 도움을 줄 수 있다.
Case 2) TDD (Test-Driven Development) 방법
1. 실패하는 테스트를 작성(설계 => Design)한다.
실패하는 테스트 규칙을 작성하는 단계이다.
주요 목표인 원하는 기능이 어떤 결과가 나와야 하는 것인지 명확하게 정의한다. 그렇게 요구 분석을 충족시키는 테스트 케이스를 작성하는 단계이며, 테스트 코드를 구현하기 이전이므로 테스트가 실패되는 과정이다.
2. 테스트 코드를 구현
테스트(설계)에 맞는 기능 구현을 진행하는 단계이다.
테스트 코드를 구현하는 것에 있어서 중요한 부분은 테스트 케이스를 통과할 수 있는 최소한의 코드만 작성하는 것이다.
3. 작성된 테스트 코드를 테스트 진행
실패했다면 다시 2번 과정의 테스트 코드로 돌아가서 테스트(설계)를 통과할 수 있는 최소한의 코드를 다시 구현한다.
성공했다면 4단계로 넘어간다.
4. 코드 리팩토링
테스트 성공 이후 테스트 작성으로 넘어간다.
요구 사항에 맞는 기능을 완성할 때까지 코드 리팩토링을 반복한다.
코드 리팩토링 단계에 대해서 다음의 2개로 나눠서 얘기하고 싶다.
1. 테스트(설계)에 맞는 기능이 모두 만족할 때까지 테스트 통과를 반복 작성하는 것.
2. 모든 테스트 케이스가 만족됐다면 클린 코드나 디자인 패턴 등을 적용하여 코드 구조를 개선하거나, 최적화하는 것.
즉, 읽기 쉽고 변경에 유연한 구조로 코드를 리팩터링 하는 것을 말한다.
(단 코드 기능은 변경되면 안된다.)
테스트를 하는 것은 생각보다 어려울 수 있다. 그렇기 때문에 요구 사항을 100% 만족하는 완벽한 설계는 어렵다. 아마도 99% 또는 원했던 기능에 근사한 정도로 기능을 구현해도 매우 잘 구현한 것이다. TDD를 통해서 버그를 최소화하는 만큼 예상하지 못한 곳에서 버그는 발생할 수 있다.
TDD의 장점과 단점
장점
1. 문서화를 통한 이점
테스트 케이스를 작성하면서 기능에 대한 설명서 역할을 하게 된다. 즉, 어떤 기능이 어떻게 동작해야 하는지에 대한 명확한 설명을 제공할 수 있기 때문에 문서화를 조금 더 쉽게 진행할 수 있다. 문서가 잘 정리되어 있으면 유지 보수에 매우 큰 도움 된다.
2. 레거시 코드 리팩토링
TDD는 기존에 테스트 코드가 없는 레거시 코드를 리팩터링 하는데 매우 유용하다.
테스트(설계)를 작성하여 원래 코드의 기능을 보장하고, 테스트를 통과시켜서 테스트 커버리지가 높아지면. 코드에 대한 신뢰도가 높아진다. 이후에 통과한 테스트 케이스 코드를 리팩터링 하는 과정에서 기능 변경 사항이 있다면 기능을 확장하거나, 코드를 수정하여 품질을 개선할 수 있게 된다.
만약 이미 구현됐던 기능에 테스트 케이스가 없다면. 테스트 케이스를 만들고, 테스트를 통과시켜서 마이그레이션 과정에서 발생할 수 있는 에러를 최소화시킬 수 있다.
본인은 이미 구현된 기능에 테스트 케이스가 없을 때. 테스트(설계)를 다시 만들고 통과시키는 것은 튼튼하게 묶은 포장 이사에 가깝다고 생각한다.
3. 코드 품질과 설계 품질의 향상
TDD를 추구하는 과정에서 좋은 설계 원칙을 따르게 된다.(SOLID 빠른 시일 내에 포스팅할 예정이다.)
예로 TDD 기법은 다음과 같은 루틴을 가진다.
1. 실패하는 테스트 코드 작성
2. 테스트 통과할 수 있는 최소한의 코드
3. 통과된 코드를 정리한다. → 리팩토링
이 단계를 반복하면서 좋은 설계(테스트)의 품질을 지켜낼 수 있으며, 리팩토링 단계에서 코드를 지속적으로 정리하기 때문에 통과된 로직을 활용해서 좋은 디자인 패턴을 적용시키고 코드를 개선할 수 있는 것이다.
좋은 설계(테스트)가 되면 코드는 잘게 분리되고, 모듈화 될 것이다. 모듈화 되는 경우. 잘게 잘라냈기 때문에 기능들이 독립적으로 동작할 수 있게 되면서 기능 단위로 응집도는 높아진다. 또한, 결합하는 성질이 낮아져서 결국 TDD는 좋은 코드 품질과 설계를 하면서 응집도는 높고, 결합도는 낮은 좋은 모듈화를 이뤄낼 수 있게 된다.
새로운 기능을 추가하기도 편하다.
응집도가 높고 결합도가 낮으면 종속성이 낮아서 새로운 기능을 추가하기도 편해진다.
(종속성 → 한 부분이 다른 부분에 의존하거나, A가 없이는 B가 제대로 작동하지 않는 그런 관계를 의미한다.)
4. 회귀(regression) 버그 감소
테스트 과정으로 버그를 사전에 줄여서 회귀 버그를 줄여낼 수 있다.
회귀 버그란?
이전에 제대로 작동하던 소프트웨어 기능에 문제가 생기는 것을 가리키는 것이다.
TDD를 통해서 버그를 빠르게 수정하고 감소시킨다.
TDD는 초기 단계부터 버그를 사전에 최소화하기 위해서 사용되는 방법론으로 완벽하게 잡아내기 어렵지만 그래도 버그가 향후에 나오는 것을 최소화하는 것이기 때문에 확장에는 열려있고, 사전에 성공한 테스트를 변경하는 것에는 닫혀있는 Open-Closed Principle을 잘 만족한다.
5. 높은 테스트 커버리지 → 코드 품질 향상 → 버그가 적음 → 고객 만족도 향상
잘 구현된 TDD는 높은 테스트 커버리지를 가지고 있으며, 소프트웨어 버그가 적고 품질이 좋다.
TDD 과정을 통하여 테스트 커버리지를 향상한다. → 품질 향상 → 버그가 적어진다.라는 얘기다. 하지만 아무리 좋은 코드도 사용하는 수요가 없다면 말짱 꽝이다. 올바른 테스트 조건을 작성하는 것은 오버엔지니어링을 방지하며, 매우 중요한 포인트다.
버그가 적어지면 사용자가 불편함을 느낄 수 있는 페인 포인트의 에러 사항이 줄어드는 것이므로 결과적으로 고객 만족도가 향상된다.
(단, 100% 완벽해지는 것은 아니다. 단점 참조)
6. 설계를 의도한 방향으로 코드가 구현됐다.
단점
1. 초기 개발 시간 증가
TDD는 테스트 케이스를 먼저 작성하고 이를 pass 할 때까지 계속 통과하는 코드를 작성하는 방식이다. 그렇기 때문에 초기 개발 시간이 다소 증가할 수 있는 단점이 있다. 특히 복잡한 기능 또는 시스템에서는 광범위한 테스트 케이스를 작성해야 하기 때문에 상당한 시간과 노력이 소비될 수 있다.
2. 테스트 작성의 어려움
모든 가능한 시나리오에 대한 테스트 케이스를 작성하는 것은 매우 복잡하고 시간이 많이 소요될 수 있다. 간단한 테스트 케이스를 작성하는 것은 비교적 쉬우나, 복잡하고 예측이 어려운 시나리오에서는 어려움이 발생할 수 있다.
3. 과도한 설계
TDD는 코드를 작성하기 전에 테스트 케이스를 먼저 작성하는 방식이다. TDD를 진행하면서 너무 불필요하게 복잡한 설계를 하게 되는 경우 노력 비용이 과하게 들어갈 수 있다.
과도한 추상화나 일찍이 과도한 최적화를 바라보고 만들다가 고립되어 무한 딜레이가 발생할 수 있다는 얘기다.
4. 테스트 코드 유지보수 필요
모든 코드는 개발 이후부터 바로 유지보수가 필요한 코드가 된다고 생각한다.
잘 동작하는 코드라도 그 코드를 만든 사람이 백 년 만년 유지 보수하는 경우는 없을 것이다. 그렇기 때문에 많은 프로젝트에서 기술 부채를 해결하기 위해 유지 보수를 진행하는데 테스트 코드 역시 유지 보수가 필요한 대상이 된다.
원하는 비즈니스 모델이 변경되면 그 코드에 맞는 테스트 케이스도 추가적으로 업데이트되어야 하며, 추가적인 기술 부채를 필요로 할 수 있게 된다.
5. 모든 경우의 수를 검증하기 어려움.
TDD의 목표는 목표하는 기능을 향해서 모든 경우의 수를 테스트하는 것이다. 하지만 실제로는 모든 경우의 수에 대해서 완벽하게 테스트하는 것은 불가능에 가까울 수 있다. 따라서, 테스트 케이스를 통과하는 것은 안전할 수 있지만 통과한다고 100% 확정적인 보장은 할 수 없다. (99.99% 에러를 잡더라도 0.01%의 문제가 발생하면 100%가 아니기 때문이다.)
오타 또는 궁금한 부분이 있다면 언제든지 댓글 남겨주시면 감사드리겠습니다!
다음 포스팅 때 뵙겠습니다!
- 참조 -
https://ko.wikipedia.org/wiki/%ED%9A%8C%EA%B7%80_%ED%85%8C%EC%8A%A4%ED%8A%B8
'Study 📔' 카테고리의 다른 글
[Study] UI 라이브러리와 디자인 시스템 (0) | 2023.12.24 |
---|---|
[Study] 프로덕트(Product) 프레임워크(Framework) 선정 과정과 데이터베이스(Database) 선택 이유 (0) | 2023.08.17 |
[Study] FOSS, permissive, reciprocal (with Postgre, MySQL 유/무료) (0) | 2023.05.21 |
[Study] SEO(Search Engine Optimization, SEO)이란? with SERP (0) | 2023.04.28 |
[Study] IaaS, PaaS, SaaS이란? (0) | 2023.03.30 |