



https://github.com/softmoca/java-racingcar-8/tree/softmoca-tdd
GitHub - softmoca/java-racingcar-8
Contribute to softmoca/java-racingcar-8 development by creating an account on GitHub.
github.com
시작하며
2번째 TDD 경험을 마쳤다 ! 1번째에서도 예상보다 시간과 리소스가 많이 소요 되었지만 이번에는 훨씬 더 예상 보다 많은 시간이 소요되어 결국 후반부터 의식적으로 프로젝트를 마무리 짓기 위해 노력했다. 가장 큰 이유는 2가지이다.
첫번째로 컨트롤러와 입출력 테스트가 생각보다 너무 많은 시간과 리소스를 잡아 먹었다.
처음으로 Mock을 사용해 런던파 스타일로 테스트를 작성해 보았는데 상상 이상으로 힘들었다..!
두번째는 기본적인 기능 구현 후 습관적으로 개선과 리팩토링할 사항을 OOP적으로 깊게 생각하며 몰입의 길을 잃어 다시금 인식하고 길을 찾는데 많은 시간이 소요 되었다.
그래도 TDD가 처음 개발과 입문 시 오랜 시간이 걸리다는걸 인지해 계획 일정을 세워서 나름 다행이라는 생각을 했다 !
“요구 사항 탐색 & 설계 피드백 도구로서의 TDD”
그간 그저 기계적으로 랜덤한 값과 관련된 테스트는 인터페이스를 통해 설계를 했었다.
하지만 TDD로 테스트를 먼저 작성하고 이후 작업을 고민하는 과정을 통해 명확한 순서로 필요성을 인지하고 설계를 정하는 경험을 했다.
그 과정에서 다시 한번 테스트를 단순한 검증 도구가 아닌, 요구사항 탐색도구이자 설계 피드백 도구로서 인지했다 !
처음에는 자동차가 전진하면 위치가 증가하는지 확인하고 싶다는 생각으로 car.move() 후 위치를 검증하는 테스트를 작성했지만, 테스트를 작성하는 순간 무작위 값 때문에 항상 일정한 결과가 나오지 않는다는 문제를 발견했다.
단순히 테스트가 실패한 것이 아니라, 요구사항과 설계 구조 자체가 테스트와 맞지 않는 다는걸 발견 했다.
그 후 전진 여부를 내부 랜덤 로직이 아닌 외부에서 주입받도록 설계를 변경했고, 이후에는 단순한 boolean이 아닌 MovingStrategy라는 인터페이스로 전진 조건을 분리하게 되었다.
또한 Car 객체가 전진 여부를 판단하는 것이 아니라, 전진하라는 명령을 받으면 전진만 수행하는 역할을 가져야 한다는 책임 분리의 중요성 느꼈다.
결과적으로 랜덤이라는 테스트 불가능 요소를 제거하는 과정에서 의존성 주입, 책임 분리, 전략 패턴과 같은 설계 개념들을 코드로 자연스럽게 녹여낼 수 있었고, 이 과정 자체가 요구사항을 더 정확히 재해석하고 구조를 더 견고하게 만들었다.
그저 이론적으로 암기식으로 알고 있던 느낌을 실제로 TDD를 통해 나의 손과 눈과 머리로 이런 과정을 느낄수 있어 너무 짜릿했다 !
“리팩토링 안전망으로서의 TDD”
초기 컨트롤러는 입력, 도메인 생성, 게임 실행, 출력을 모두 한 번에 처리하는 구조였고 테스트가 어려운 상태였다. TDD를 통해 살아있는 테스트 코드가 있는 상태에서 입출력을 인터페이스로 분리하며 의존성 주입 기반으로 구조를 과감하게 수정할 수 있었다.
특히 static 구조에서 인스턴스 구조로의 대규모 변경에도 불구하고 모든 테스트가 통과하는 것을 보며,
TDD가 구조 변경의 족쇄가 아니라, 리팩토링을 가능하게 하는 안전망이라는 사실을 또한번 체감했다.
이전에는 “고치면 깨질까 봐” 리팩토링을 회피했지만, 이번에는 “일단 고쳐보고 테스트로 확인하자”라는 태도로 설계를 개선할 수 있었다.
생각보다 많은 시간과 리소스가 소요 되었지만 막막함과 혼란스러움 보다 온전히 오랜 시간이 걸려서 힘들다 라는 점 말고는 리팩토링 안전망으로서의 기능을 온전히 느꼈다 !
이 역시 TDD의 심리적, 기술적 안정 장치를 통한 효과라고 느껴져서 너무 뿌듯했다 !
“컨트롤러 테스트의 비효율”
컨트롤러 테스트를 작성하는 과정에서 “모든 테스트가 같은 가치를 가지지는 않는다”는 사실을 몸소 온전히 느낀 경험을 했다. 입출력을 테스트하기 위해 여러 인터페이스를 도입하고, FixedInputReader, MockOutputWriter 같은 테스트용 객체를 구현하며 구조를 크게 개선했지만, 그만큼 테스트 작성과 유지 비용도 급격히 증가했다.
특히 MockOutputWriter를 관리하는 과정에서, 실제 검증 로직보다 Mock 세팅 코드가 더 길어지고 복잡해지는 상황을 겪으며 큰 의문이 들었다.“이 테스트는 정말 필요한가..?”, “아니면 테스트를 위해 테스트를 만들고 있는 건 아닐까?”라는 고민이 자연스럽게 따라왔다.
컨트롤러 테스트는 결국 입출력 메서드가 어떤 순서로 몇 번 호출되었는지와 같은 흐름을 검증하는 수준에 머물렀고, 핵심 비즈니스 로직은 이미 도메인 테스트에서 충분히 보장되고 있었다. 하지만 컨트롤러 테스트는 도메인 테스트보다 훨씬 많은 리소스를 요구했고, 리팩토링 시 오히려 발목을 잡을 정도로 구현 세부사항에 강하게 결합되고 있었다.
또한 Mock 기반 테스트는 메서드 이름 변경, 호출 순서 변경 같은 리팩토링 과정에서 쉽게 깨지며, 테스트 자체의 유지보수 비용을 급격히 증가시켰다. 이 시점에서 테스트가 설계를 돕는 도구가 아니라, 설계를 억제하는 족쇄처럼 느껴지기 시작했다.
결국 나는 이후에는 컨트롤러에 대해 모든 것을 자동화 테스트로 검증하려는 욕심을 내려놓고, 비즈니스 로직이 있는 도메인 테스트에 집중하고, 컨트롤러는 통합 테스트와 수동 테스트로 보완하기로 결론을 지었다 !
테스트도 하나의 코드이기에, 유지 비용 대비 얻는 가치를 고려해야 했고, 지금의 컨트롤러 테스트는 그 균형이 전혀 맞지 않는다고 판단했기 때문이다.
물론 여러 프로젝트 상황에 따라 다르겠지만 일반적으로 접하게 되는 여러 상황에서는 컨트롤러 테스트는 분명 독으로 작용할것 같다 !
하지만 이런 경험들 격었기에 “테스트를 많이 쓰는 것”이 중요한 것이 아니라, “가치 있는 테스트에 집중하는 것”이 훨씬 중요하다는 이론적인 내용을 온몸으로 느낄 수 있었다 !
매우 힘들었지만 오픈미션의 목표였던 “직접 경험하고 체득”을 잘 하고 있어 뿌듯했다.
”OOP 학습과 TDD 학습의 모순”
기능 구현을 모두 마친 후 여러 개선과 리팩토링 사항을 고민하는 과정에서 OOP와 TDD 사이의 모순과 충돌을 느꼈다. 첫 시작과 목적은 지금 필요한 것만 만들고, 과도한 설계를 피하기 위한 TDD의 원칙이었지만, 어느 순간 “유지보수성을 위해 구조를 미리 잘 설계해야 한다”는 OOP 학습을 하려고 끙끙대는 날 발견했다.
처음에는 이 두 가지를 동시에 완벽하게 만족시켜보기 위해 책임을 더 잘 나누고, 역할을 더 분리하고, 확장 가능한 구조를 만들고, Value Object를 최대한 도입하려고 노력했다.
하지만 그 과정에서 불필요하게 코드 복잡도가 증가하며, ‘개발 방향성을 놓치지 않게 해준다’는 TDD의 핵심 장점을 완전히 놓으며 잘못된 몰입을 하게 되었다.
당연히 TDD와 OOP는 같은 방향을 바라볼것 이라 생각했지만 실제로는 전혀 추구하는 가치가 달랐던 걸 깨달았다 ! TDD는 “지금 요구사항을 정확하게 해결하는 것”에 집중하고, OOP는 “미래 변경에 잘 대응할 수 있는 구조를 만드는 것”에 집중한다.
그 후 오픈 미션을 진행하는 동안 나만의 기준을 세웠다.
설계는 목적이 아니라 수단이며, 리팩토링은 ‘해야 해서’가 아니라 ‘필요해서’ 하는 것이라는 기준 !
그래서 이후에는 이론적으로 좋아 보이는 설계를 미리 적용하기보다 실제 사용 과정에서 불편함과 문제를 직접적으로 느낄 때 리팩토링과 구조 개선을 하려고 한다.
왜냐하면 이번 나의 목표는 TDD이기 때문이다 !
이 경험을 통해 완벽한 설계란 존재하지 않으며, 모든 설계는 항상 상황과 맥락에 따른 트레이드오프의 결과라는 것을 체감하게 또 다시 느꼈다. 중요한 것은 이론적으로 멋진 구조가 아니라 현재 문제를 가장 잘 해결하면서도, 나중에 고칠 수 있는 여지를 남기는 균형이라는 점이다.
하지만 현재의 나는 이 또한 꾸준한 학습과 경험이 동반되어 OOP적으로나 TDD적으로나 균형을 가지며 학습하고 성장해야할것 같다고 생각한다. 지금이야 TDD를 입문하는 입문자로서 이런 기준을 세웠지만 실제 실무에서는 TDD사이클 사이사이 제때 필요한 OOP적인 사고를 잘 녹여내야 한다고 생각한다.
마치며
1차 TDD 경험이 “충격과 깨달음의 시작”이었다면,이번 2차 경험은 그 충격 이후 마주한 현실적인 혼란과 선택의 연속이었다.
단순히 TDD를 한다는 것만으로는 충분하지 않았다.
어디에 힘을 써야 하는지, 어디까지 테스트를 해야 하는지, 어디서 멈춰야 하는지에 대해 모든 것을 스스로 판단해야 하는 순간들이 계속해서 찾아왔다.
컨트롤러 테스트에서 느낀 비효율, Mock 기반 런던파 스타일의 난이도, OOP 학습과 TDD 실천 사이에서의 충돌은 이제 이론이 아니라 직접 겪은 현실적인 딜레마가 되었다.
그럼에도 이번 경험이 의미 있었던 이유는, ‘완벽하게 해냈기 때문’이 아니라 헷갈려보고, 과하게 해보고, 실패해보고, 그 속에서 기준을 세웠기 때문이라고 생각한다.
특히 이번 미션을 통해 얻은 가장 큰 것은 TDD를 기법이 아니라, 상황에 맞게 조율해야 하는 도구로 바라보게 되었다는 점이다.
중요한 것은 지금 내가 무엇을 배우고 있는지, 어디에 시간을 써야 하는지, 어디서 멈출 수 있는지를 스스로 의식하며 선택하는 과정이었다.
또한 이번 2차 경험을 통해 OOP와 TDD가 충돌하는 것이 아니라 서로 다른 시간 축에 존재하는 개념이라는 사실을 조금은 이해하게 되었다.
TDD는 지금을 다루고, OOP는 내일을 준비하는 것 !!
나는 이제 그 사이에서 상황에 맞게 중심을 옮겨 가며 선택하는 연습을 하고 있다는 생각이 든다.
아직은 많이 서툴고, 여전히 헷갈리는 순간도 많지만, 분명한 건 나는 이제 TDD를 “따라 해보는 입문자”가 아니라 TDD 속에서 나만의 기준을 만들어가는 학습자가 되어가고 있다는 것이다.
이제 중요한 건 또 한 번의 미션, 또 한 번의 실패, 또 한 번의 선택 속에서 이 기준들을 조금씩 더 다듬어 가는 일일 것이다.
꽤나 많이 지치고 몇번의 좌절과 무기력을 느끼기도 했지만 이러한 모든 과정 자체가, 내가 이번 오픈 미션을 통해 가장 얻고 싶었던 진짜 경험이라고 생각한다 !!
'외부활동 > 우아한테크코스 [프리코스]' 카테고리의 다른 글
| 테스트 주도개발 TDD 실천법과 도구 정리 도서 요약[FAQ & 설계사고과정] (0) | 2025.11.13 |
|---|---|
| 테스트 주도개발 TDD 실천법과 도구 정리 도서 [핵심 요약] (0) | 2025.11.13 |
| [우아한테크코스 8기] 프리코스 오픈미션 1차 회고- 문자열계산기 TDD (0) | 2025.11.09 |
| TDD 핵심 정리 (0) | 2025.11.08 |
| 테스트주도 개발 시작하기 - 챕터 10~11. 테스트 코드와 유지 보수 & 마무리 (0) | 2025.11.08 |