TDD를 하지 않으면 프로 개발자가 아니다?
Bob Martin과 Jim Coplien 의 TDD에 대한 토론을 보고 느낀 점에 대한 기록
5/19/2024, 9:39:00 PM
유튜버 데브원영 님이 올려주신 영상 ‘테스트 주도 개발(TDD)의 장단점: Bob Martin과 Jim Coplien의 토론’ 을 보고 느낀 점에 대한 기록
서론
클린 코드, 클린 아키텍처 라는 책으로 너무나 유명한 로버트 마틴 과 역시 에자일이라는 주제로 저명한 코프의 TDD에 대한 토론을 보았다.
짧은 토론이지만 덕분에 오히려 간결하고 명확하게 각 입장을 이해할 수 있었다.
에자일을 누구보다 실천하고자 하는 두 거장의 입장차이와 그 사이에서 합치되는 의견을 보다보면 정말로 중요한 가치는 무엇이었는가를 깨닫게 해준다.
TDD의 정의
토론의 시발점이 되는 마틴의 논지는 다음과 같다. TDD를 실천하지 않는 개발자는 '프로'라고 불릴 자격이 없다.
두 사람은 먼저 TDD를 명확하게 정의하고 토론을 시작한다.
- 실패하는 단위 테스트를 작성하기 전까지는 단 한줄의 상용 코드도 작성하지 않는다.
- 의미있게 실패하는 것 이상의 단위 테스트를 작성하지 않는다. 상용 코드를 적기 전에 너무 많은 테스트를 작성하면 안된다.
- 테스트를 성공시킬 수 있는 만큼의 상용 코드를 작성한다.
- 위 세가지 단계를 짧은 주기(약 30초~1분)로 반복하면서 진행한다. 결론적으로 테스트와 상용코드는 ‘동시에’ 작성된다.
두 사람은 현재 (이 영상은 이미 12년 전 이지만..) TDD 를 실천하는 사람들의 대다수가 잘못된 방식으로 TDD 를 이해하고 있다는 점에 동의하기 때문에, 이 정의는 마틴이 생각하는 올바른 TDD 의 정의라고 할 수 있다.
특히 마틴은 ‘딱 필요한 만큼만의 테스트 코드와 상용 코드’ 라는 지점을 강조하는데 이 부분이 이후 코프가 계속해서 주장하는 TDD의 문제점과 맞닿아있다. 테스트 코드는 얼마나 앞서 나갈 수 있는가? 테스트보다 앞서는 것은 정말 없는가?
테스트 코드 vs 아키텍처
코프는 TDD 를 진행하기 이전에 아키택처를 먼저 고려해야 한다고 주장한다.
TDD 는 단위 테스트로 수행되며, 객체지향 프로그레밍에서 ‘단위(Unit)’ 는 ‘객체(Object)’ 가 되는데, 우리가 테스트하는 것은 ‘절차(Procedure)’ 이다.
이렇게 되면 각 객체 수준에서 정의된 메서드를 통해 Bottom-Up(상향식) 으로 절차가 구현되게 되는데, 코프는 이를 ‘2차원 데이터로 3차원 구조물을 설계’ 하는 것이라고 비유한다.
즉, 3차원에 대한 이해 없이 2차원 데이터로 만들어 나가다가는 어느 순간 근본적인 구조적으로 잘못됨을 깨닫게 되고, 이때는 돌이키기 어려워진다는 이야기이다.
코프는 도메인 지식이나 사용자의 개념 모델을 기반으로 우선 고려된 아키텍처가 존재해야 한다고 말한다.
마틴은 아키텍처에 대한 신중한 고려가 중요하다는 명제에는 강하게 동의한다.
특히 몇몇 TDD 개발자들은 “테스트를 작성하다보면 아키텍처는 저절로 완성된다” 라는 믿음을 가지고 있는데 완전히 잘못된 말이라고 동의한다.
하지만 마틴은 아키텍처는 완벽하게 정의된 상태로 존재하는 것이 아니며 여러번의 반복(iteration) 을 통해 조금씩 완성된다고 믿는다.
TDD 의 방식을 취하면서도 아키텍처를 고려하며 발전 시킬 수 있다는 주장으로 보인다.
이후는 위 주장을 기반으로한 몇번의 대화가 이어진다.
코프는 도메인 지식을 기반으로 초기부터 알고 있는 것은 충분히 고려하고 적용된 상태로 시작되어야 한다고 주장하며, 그러한 지식을 구조화하는 인터페이스와 역할을 미리 정의하고 필요할때 해당 인터페이스를 구현하는 방식으로 작업해야 한다고 한다.
반면 로버트는 무언가 쓰일지도 모르는 추상이나 인터페이스를 ‘미리’ 만들지는 않을 것이며 그것은 테스트가 이끌어갈 것이라고 한다.
코프는 추정하지 않는 것의 중요성에는 동의하지만(그것이 Lean 이므로) ‘이미 알고 있는 지식(또는 필수적인 도메인 지식)’ 을 고려하지 않고 출발하는 것의 위험성을 이야기한다.
결론적으로 둘은 최소한의 추측(추상) 을 기반으로 나아가야함에 대해 상호 동의한다.
‘프로’ 개발자
이제 논의의 최종장으로 진행된다.
프로페셔널한 개발자는 TDD 를 반드시 수행해야 하는가?
로버트의 주장은 책임감 없이 단위 테스트도 하지 않고 코드를 배포하는 개발자들이 존재하며 이를 방지하는 가장 좋은 방법이 TDD 라는 것이다.
반면 코프는 단순히 테스트를 하는 것 보다 더 중요한 것이 있다고 주장한다.
코드를 깊게 이해하고 외부(실세계)의 관점에 집중하는 것, 비즈니스의 중요성을 이해하는 것 이 더 중요하다고 믿는다.
그러면서 ‘계약에 의한 설계’ 가 TDD 의 모든 장점을 가지고 있으면서 더 좋은 방법론이라고 주장한다.
계약에 의한 설계(Design by Contract - DbC ) 는 사전 조건, 사후 조건, 불변 조건을 미리 정의하고 해당 계약을 충실히 이행하는 코드를 짜는 방법론이다.
마치 계약서처럼 핵심적인 범위와 기능을 정의하고 이를 보장하기 때문에 더 근본적으로 ‘이 코드가 존재하는 이유’ 에 집중하는 방식이다. TDD 의 단위는 객체이기 때문에 더 큰 그림을 보기 힘들다고 주장한다.
로버트는 계약과 단위 테스트는 1대1로 변환 가능하다고 본다.
즉 오히려 TDD 가 계약에 의한 설계의 장점을 소화할 수 있으며, 오히려 TDD에서 상용코드는 테스트코드에 전혀 의존하지 않지만, 계약 이라는 개념은 코드 전역에 넓게 퍼져서 존재하게 되므로 더 혼란스럽다고 주장한다.
이후 TDD 와 계약에 의한 설계 의 장단점에 대해 짧게 논의하고 토론은 끝나게 된다.
감상평
최근 나는 업무를 하면서 테스트 코드의 위대함을 느끼고 있는 참이었다.
사실 전문 QA 조직이 존재하는 환경에서는 테스트 코드의 중요성을 쉽게 까먹게 된다.
계약에 의한 설계 에서 말하는 계약의 모든 부분을 QA 에서 직접 테스트를 해주기 때문에 정말 어지간한 UX에서 발생할 수 있는 대부분의 조건은 테스트 코드가 없더라도 모두 검증된 이후에 프로그램이 사용자에게 전달되게 된다.
그러나 최근 프로젝트는 부족한 QA 리소스와 급변하는 비즈니스 상황 속에서 속도감있게 진행되었는데,
급하게 오픈한 이후 많은 버그를 만나게 되었고 결국 테스트 코드의 필요성을 느껴 CI 툴을 연동하고 테스트 커버리지를 올렸다.
이후 새로운 기능을 추가할때도 테스트 커버리지를 유지하기 위해 기능 개발과 동시에 단위 테스트를 작성하고 있는데,
이것이 단순히 버그를 줄여주는 것 뿐만 아니라 테스트 하기 좋은 코드가 근본적으로 더 좋은 코드로 향하게 해준다는 것을 알게 되었다.
그러나 단위 테스트를 수없이 작성해도 결국 전체 비즈니스 로직을 돌려보면 기대하지 않은 결과를 만나게 되는 경우가 종종 있었다.
이것이 코드 설계 자체의 문제일 수도 있고, TDD를 온전히 수행하지 않은 탓일 수도 있겠지만 어쨋든 이러한 경험에서 코프가 이야기한 ‘우리가 테스트하는 것은 절차’ 라는 말에 크게 공감되었다.
결국에 나는 수많은 단위들이 포함된 통합 테스트를 여럿 작성하였고 비로소 모두 검증되었다는 느낌을 받았다.
단위 테스트에서 검증되지 않는 무언가를 발견하면 "지금 아키텍처가 잘못되었나?" 라는 의구심을 가지지 않을 수 없다.
내가 정의한 단위, 클래스의 역할과 책임이 잘못된 것인가?
분명 각 클래스는 명확한 의도와 책임이 있는 것처럼 보였다. 아마 실제로도 그럴 것이다.
그러나 절차를 설명하는데에는 어떤 결함이 있는 것이다. 나는 이러한 경험을 통해 아키텍처가 TDD 보다 우선한다는 주장에 크게 동의한다.
그러나 동시에 프로페셔널한 개발자라면 반드시 TDD 를 해야한다는 마틴의 주장에도 일리가 있다고 본다.
도메인에 대한 충분한 이해가 바탕이 되는 상태에서의 TDD 보다 더 안정적으로 프로덕션 코드를 작성할 수 있는 방법이 있을까?
반드시 단위 테스트를 짜야 한다는 명제에 동의한다면 TDD 를 하지 않을 이유가 있는가?
TDD가 너무 강력한 원리원칙을 제시하여 거부감이 드는건 사실이지만, 여러 명의 개발자들과 협력하는 상황에서 TDD라는 원칙을 공유하는 것보다 서로에게 더 책임감을 부여하고 행동하게 하는 명분과 수단이 있는가?
마틴은 이 업계의 프로페셔널에 대한 정의가 모호한 편이라고 생각한다.
확실히 모든 개발자들이 보편적으로 지향하는 프로페셔널한 개발자의 자질이나 프로페셔널을 위해 추구해야하는 가치 같은 것이 명확한 형태로 공유되지는 않는 것 같다.
적어도 올바른 TDD는 일관된 원칙을 통해 보다 견고하고 오류가 적은 소프트웨어를 구축할 수 있게 돕는다.
이를 실천하는 것 자체가 바로 프로페셔널한 접근법이라고 할 수 있을 것이다.
댓글
* 작성 이후에 수정/삭제 할 수 없어요!
아직 작성된 댓글이 없습니다.