TDD는 RED → GREEN → YELLOW가 전부는 아니다
지금 까지는 TDD가 '테스트, 구현, 리팩토링 순서로 진행하는 개발 방법'이라는 정도로만 이해하고 있었는데요.
최근 테스트 주도 개발을 읽으며 이게 전부가 아니라는 사실을 배웠습니다.
그래서 이번 글에서는 제가 배운점들을 공유드리려고 합니다.
TDD의 정의
TDD란 무엇일까요?
많은 글과 책 등에서 TDD에 대해 정의했는데요.
위키
먼저, 위키에서는 다음과 같이 정의하고 있습니다.
Test-driven development (TDD) is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases.
테스트 주도 개발(TDD)은 소프트웨어가 완전히 개발되기 전에
소프트웨어 요구 사항을 테스트 케이스로 변환
하고, 모든 테스트 케이스에 대해소프트웨어를 반복적으로 테스트
하여 모든 소프트웨어 개발을 추적하는소프트웨어 개발 프로세스
.
마틴 파울러의 정의
그리고 저명한 개발자 마틴 파울러의 글에서는 다음과 같이 정의합니다.
Test-Driven Development (TDD) is a technique for building software that guides software development by writing tests.
테스트 주도 개발(TDD)은
테스트를 작성
하여소프트웨어 개발을 안내
하는소프트웨어 구축 기법
입니다.
켄트 벡의 정의
마지막으로 켄트 벡의 테스트 주도 개발에서는 다음과 같이 말합니다.
TDD란 프로그래밍 도중 내린
결정
과 그 결정에 대한피드백
사이의 간격을 인지하고, 또한 이간격을 통제
할 수 있게 해주는기술
을 말한다.
여기서 테스트(피드백)
가 공통적으로 등장하는것으로 보아 이게 가장 중요한 키워드임을 알 수 있는데요.
아마 대부분의 개발자분들도 테스트의 중요성을 잘 알고있어, 테스트를 작성하고 계시리라 생각합니다.
그런데 우리는 왜 테스트를 작성하거나 혹은 왜 그래야 한다고 생각하는걸까요?
우리가 테스트를 작성하는 이유
여러 이유가 있겠지만, 제가 생각하는 주요한 이유는 다음과 같습니다.
- 기능이 예상한 대로 잘 동작하는지 확인
- 반복적인 테스트를 효율적으로 수행
- 기능의 제약사항을 문서화
이들은 모두 개발자가 느끼는 불안
을 해소하기 위함인데요.
- 기능이 내 예상대로 동작하지 않으면 어쩌지?
- API 호출해가며 여러 케이스 테스트 했는데, 기능 변경되면 다음에 이걸 또 해야하나..
- 이 코드가 분명 어떤 이유로 추가했었는데 이유가 기억이 안나네
그리고 우리는 불안
이 생기면 주저하게 되거나, 보수적인 태도를 취하게 됩니다.
- 변경에 거부감을 느낌
- 기능이 잘 동작하는지에 대한 객관적 자료 부족
이런 불안들로 인해 우리는 생산성이 저하되고, 소프트웨어는 발전하지 못하게 됩니다.
불안을 해소하기 위한 노력
그래서 우리는 이런 불안들을 해소하기 위하여 테스트를 작성하는데요.
하지만 기능을 구현한 후 테스트를 작성하는건 심리적 거부감이 들 수 있습니다.
- 이미 끝난 작업이라 생각되어 귀찮음을 느낌
- 다음 구현할 기능이 밀려있어 테스트를 대충 작성
- 구현하는 동안 생각난 엣지 케이스등을 잊어버려 테스트 케이스 일부 누락
이런 상황이 발생하면, 테스트에 대해 회의감을 느낄 수 있는데요.
사실 테스트는 아무 잘못이 없고, 잘못은 테스트를 잘못 작성한 개발자에게 있습니다.
그럼 이를 해결하려면 어떻게 하는게 좋을까요?
기능 구현 후 테스트 작성
→ 일부 케이스 누락
→ 해당 케이스에 대한 장애 발생
문제 상황을 살펴보니 구현(결정)
과 검증(피드백)
사이의 간격
이 원인일 가능성이 높아 보이는데요.
즉, 이 간격을 줄여 빠르게 피드백
을 받을 수 있다면 이를 어느 정도 해결할 수 있어 보입니다.
빠르게 피드백을 받는 방법
가장 빠르게 결정
에 대한 피드백
을 받으려면, (예상하셨다 시피)테스트를 먼저 작성하면 됩니다.
테스트 주도 개발에서 켄트 벡은 아래와 같은 순서로 TDD를 진행합니다.
- 기능 요구사항을 분석하여 리스트 작성
- 리스트 중 하나를 선택하여 테스트 작성
- 실패하는 테스트를 작성
- 테스트 코드가 컴파일(혹은 성공) 되도록 필요한 부분을 구현.
- 테스트 코드가 컴파일(혹은 성공)되는것만을 목표로 작업
- 구현 도중 추가로 필요한 기능 혹은 제약사항이 생각나도 현재 작업 범위만 이어서 진행
- 추가로 생각난 내용은 1번에서 작성한 리스트에 추가
- 테스트를 실행하고 성공하는것을 확인
- 실패한다면 그 이유를 확인하여 구현을 수정
- 성공한다면 리스트에서 해당 항목을 제거
- 코드 스멜이 나는 부분을 찾아 리팩토링
- 리스트에 작성한 모든 내용이 제거될 때 까지 3~6을 반복
장황해보일 수 있지만, 이를 그림으로 표현하면 익숙한 TDD 사이클 그림으로 나타낼 수 있습니다.
그런데 이 그림에서는 1번이 보이지 않는데요.
RED → GREEN → YELLOW 를 시작하기 전에 해야할 일
RED에 1번이 포함되는게 아닌가 잠시 생각해봤지만, 저는 분리해서 생각해봤습니다.
(1. 기능 요구사항을 분석하여 리스트 작성)
둘 다 궁극적으로는 좋은 소프트웨어를 만들기 위함이라고 생각하는데요.
하지만 조금 더 디테일하게 보면 이 둘은 다른 곳에 포커스를 둬야한다고 생각합니다.
- 1번 : 이 기능을 요구사항(제약사항)을 잘 지켜서 출시하기 위함
- 2번(RED) : 특정 항목이 테스트를 통해 잘 검증될 수 있도록 하는 것
위 처럼 2번은 조금 더 좁은 범위에 포커싱하는게 좋은데요.
왜냐하면 테스트를 잘 작성하는것 자체도 어려운 일이고, 범위가 너무 넓으면 집중이 분산되기 때문입니다.
이렇게 집중이 분산되면 테스트에만 과하게 비중을 두거나, 구현에만 너무 몰두할 수 있죠.
그래서 의도적으로 작업 범위를 제한하여 작은 단위
를 빠르게 시도
하고 성공(혹은 실패)하는게 중요합니다.
왜냐하면 결국 TDD가 이루고하자 하는 궁극적인 목표는 작동하는 깔끔한 코드
이니까요.
그러기 위해서는 목표를 명확히 설정하고 작업 범위를 잘게 나누어 제한하는 1번 과정이 중요하다고 생각되었습니다.
그래서 만약 제가 TDD에 대한 그림을 그린다면 아래와 같이 그리고 싶네요.😁
멈출 줄 아는 용기
그리고 저는 1번 단계가 또 다른 중요한 장치가 될 수 있다고 생각하는데요.
(1. 기능 요구사항을 분석하여 리스트 작성)
현 상황이 TDD를 할 수 있는 상황인지를 판단해볼 수 있는 단계가 될 수 있다고 생각합니다.
- 외부 요인에 의해 일정이나 요구사항이 많이 영향 받는 경우
- 구성원들이 아직 TDD에 숙련되지 않은 경우
만약 이런 상황에 무작정 TDD를 시도했다간, 팀원들의 생산성과 프로젝트의 일정에 안좋은 영향을 미칠 수 있죠.
하지만, 1번 단계에서 미리 판단하고 멈춘다면 TDD를 무리하게 적용하는 리스크를 없앨 수 있습니다.
물론 TDD를 진행하는 중에도 멈출 수는 있겠지만, 시작 전에 멈추는 것 보다는 당연히 비용이 들것입니다.
마무리
배운점들과 더불어 제 생각을 길게 작성했는데, 이를 정리하여 말씀드리면요.
- 우리는
불안
을 해소하기 위해 테스트를 작성- 하지만,
결정
과피드백
사이의간격
으로 불안이 충분히 해소되지 못함
- 하지만,
- 남은 불안을 조금이라도 더 해소하기 위해
TDD
를 활용- 그렇지만 가장 중요한건
작동하는 깔끔한 코드
를 만들어 내는 것이다.
- 그렇지만 가장 중요한건
- 그러기 위해서는
목표 설정
을 명확히 해야함- 그리고 이 과정에서 상황에 따라 과감히 멈출 수 있음
저는 1번이 가장 중요하다는 말씀을 드리고싶은건 결코 아닙니다.
오히려 테스트, 구현, 리팩토링 과정은 아주 어려울 것이고 상황에 따라 또 다를겁니다.
그래서 무작정 테스트를 작성하며 시작하는건 옳지 않고, 미리 큰 그림을 그려봐야 한다는 것 입니다.
그리고 TDD와 TDD 연습에 대한 내용은 아래 첨부한 내용을 참고하시면 좋을 것 같습니다.
훌륭하신 분들의 좋은 자료들이 훨씬 더 쉽고 깊이있게 설명 해 주실겁니다 :)