tag:

테스트 주도 개발 : Test-Driven Development by Example

2017년 6월 13일

예전에도 테스트주도개발에 관한 글을 인터넷에서도 한참 찾아보고 읽었었다. 글을 읽고서 TDD를 행동으로 옮겨보면 대부분 글이 구호만 잔뜩 나열했지 무슨 일을 어떻게 해야 하는지 과정을 제대로 설명하는 경우가 거의 없었다. 나도 중요하다고는 늘 이야기하지만 현장에서 제대로 사용하지 않고 있었다. 막히는 부분을 어떻게 풀어야 하는지, 어떤 방법으로 고민해야 하는지 생각만 많아지고 해결하질 못했었다. 그래서 지난 번 사온 책 중 테스트 주도 개발 (켄트 백, 인사이트, 김창준 강규영 옮김)을 가장 먼저 읽어보게 되었다.

이 책에서는 예제로 먼저 시작해 TDD가 어떤 생각의 흐름에 따라서 진행되는지 보여준다. 그 뒤로는 TDD를 하게 될 때 접할 수 있는 의문점과 그 해결책을 나열한다. 대략적으로만 알았었기 때문인지 새로웠던 부분, 기억하고 싶은 내용이 많았다.

  • TDD는 프로그램을 코드 단위로 잘게 쪼개서 실행해볼 수 있는 좋은 방법이다. 작은 단위로 내 의도대로 실행되는지 입력과 결과를 관찰한다. 단, 테스트를 먼저 작성하는 것으로 코드가 동작하지 않는 상태임을 명확하게 확인한다.
  • 빌드가 되지 않는 상태에서 빨간 불이 들어오도록 최소 코드를 작성하는 것, 이 빨간 불을 초록 불로 최대한 빠르게 바꾸기 위해 매직 넘버도 서슴치 않고 사용하는 것, 초록 불이 들어온 후에 작성한 테스트를 통해 리팩토링 하는 과정을 따른다.
  • 매직 넘버를 반환하는 메소드는 삼각측량으로 고친다. 즉 동일한 메소드를 다른 입력과 결과로 테스트를 작성했을 때 두 케이스를 모두 만족하기 위한 리팩토링을 수행한다.
  • 결과를 하드코딩 하는 것은 죄악으로 일반적으로는 죄악으로 여겨지는 일이지만 필요할 때 사용할 수 있어야 한다. 이 단계를 생각하지 않고 먼저 큰 코드를 작성해버리면 당장은 테스트의 단계를 줄일 수 있겠다. 문제는 그렇게 작성한 코드가 생각대로 동작하지 않았을 때 테스트 작성도 어려워지고 고민해야 할 단위도 커진다는 점이다. 이럴 때 다시 매직 넘버를 반환하는 수준으로 돌아올 수 있어야 하는데 생각의 단위를 가볍게 돌리는 일은 쉽지 않다. 테스트를 작성하면서 코드와 멀어진다는 생각을 하게 되는 지점이었는데 이 단순한 답이 큰 도움되었다.
  • 코딩이 안될 땐 쉬어야 한다는 이야기는 참 좋은 미덕이다.

테스트를 언제 작성하는 것이 좋을까? 테스트 대상이 되는 코드를 작성하기 직전에 작성하는 것이 좋다. (p. 210)

나중에 작성하면 항상 통과하는 무의미한 테스트를 작성하게 될 수도 있다.

시스템을 개발할 때 무슨 일부터 하는가? 완료된 시스템이 어떨 거라고 알려주는 이야기부터 작성한다. 특정 기능을 개발할 때 무슨 일부터 하는가? 기능이 완료되면 통과할 수 있는 테스트부터 작성한다. 테스트를 개발할 때 무슨 일부터 하는가? 완료될 때 통과해야 할 단언(assert)부터 작성한다. (p. 211)

TDD를 가장 간단하고 와닿게 풀어낸 문장이다.

