본문 바로가기
Dev/고민과 삽질의 기록들🤔

팀내 테스트 코드 문화 도입 과정 (Unit test의 본질 재정립)

by Mintta 2024. 1. 8.

오랜만에 아티클로 다시 돌아왔습니다. 연말에는 몇일정도 잠시 휴식기간도 가졌고, 새해초부터는 알고리즘 캠프에 들어가서 빡세게 과제들을 하느라 정신이 하나도 없었네요..ㅎㅎ 그래도 이번주부터는 다시 포스팅도 힘내서 써볼 동기부여되는 일들도 있었기에 다시 열심히 해보겠습니다. 오늘 글의 주제는 테스트 코드입니다. 항상 관심이 많았던 주제인데 최근에 좀 새로운 인사이트를 얻게 되어서 아직 팀내에서 명확한 결론이 난 주제는 아니지만 그 과정이 의미있어 이에 대해서 정리해보고자 합니다! 


 

1. 배경

저는 MVC로 시작한 프로젝트를 최종적으로 MVVM-C의 형태로 리팩토링을 진행한 경험이 있습니다.
그리고 그 모든 과정을 관통하는 것이 바로 "Testable한 객체"입니다.
 
Testable한 객체를 만들기 위해 객체간의 책임과 역할을 명확히 하고, 단일 책임을 가진 객체로 만들어주기 위해 MVC의 Massive View Controller의 책임을 분리시키다 보니 끝내 도달한 형태가 MVVM-C의 형태였을 뿐인거죠. 단일 책임을 가진 객체에 진심이였던 이유는 이때까지 생각했던 unit test의 본질 때문이였습니다. 이 때에는 Unit test의 본질은 '문제가 생겼을 때 해당 문제의 원인을 바로 특정지을 수 있는 것에 있다'고 생각했었습니다.
https://codingmon.tistory.com/64

Coordinator Pattern 도입 이유와 실제 도입기 (ft. 객체들의 책임과 unit test)

https://github.com/Team-LionHeart/LionHeart-iOS GitHub - Team-LionHeart/LionHeart-iOS: 라이옹 🦁 라이옹 🦁. Contribute to Team-LionHeart/LionHeart-iOS development by creating an account on GitHub. github.com Coordinator Pattern을 공부를 시

codingmon.tistory.com

https://codingmon.tistory.com/74

DIP의 본질? (Testable한 객체를 위한 여정 중..)

정말 오랜만에 다시 아티클을 쓰네요. 최근에는 여러 기업 코딩테스트도 있었고, 알고리즘 공부, 리팩토링 당장 해야할 것들로 벅차서 계속 미뤄지고 있었네여 다시 열심히 시간을 내서 써보겠

codingmon.tistory.com

 
Testable한 객체를 만들기 위해 거대한 다수의 책임을 지닌 객체를 단일 책임을 가진 객체들로 분리한 결과, 이제 모든 로직을 '객체와 객체간의 협력'을 통한 방식으로 생각할 수 있게 되었습니다. 오브젝트 책에서의 표현을 빌리면 일종의 애니메이션과도 같이 말입니다. 살아있지 않은 객체들이지만 서로 메시지를 보내고 이에 기대하는 값을 응답해주는 식으로 마치 살아있는 듯이 동작합니다.
 
'객체와 객체간의 협력'으로 바라보게 되면, 객체가 다른 객체에게 요청을 보내기 위해 그 객체를 가지고 있게 되었습니다. 이런 객체를 의존성 객체라고 부릅니다. 이 때 의존성 객체를 구체 타입으로 들고 있게 되면 결합도가 높아지게되어 책임을 다른 객체로 분리한 효과를 볼 수 없게 됩니다. 이를 위해서 DI를 통해 객체간 의존성을 낮추어주었습니다. '추상화'를 적극적으로 활용하게 됩니다.
 
이런 추상화들 덕분에 Unit test를 할 때에도 많은 이점이 있었습니다. test를 하고자 하는 로직을 위해 필요한 환경들을 Test double로 손쉽게 주입해줄 수 있었습니다. '추상화'를 통해 얻을 수 있는 유연함의 이점을 몸으로 직접 느낄 수 있는 순간이었습니다.
 

2. Unit test의 본질 재정립

이 쯤에서 다시 처음으로 돌아가서 unit test의 본질에 대해서 다시 생각해볼 필요가 있을 것 같습니다.
 
처음에 unit test의 본질을 '문제가 생겼을 때 해당 문제의 원인을 바로 특정지을 수 있는 것에 있다'고 생각했었습니다. 하지만 생각해보면 이는 부수적인 효과와 같은 것이었고 test의 본질은 다른 곳에 있었음을 느낄 수 있었습니다.
 
바로 "테스트에 대해 생각하고 코드를 작성하는 것"입니다. 테스트 코드를 짜기 위해 테스트라는 개념을 기반으로 코드에 대해 고민을 하다보니, 객체간의 결합도를 낮추고자 하는 시도를 하게되었고 본격적으로 추상화를 하게 되었습니다.
 
실용주의 프로그래머 책에서 나온 표현을 빌리자면 테스트 작성에 대해 생각함으로써 코드의 작성자가 아니라 '사용자'인 것 처럼 메서드를 외부의 시선으로 보게 되었기에 이런 장점들을 얻을 수 있었습니다.
 
