회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 최대 700만 원 지원받으세요
개발자가 작성하는 자동화된 테스트는 오랫동안 갑론을박이 있던 화제입니다. 그러나 현실적인 타협점을 찾는다면 그 효용성은 분명합니다. 평소에 테스트를 바라보는 몇 가지 관점이 부딪힌다는 생각을 갖고 있었지만 아직 글로 쓴 일은 없었는데, 지난 글 <코드 리뷰에 ‘켄트 벡’의 아이디어 접목하기>와 비슷하게, 켄트 벡(Kent Beck)이 쓴 ‘Abstract vs. Concrete Parameters’란 제목의 글이 고맙게도 또다시 영감을 주어 쓰는 글입니다.
회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!
확인
개발자가 작성하는 자동화된 테스트는 오랫동안 갑론을박이 있던 화제입니다. 그러나 현실적인 타협점을 찾는다면 그 효용성은 분명합니다. 평소에 테스트를 바라보는 몇 가지 관점이 부딪힌다는 생각을 갖고 있었지만 아직 글로 쓴 일은 없었는데, 지난 글 <코드 리뷰에 ‘켄트 벡’의 아이디어 접목하기>와 비슷하게, 켄트 벡(Kent Beck)이 쓴 ‘Abstract vs. Concrete Parameters’란 제목의 글이 고맙게도 또다시 영감을 주어 쓰는 글입니다.
먼저, 자동화된 테스트를 다룰 때 TDD(Test Driven Development)를 빼놓을 수는 없습니다. TDD는 다름 아닌 켄트 벡이 정의한 방법입니다. 그는 자신의 저서 <테스트 주도 개발(Test-Driven Develipment)>을 통해 TDD 개념과 실천 방법을 소개했습니다. 더불어 TDD를 위한 실행 도구인 JUnit이라는 프로그램도 내놓아 개발자들에게 상당한 기여를 한 분입니다.
또한, TDD는 논쟁의 화두로 사랑받았던 주제이기도 합니다. 이를 찾아보려고 오래간만에 구글링을 해 보니 마틴 파울러의 블로그에 오래전에 봤던 기사 ‘Is TDD Dead?’가 남아 있었습니다. 예전에 커뮤니티 활동할 때 뵈었던 분이 한글화도 해 두셨네요.
그 유명한 논쟁에 대해 말하려는 것은 아니고, TDD는 이렇듯 꽤 오랫동안 견해 충돌을 빚어온 방법이라는 점을 먼저 전제하려는 목적입니다. 어떤 의견 충돌일까요?
많은 사람들이 TDD에 대해, 이것이 사용자 역할을 수작업으로 대신하는 것이 아니라 프로그램이 사용자 역할을 대신하는 것을 지칭하는 '자동화된 테스트'를 의미한다고 생각하곤 합니다. 하지만, 그런 식으로 활용하는 것은 엄밀히 말하면 TDD를 따르기보다는 XUnit을 쓰는 일이라고 할 수 있습니다. 그럼에도 불구하고 실제로 다수가 그런 식으로 표현합니다. 이게 옳다, 그르다를 말하는 건 아닙니다. 관찰해본 결과 그렇다는 것이죠.
저는 이런 방식이 TDD의 정확한 의미는 아니라고 봅니다. TDD에 포함된 '주도(Driven)'한다는 것의 의미는 자동으로 테스트한다는 것을 뜻하는 것이 아니기 때문입니다. 제가 익히고 활용하는 TDD는 지금 짜야 할 코드가 정확하게 무엇인지를 규정하는 일에 가깝습니다. 그런 관점으로 보면 TDD는 테스트 작성을 통해 ‘짜야 할 프로그램'이라는 문제 영역을 정확하고 체계적으로 구성해 나가는 개발 방식이라고 할 수 있습니다. 여기서 테스트는 전통적인 역할을 넘어서서 점진적인 설계를 하는 수단으로 쓰여 개발의 중심이 되기 때문에, ‘테스트 주도’라고 표현할 수 있는 것입니다.
정리하면, TDD란 단어는 이와 같이 크게 두 가지 용례로 쓰이고 있었습니다.
그런데 최근 한 가지 이야기를 듣고 개발자의 테스트에 대해 관점이 나누어진다는 사실을 다시 한번 확인했습니다. 최근에 동료가 함께 일하는 다른 회사의 개발자에게 프론트 테스트 코드 작성을 권하고, 해 보지 않았다면 자신이 방법을 알려주겠다고 했는데 이를 강하게 거부했다는 말을 들었습니다. 과거의 개발자들의 행동 패턴으로 짐작해보건대, 그가 거부한 것에는 두 가지 이유가 있을 것이라고 생각합니다.
하나는 그가 새로운 것을 배우기 힘들어하는 유형의 개발자일 경우, 배우는 고통에 비해 얻을 수 있는 효과가 적다고 판단했을 가능성이 있습니다. 사실 해 보기 전에는 얻을 수 있는 효과를 추정할 수가 없습니다. 그래서, 일단 한 번은 해보고 판단해야 하는데, 귀찮거나 막연한 두려움을 느끼는 분들이 의외로 많습니다.
두 번째 이유는 코드를 가꿔나가는 일과 협업에 대한 경험이 부족한 탓입니다. 제가 <코드 리뷰가 개발 문화에 미치는 영향>이라는 글을 쓴 배경에는, 협업이 중요하고 실질적으로는 코드 리뷰를 통해 빈번한 협업이 가능하다는 믿음이 있습니다. 그런데 코드 리뷰나 협업이 서툰 분들은 남의 코드를 읽는 어려움, 자신의 코드가 남에게 보일 때 느끼는 긴장감 등을 잘 견디지 못합니다. 더구나 코드 리뷰는 바로바로 결과를 만드는 일과 달리 짜릿함이 없습니다.
테스트 이야기를 하다가 왜 갑자기 코드 리뷰냐고요? 코드 리뷰와 테스트 코드 작성은 다른 일이지만, 두 가지 모두 협업 여부와 밀접하게 관련되어 있습니다. 테스트 코드를 작성하면서 점차 하려는 바가 명확해지고, 또 어떤 부분을 중점적으로 판단했는지 기록으로 남길 수 있습니다. 이는 효과적인 협업을 위한 탄탄한 토대가 되기 때문에 중요합니다. 즉 테스트를 협업의 관점에서도 생각해볼 수 있는 것이죠.
정리해보자면, 테스트를 바라보는 관점은 세 가지가 있습니다. 앞서 TDD의 의미에 관한 두 가지 관점에 더해, 협업에 관한 관점까지 고려해볼 수 있습니다.
먼저 첫 번째 관점은 ‘자동화 테스트'를 주제로 한다고 할 수 있습니다. 요즘IT에도 ‘효과적인 JUnit 사용 방법과 유용한 팁’이란 글이 있는데, 같은 주제로 볼 수 있습니다. 프로그래밍 언어나 테스트 대상 프로그램에 따라 확장된 개발 도구를 XUnit 이라고 하는데, 자신의 작업 환경에 맞는 방법을 익히면 반복적인 수작업을 줄일 수 있어 생산성이 향상됩니다. 상대적으로 익히는 데 큰 어려움이 없고 투자한 만큼 효과를 바로 얻을 수 있죠.
다만, 자동화 테스트를 하다 보면 프로그램을 테스트하기 어렵게 작성한 점을 발견할 수 있습니다. 그 문제를 해결하는 일은 쉽지 않을 수 있습니다. 여기서 테스트에 대한 고민을 멈출 수도 있고, 더 나아지기 위한 고민을 할 수도 있습니다. 그때가 바로 테스트에 대한 첫 번째 관점을 벗어나 새로운 인식이 생기는 시점이고, 이러한 인식이 생기면 제가 두 번째 관점으로 제시한 내용에 공감할 가능성이 높습니다.
두 번째는 TDD의 정의 그대로를 익히면서 배우는 측면에 대한 것입니다. TDD를 처음 배울 때, JUnit 을 익히는 일은 비교적 간단한 문제였습니다. 그런데 바로 다음 장애물은 생각보다 커다란 문제였습니다. 테스트할 코드 말고 현재 상태를 재현하는 일이 바로 그것이었는데요. 가장 먼저 반복적으로 겪는 문제가 바로 데이터베이스 상태를 재현하는 일입니다.
2007년이라 오래전 일이지만 개발자 커뮤니티를 만들기 위해 해당 주제를 첫 번째 발표로 기획하고, 제가 발표했던 경험도 있었습니다. 발표 제목은 ‘다중 레이어 환경에서 Spring을 활용한 통합 테스트 및 단위 테스트 방안’이었는데, 당시로써는 흔히 찾아볼 수 없는 희귀한 내용이었습니다.
당시에는 이런 실용적인 테스트 방안의 해법을 찾는 일이 어렵다고 느낄 뿐 원인을 제대로 설명조차 하지 못했습니다. 지금 돌이켜보면 단지 프로그래밍 기술의 문제가 아니었다고 생각합니다. 정규 교육 과정에서는 문제를 해결하는 데에만 초점을 맞추었기 때문에, 문제를 명확히 정의하고 훈련하고 연습하는 부분은 부족했습니다. 그래서 문제 정의도 못하는데, 여기에 더해서 ‘프로그래밍 언어 수준'에서 정교하게 문제 정의를 익히는 게 필요한 TDD를 실행하는 건 굉장한 고행의 시간이 아닐 수 없었습니다. 조금 과장해서 말하면 개발자로 다시 태어나는 기분이 들었습니다.
다행스러운 사실은 몇 달간의 고행 끝에 감을 좀 잡은 뒤부터 TDD는 저에게 프로그래밍 영역을 벗어나서 응용할 수 있는 ‘시간을 극도로 효율적으로 쓰는 일’을 알려 주었습니다. 그리고 그런 저에게 TDD는, 충동적으로 하고 싶은 대로 프로그램을 짜거나 호기심에 이끌려 코드를 남발하던 습관을 이겨내고, 지금 당장 꼭 필요한 코드가 무엇인지부터 생각하고 정의하게 만드는 스펙 정의에 가까웠습니다.
결국 프로그래밍을 하기 전에 ‘정확하게 무엇을 짤 것인지' 생각하고 정교하게 기록하는 훈련은 문제 정의를 연습하는 탁월한 방법이 되었습니다.
TDD를 개인 차원에서 익힌 사람이라면 팀 차원에서의 효과를 생각해 볼 수 있습니다. 사실 세 번째 관점은 켄트 벡의 새로운 글을 읽으며 이제 막 인식한 관점입니다. 켄트 벡은 자신의 글에서 테스트를 ‘통제력(controllability)을 유지하는 설계를 위한 도구’로 설명합니다. 앞서 언급한 TDD를 경제성의 관점에서 해석한 것이죠. 이러한 관점을 통해 테스트를 소프트웨어 설계가 지향해야 하는 장기적 생산성 그리고 변화에 대한 유연성을 확보하기 위해 투자하는 활동으로 바라볼 수 있습니다.
그는 테스트를 통해 소프트웨어 통제력을 높이는 과정에서 코드를 구성하는 매개 변수가 경우에 따라 추상적일 때 유리할 수도 있고, 구체적일 때 유리할 수도 있는 모순을 설명합니다. 개발자들이 테스트를 먼저 작성하다 보면 이런 결정들을 (테스트를 작성하지 않을 때에 비해) 명료하게 인식할 수 있습니다. 그리고, 기록(테스트 케이스 형태)으로 남다 보니 이후에 다시 개선할 여지도 생기고, 다른 사람과 협업하는 데에도 확실히 유용합니다.
켄트 벡의 글은 코드 예제를 바탕으로 추상성과 구체성에 따르는 비용과 경직성(rigidity)의 관계를 묘사함으로써, 테스트를 활용하여 설계의 목표를 달성하는 일은 결국 직업 일상에서 적절한 균형점을 잘 유지하는 일임을 보여 주는 듯합니다.
저는 여기에 더하여 ‘정원 관리’라는 개념을 추가하여 꾸준히 해나가는 일상성과의 결합을 강조하고 싶습니다. 일단, 정원 관리와 프로그래밍의 연결은 생소할 수 있는데요. 이는 자신이 짠 코드를 다듬고 계속해서 잘 쓰이게 하는 일에 대한 은유입니다. 정원 관리가 정원이 있는 집에 사는 대가로 치러야 하는 일상의 노력이듯이 코드를 장기적으로 사용하려면 매번 기능을 추가하는 일과 별개의 노력을 들여야 합니다. 개발자들은 이를 흔히 리팩토링(refactoring)이라고 부릅니다. 변수 이름을 성의껏 짓는 일처럼 작은 단위부터, 여러 곳에 흩어진 코드에 영향을 주는 구조적인 변경까지를 일컫는 말입니다.
정원 관리에서도 때가 중요하듯, 리팩토링도 때가 중요합니다. 수정해야 할 프로그램이 어디인지 단번에 떠오르지 않는 수준이 되면, 리팩토링은 점차 기피하는 일이 되기 십상입니다. 그러한 이유로 테스트가 일상과 결합되지 않는다면, 장기적 생산성에 기여하기 어렵습니다.
한편, 기능을 추가하는 일 위주로 개발을 해 온 분들에게는 정원 관리가 지루하고 가치가 낮은 일처럼 여겨지기도 합니다. 앞서 이야기한, 프론트 코드 테스트를 거부한 개발자에 관한 일화가 떠오릅니다. 종종 개발자들은 늘어나는 코드량이나 눈에 보이는 기능의 완성만으로 자신의 할 일을 다했다고 느끼곤 합니다. 하지만 과연 그럴까요?
지금은 코드를 많이 짜고 기능을 빨리 추가한다고 사업이 잘 되는 시기는 지나갔습니다. 실제로 제 주변에는 짜고 싶지 않은 덜 중요한 영역의 코드는 ChatGPT를 이용해서 8할을 짠 후에 나머지 20%만 자신이 작성하는 것이 협업이 어려운 개발자를 고용하는 일보다 낫다고 말하는 스타트업 CTO가 있습니다. 세상은 바뀌었고, 앞으로도 계속 바뀔 예정입니다. 그것만은 분명하죠.
한편, 세 번째 관점은 TDD를 꼭 염두에 두지 않고도 자동화 테스트를 개인 차원이 아니라 팀 차원 활용하여 장기적 생산성과 유연성을 확보할 수도 있습니다. 모든 개인에게 TDD로 설계하는 훈련을 시킬 수는 없습니다. 보통 설계는 모든 개발자가 즐겁게 하는 활동은 아니기 때문에 현실에서는 적절한 타협점을 찾아야 합니다. 스포츠 경기에서 자주 회자되는 ‘팀보다 위대한 개인은 없다'는 말은 개발 팀에도 적용할 수 있다고 생각합니다.
누적되는 코드에 대해 미래에 발생할 수정을 쉽게 하기 위해 쌓아두는 테스트를 회귀 테스트라고 부르는데, 그 효과는 굉장합니다. 예전에 채수원 님이 쓴 <테스트 주도 개발 TDD 실천법과 도구>란 책에 제 인터뷰가 실린 일이 있는데, 다음 내용이 테스트를 바라보는 첫 번째 관점을 대변하는 글입니다.
Q.현재 테스트주도개발(이하 TDD)을 업무에 적용하고 계신가요?
네. 앞서 말씀드린 바대로입니다. 다만, TDD 전문가는 아닌 터라 장애물을 만나죠. 학습 목적이 아닌 경우에는 개발과 동시에 실험을 할 수는 없기 때문에 선택적으로 TDD를 적용합니다. SI 프로젝트를 참여하다 보면 처음 만나는 개발팀과 협력을 합니다. 이렇듯 실력을 알 수 없는 개발자 수 십 명이 함께 하는 경우는 테스트 환경을 통일하고 테스트를 쉽게 해주는 유틸리티를 만들어 교육을 하고 주로 데이터 입출력 부분에 대해서만 자동화 테스트를 작성하도록 유도합니다. 불특정 다수를 대상으로 무리하게 TDD를 적용하려고 욕심을 부리다가 학원(?)으로 바뀔 우려가 있고 저항감도 있으니 현장에서 터득한 적정 수위죠.
인터뷰를 할 당시 직업 일상에서는 함께 일하는 개발자가 자동화 테스트를 만들도록 가이드하고, 가끔은 강제하는 일도 했습니다. TDD를 하라고 권하지는 않았습니다. (KSUG라는 커뮤니티 활동에서는 권장하고 발표도 했습니다.) 고통을 참아가며 테스트를 먼저 짤 필요는 없지만, 자신이 짠 코드를 검증하기 위한 프로그램은 만들어야 한다고 판단했습니다.
두 가지 분명한 이점이 있습니다. 하나는 한참 시간이 흐른 후에 발생하는 수많은 장애를 줄이기 위해, 프로그램 작성자가 직접 테스트 코드를 짜는 일은 생산적인 활동이라는 점입니다. 그보다 더 큰 효과를 주는 이유는 따로 있었습니다. 테스트 코드의 묶음을 뜻하는 테스트 스위트(Test Suite)를 구축하면, 향후 내가 짠 코드를 다른 이유로 수정했을 때 발생할 수 있는 부작용을 암산이 아니라 테스트 코드 실행을 통해 눈으로 바로 확인할 수 있다는 점입니다. 개발자 자신과 개발팀의 스트레스가 대폭 줄고 수정을 쉽게 받아들일 수 있어 놀라울 정도로 생산성과 삶의 질이 올라갑니다.
테스트에 관해 대화를 하다 보면, 상대가 나와는 다른 관점에서 테스트를 언급한다는 걸 알게 되는 경우가 있습니다. 이럴 때 상대가 어떤 목적으로 테스트를 진행하고 있는지 알수록 대화가 잘됩니다. 이 글을 통해 설명한 것처럼 테스트에 관해 다양한 관점이 존재한다는 사실을 안다면, 테스트에 관한 대화를 할 때나 테스트에 관한 팀의 목표를 세울 때도 도움이 될 것입니다.
*글을 발행한 후 이 글의 소스가 된 켄트 벡이 이 글에 대해 공유해준 의견을 공유합니다.
“자동 번역된 버전을 읽어보니 감명 깊었습니다. 테스트에 대해 자신과 다른 이해를 가진 사람들이 공감할 수 있는 길을 제시해 주셨습니다. 테스트를 비웃는 프로그래머들은 자신이 작성한 버그에 대해 책임이 없다고 생각하는 경우가 많은 것 같습니다. 일부 조직에서는 이러한 사고를 장려하기도 합니다. 코드의 작동에 대한 책임이 누구에게 있는지에 대해 동의하지 않는다면 테스트에 대해서도 동의하지 않을 것이 분명합니다.” DeepL로 번역함
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.