상향식, 하향식 둘 다 TDD의 프로세스를 효과적으로 설명해 줄 수 없다. 첫째로 이와 같은 수직적 메타보는 프로그램이 시간에 따라 어떻게 변해 가는지에 대한 단순화된 시각일 뿐이다. 이보다 성장(growth)이란 단어를 보자. ‘성장’은 일종의 자기유사성을 가진 피드백 고리를 암시하는데, 이 피드백 고리에서는 환경이 프로그램에 영향을 주고 프로그램이 다시 환경에 영향을 준다. 둘째로, 만약 메타포가 어떤 방향성을 가질 필요가 있다면 (상향 혹은 하향보다는) ‘아는 것에서 모르는 것으로(known-to-unknown)’라는 방향이 유용할 것이다. ‘아는 것에서 모르는 것으로’는 우리가 어느 정도의 지식과 경험을 가지고 개발을 시작한다는 점, 개발 하는 중에 새로운 것을 배우게 될 것임을 예상한다는 점을 암시한다. 이 두 가지를 합쳐보자. 우리는 아는 것에서 모르는 것으로 성장하는 프로그램을 갖게 된다. (p. 218-219)

TDD가 어떤 순환성을 갖는지 설명하는데 이 부분 탓에 TDD를 더 어렵게 고민하게 만든다는 생각이 들었다.

첫 걸음으로 현실적인 테스트를 하나 작성한다면 상당히 많은 문제를 한번에 해결해야 하는 상황이 될 것이다. … 정말 발견하기 쉬운 입력과 출력을 사용하면 이 시간을 짧게 줄일 수 있다.(p. 219)

한번에 모든 것을 작성하려는 습관을 버려야 한다.

화이트 박스 테스트를 바라는 것은 테스팅 문제가 아니라 설계 문제다. 코드가 제대로 작동하는지를 판단하기 위한 용도로 변수를 사용하길 원한다면 언제나 설계를 향상할 수 있는 기회가 있다. 하지만 두려움 때문에 포기하고 그냥 변수를 사용하기로 결정해 버리면 이 기회를 잃게 된다. 그렇게 말하긴 했지만 정말 설계 아이디어가 떠오르지 않으면 어쩌겠는가. 그냥 변수를 검사하게 만들고 눈물을 닦은 후, 머리가 좀더 잘 돌아갈 때 다시 시도해보기 위해 적어놓고서 다음 작업을 진행할 것이다. (p. 255)

테스트를 작성하면서 테스트가 코드 내부 구현을 너무 많이 알고 있을 때 코드에 의존적인 테스트를 작성하기 마련이다. 이 책을 읽으면서 항상 느낀 점인데 테스트를 통과하면 일단 두고 넘어가도 된다는 점이다. 리팩토링에서 다시 만나면 코드를 새로 작성하거나 테스트를 새로 작성하면 된다고 계속 이야기한다.

‘관측상의 동치성’이 성립되려면 충분한 테스트를 가지고 있어야 한다. 여기에서 충분한 테스트란, 현재 가지고 있는 테스트들에 기반한 리팩토링이 추측 가능한 모든 테스트에 기반한 리팩토링과 동일한 것으로 여겨질 수 있는 상태를 말한다. (p. 292)

빨간 불에서 초록 불로 바꾸는 과정에서 관측 상의 동치성이 나타난다. 초록 불 상태에서 코드를 바꿔도 초록 불이라면 외부에서 보기에는 동일한 결과를 반환하기 때문에 문제가 없다는 것이다. 초록 불 상태로 어떤 방식으로든 빠르게 만들어내야 한다는 설명이 이 맥락에 닿아 있다.

TDD 주기(테스트/컴파일/실행/리팩토링)를 수행하기가 힘든 언어나 환경에서 작업하게 되면 단계가 커지는 경향이 있다. 각 테스트가 더 많은 부분을 포함하게 만든다. 중간 단계를 덜 거치고 리팩토링을 한다. 이렇게 하면 개발 속도가 더 빨라질까, 느려질까? (p. 315)

단기적으로는 빨라지는 것 같지만 결국엔 테스트와 거리가 먼 코드를 작성하게 된다.

패턴 복사하기 자체는 훌륭한 프로그래밍이 아니다. 이 사실은 내가 패턴에 대해 이야기 할 때면 늘 강조한다. 패턴은 언제나 반숙 상태며, 자신의 프로젝트 오븐 속에서 적응시켜야 한다. 하지만 이렇게 하기 위한 좋은 방법 중 하나는 일단 무턱대고 패턴을 복사하고 나서, 리팩토링과 테스트 우선을 섞어 사용해서 그 적응과정을 수행하는 것이다. 패턴 복사를 할 때 이렇게 하면 해당 패턴에 대해서만 집중할 수 있게 된다(한 번에 하나씩). (p. 340)

TDD와 리팩토링 과정 속에서 패턴은 자연스럽게 발견되어야 한다는 이야기와 유사하다. 패턴은 반숙이라는 표현이 와닿았다.