결국 무언가 테스트하기 좋게 만들고자 하면 결합도가 낮아지는 것은 그에 따라오는 당연한 효과인 것 입니다.
 
 

추상화를 왜 하나요?

 
누군가 제게 추상화를 왜 하냐고 묻는다면 예전에는 추상화의 장점에 대해서만 이야기했을 것 같습니다.
이제는 테스트 코드에 대한 것을 먼저 물을 것 같습니다. 결국 테스트에 대해 생각하게 된다면 추상화 작업은 따라오게 되는 부수적인 것에 불과하기 때문이니까 말이죠. 처음 생각했던 본질이였던 '문제가 생겼을 때 해당 문제의 원인을 바로 특정지을 수 있다'라는 것도 결국 테스트 코드에 대해 생각하고 작성하다보니 얻게 된 결과에 불과했던 것이라는 생각이 들었습니다.
 

3. TDD? 우리팀에서는 어떻게 ? 🤔

이렇게 테스트에 대한 본질까지 정리하고 나니, TDD에 대한 궁금증도 생겼습니다.
 
TDD는 기본적으로 '완료'에 대한 정의를 먼저 해두고, 이 후 개발을 통해 미리 정의해둔 '완료'를 달성하면서 리팩토링하는 방법론에 대한 것이라고 생각합니다. 그런데 이 '완료'에는 두가지 측면이 있습니다.
 
첫번째는, 기획(고객 기준)이 정해주는 '완료'입니다. 제 경험을 빗대어 떠올려보면 기획팀에서 주는 기능명세서를 말할 수 있을 것 같습니다.
두번째는, 개발자 기준에서 정해지는 '완료'입니다. 제가 구현할 기능에서 필요한 로직의 명세를 미리 작성해두고 개발을 시작하는 것 입니다.
 
첫번째 '완료'에 대한 정의의 경우, 이를 바로 테스트 코드로 표현할 수 있습니다. 하지만 이건 개발쪽에서 바로 실행할 수 있는 것은 아니고, 기획팀 쪽에서 '완료' 작업에 대한 정의를 받아야하기 때문에 협력을 통해 이루어질 수 있는 부분이라서 언제나 적용가능한 테스트는 아닐 수 있다고 생각했습니다. 하지만 해당 작업이 사전에 이루어진다면 명세에 대한 이해가 잘되었는지, 요구사항에 대한 피드백이 구현 뒤에 이루어지는 것이 아니라 구현 전에 이루어지기 때문에 훨씬 더 빠르고 많은 피드백이 이루어질 수 있다는 점에서 장점이 있을 것 같습니다.
 
두번째 측면은, 개발팀 혹은 개인으로도 충분히 가능한 부분이라고 생각합니다. 미리 구현할 기능에 대한 명세를 작성해두고 그에 생길 수 있는 엣지케이스들에 대한 고민을 하고 개발을 시작하게 되면 최소한의 것만을 가져가면서 기능을 구현할 수 있게 될 것이라는 장점이 있을 것 같습니다.
 
 
이렇게 '완료'에 대해 정의해두고 리팩토링해나가는 TDD방법론을 팀내에서 적용해보고자 이야기를 꺼냈고, 지금은 테스트 코드를 쓰는 방법에 있어서 현재 이야기중에 있습니다.
 
팀내에서 있었던 논의 중 몇가지 의미있었던 내용을 남겨보겠습니다.
 
- TDD에서의 테스트 코드를 미리 작성하는 것에 대해서 진짜 사소한 것까지 테스트 코드를 먼저 적는 것이 의미가 있을까
- 개발을 할 때에 어느정도 에러 상황에 대해서 염두에 두고 개발을 짜는데, 이런 상황에서 먼저 테스트 코드를 적는 것에 대한 의미?
- 에러가 난다면, 에러가 난 상황에서 테스트 코드를 적고 리팩토링 과정을 거치고 테스트 코드를 통과시키는 방법, 에러가 난 이후의 TDD 사이클을 돌리는 것은 어떨까 ?
 
위의 논의들을 거치고 난 후에 우선 제가 느낀 것은 테스트 코드를 작성하는 것이 아직 온전히 제 손에 익지 않았기에 판단이 불가능하다는 것이었고 중요한 테스트 코드를 많이 작성해보는 것이었습니다. 따라서 이번주 수요일, 글을 쓰는 시점으로는 2일 뒤겠네요. 그 날 오프라인으로 팀원들끼리 모여서 하나의 ViewModel을 가지고 구현해둔 것을 지운채 TDD방식과 코드와 테스트 코드를 함께 작성하는 두가지 방법을 적용한 페어프로그래밍을 하기로 했습니다🧑🏻‍💻🧑🏻‍💻🧑🏻‍💻
 
아마 그 날 페어프로그래밍이 끝나고 느낀점을 모아서 팀내에서 어떻게 테스트 코드를 작성해나갈지 판단을 해볼 것 같습니다! 그 때 다시 새로운 글로 어떤 결정을 내리게 되었는지 느낀점을 담아서 다시 정리해보도록 하겠습니다! 

댓글