실천 없는 구호는 의미 없다. 이 계기로 앞으로는 TDD 하도록 노력해야겠다.

Thinking Fast and Slow

2015년 12월 2일

이상한모임에서 대림절 달력으로 크리스마스 전까지 12월 내내 하루 한 권씩 각자 올해 읽은 책을 공유합니다. 매일 공유되는 독후감을 이상한모임 대림절 달력 페이지를 통해 확인할 수 있습니다.


책을 설명하기 전에 아래 영상을 먼저 보자. 하얀 옷을 입은 사람이 공을 몇 번이나 주고 받는지 숫자를 세야 한다.

대니얼 카너먼의 책 <Thinking Fast and Slow>1을 연초에 구입했었는데 연말이 다 되어서야 끝까지 읽을 수 있었다. 이 책은 직관의 편향성을 주제로 하고 있는데 빠른 생각과 느린 생각 (책에서는 System 1과 System 2) 둘을 설명하고 이 두 가지가 어떻게 유기적으로 영향을 보이는지 다양한 실험 결과를 보여준다.

빠른 생각은 자동적으로 동작하고, 고민하지 않고, 의식하지 않고 나오는 생각을 의미한다. 느린 생각은 고민하고, 문제를 해결하거나 연산하는 등의 과정을 거쳐 나오는, 노력이 필요한 생각을 말한다. 빠른 생각은 화난 사람 얼굴을 봤을 때 화났다고 아는 것과 같이 직관적인 생각이고, 192 + 385 같은 문제를 봤을 때 결과를 생각하는 경우가 느린 생각이다. 사람은 이 두 가지 방식으로 사물과 문제를 판단한다. 그래서 이 책에서는 이 두 가지 방식이 어떻게 동작하는가 살펴보는 것을 주요 내용으로 하고 있다.

이 책은 내내 인지과학에 관한 이야기를 다루고 있다. 예를 들면, 빠른 생각이 상황에 맞게 자동으로 추측해서 생각을 채워 이야기를 완성하게 되면 다른 가능성에 대해 판단할 필요가 없어지게 된다. 그런데 이 빠른 생각에서 잘못된 판단을 내렸다면 느린 생각이 활동을 시작해 다른 대안을 찾아야 하는데 느린 생각이 다른 일로 바쁘거나 게으른 상태가 되면 그 판단이 쉽게 바뀌지 않는 것이다. 처음 어떤 인상을 갖는가에 따라서도 이런 편향적인 사고가 나타날 수 있다. 그런 이유로 직접 보지도 않고 엄청 좋아하거나 무조건 싫어하는 경향도 나타날 수 있다.

이런 다양한 사례를 줄줄이 나열하고 있어서 다소 따분하게 느껴질 수도 있다. 그래도 앞서 본 영상에서 느린 생각이 주로 동작하기 때문에 빠른 생각의 주요 역할인 다른 움직임에 대한 파악이 약화되는 것을 경험할 수 있었던 것과 같이 실제로 살펴볼 수 있는 많은 연구 사례를 다루고 있다. 책 내용을 적자면 끝도 없이 적을 수 있는데 그러면 재미 없으니 꼭 책을 보도록 하자. 막연하게 “애플 제품이 이유 없이 막 끌린다”라거나 “MS는 어찌 되었든 까야한다” 식의 직관 편향을 접해봤다면 (아니면 본인에 해당한다면) 재미있을 내용을 많이 다룬다.2

자기계발서가 아니기 때문에 무엇을 어떻게 해야한다고는 얘기하는 책이 아니다. 하지만 좁게는 자신에게 나타나는 편향성이 어떤 방식으로 동작하고 있고 내가 어떤 부분이 강한 지, 약한 지 판단해볼 수 있고, 넓게는 사람들이 어떤 방식으로 인지하는가에 대한 연구를 읽는 과정으로 더 깊게 살펴볼 수 있는 시각을 제공한다. 만약 자신이 프로그래밍이나, 디자인, 기획 등으로 사람이 사용하는 무언가를 만드는 일을 한다면 꼭 읽어봤으면 좋겠다.

  • 번역서도 있다. 대니얼 카너먼, 이진원(역), 『생각에 관한 생각』, 김영사, 2012. 
  • 직접 목격하지 않았는데도 무엇을 완전 좋아하거나 아예 싫어하는 경향을 후광 효과(Halo Effect)라고 한다.