TDD: 개와 울타리
Test Driven Development: By Example(Kent Beck)이 TDD를 소프트웨어 실무에 어떻게 적용하는지 완벽하게 보여 주는 바이블 같은 역작이라고 생각했나? 그렇다면 오산이다! 오늘 나는 더 나은 예제가 있다고 주장한다.
무슨 소리냐고?
지난 토요일부터 the_house의 평소 메인테이너가 휴가로 자리를 비우고 있다. 지금은 미국 남부를 돌아다니며 Florida 해변을 어슬렁거리고 있다. 비행기를 타기 전에 그녀는 나에게 자기 프로젝트를 관리해 주고 모든 게 잘 돌아가는지 챙겨 달라고 부탁했다. 할 일이 별로 없으니 딱히 문제 될 것도 없었다.
그런 줄 알았다!
메인테이너가 자리를 비운 때만큼 the_house가 크래시 나기 좋은 때가 또 있을까?
배경
이틀쯤 전, 적어도 /16 블록 전체에 가비지가 잔뜩 쌓이기 시작했는데(같은 서버에 입주한 이웃들 중에는 이걸 "눈"이라고 부르는 사람도 있다), 이걸 치울 뚜렷한 방법이 없었다. 점잖은 사람이라면 누구나 그러듯, 나는 그 쓰레기 상당량을 메인 메모리에서 빼내 the_house 앞의 스왑으로 손수 옮기는 대업에 착수했다. 그렇게 길을 치워 cars와 pedestrians가 제대로 실행될 공간을 확보했다.
장애의 첫 징후
몇 시간 동안은 다 좋았다. 그러다 이틀 전 아침, 장애를 예고하는 징조가 나타났다. 같은 서버에 입주한 another_house의 관리자가 나에게 이메일을 보냈는데, 무척 희한하게도 그걸 출력해서 the_house.door에 붙여 놓았다(참 별난 사람이다). 보아하니 우리 dogs 두 마리가 로컬 네트워크 밖으로 탈출한 모양이었다.
여기서 "도대체 왜 dogs를 두 마리나 키우는 거야" 하고 궁금할지도 모르겠다. 실은 the_house의 사용자들이 dogs와 함께 지내는 걸 좋아해서, 자기네 localhost 경계 안에 두 마리를 가둬 두고 키운다(본인들은 그런 줄 알았다!). 이건 상식에 완전히 어긋나는 일이다. 점잖은 사람이라면 자기 house가 온갖 쓰레기로 어질러지거나 the_house의 일부가 망가지는 꼴을 보기 싫어서라도 이런 민폐웨어는 알아서 피한다.
그러니 자기네 the_house 인스턴스를 그렇게 위험에 빠뜨리는 건 전적으로 그들의 권리지만, dogs가 이웃 시스템으로 나가지 못하게(그래서 그쪽 환경을 망가뜨리지 못하게) 단속할 책임은 그들에게 있다!
이번에는 이웃 환경이 망가지는 일까지는 없었지만, 그래도 그날 아침 dogs 두 마리가 fence 방화벽을 넘었다.
처음에는 설정 오류인 줄 알았다. 나는 fence를 둘러보다가 파라미터에 대수롭지 않은 구멍이 하나 난 걸 발견했다. 간단한 땜질로 그걸 고쳤다. the_house.trash 어딘가에서 구멍을 막을 수 있을 것 같은 폐품 조각 하나를 찾아낸 것이다.
식은 죽 먹기지!
...라고 나는 속으로 생각했다!
고약한
dogs녀석들, 이제 한동안은 못 나간다!
하! 겸손하지 못했던 대가로 실패를 톡톡히 맛보기까지 12시간 남짓밖에 걸리지 않았다.
에러, 에러, 에러!
처음 장애는 dotfile 설정 오류보다 조금 더 복잡한 문제였던 모양이었다. 그날 저녁 the_house에 다시 로그인했을 때, dogs 두 마리가 보이지 않았다. 녀석들이 또 달아난 모양이었다. 무슨 바람이 불었는지, 우리 /24 블록에서 가장 가까운 이웃이 the_house의 대리 메인테이너(그러니까 나)가 돌아올 때까지 탈출한 dogs를 맡아 두기로 했다.
허, 이것 참 이상했다. 구멍 앞에 대 놓았던 폐품 조각은 사라졌고, 주변 어디에도 없었다(내 짐작으로는 같은 서버에 입주한 house의 kids 두 명이 fence 권한을 남용해 땜질 조각을 일부러 떼어 낸 것 같다. dogs를 상대로 지칠 줄 모르는 play 프로그램을 돌리고 싶었던 거겠지).
정말로 문제는 생각보다 컸다. 이 fence를 고치지 않으면 공유 서버에서 the_house의 입지가 위태로워지고, 같이 쓰는 이웃들의 원성을 살 수도 있었다.
이쯤 되면 이 머리에도 교훈이 뼈저리게 박혔으리라 생각할지도 모르겠다. 그렇다면 틀렸다.
강철보다 단단한 내 돌머리
다음 날(그러니까 오늘) 아침, 나는 일어나서 샤워하러 가려던 참이었다. 씻고 나와서 녀석들을 다시 들여놓기까지 고작 15분이니, 그사이에 dogs가 구멍으로 빠져나가지는 못할 거라고 안이하게 믿었다.
그리고 역시나, 녀석들은 또 달아났다. 보란 듯이 탈출해서 이번에는 /16 블록 이웃들을 습격하기로 했다. 나는 꽤 창피해하며 동네 구석구석에 핑을 날리면서 dogs가 어디 있는지 찾아다녔다. 다행히 녀석들은 아주 멀리 가지는 않아서, 15분 뒤에 집으로 데려왔다.
나는 녀석들을 우리가 crate라고 부르는 가상 네트워크 뒤에 대기시켜 두고 집을 나섰다. 목적지로 가는 길에 the_house에 버그 리포트를 올리고 상태를 '확인됨'으로 바꿨다.
마침내 상식이 이기다
TL;DR:
내가 울타리에 진짜 문제가 있다는 걸 깨닫기(혹은 받아들이기)까지 개들은 세 번이나 집을 탈출했다. 그리고 나는 내 TL;DR을 이 빌어먹을 글 한가운데에 넣었는데, 이보다 더 쓸모없을 수가 없다. 다른 데서 이런 걸 보고 "TL;DR을 넣기엔 참 쓸데없는 위치네" 하고 생각한 적이 있다. 자, 이제 여러분도 똑같이 당했다!
참 친절하기도 하지!
문제의 진짜 정체
알고 보니 소위 snow라는 엄청난 양의 쓰레기가 fence에 가해지는 압력을 높였고, 그래서 dogs가 넘어가겠다고 요청을 보내기만 하면 fence가 쉽게 뚫렸다. 평소라면 fence는 dogs를 막아 낼 만큼 리소스가 충분하다. 하지만 늘어난 snow 때문에 dogs가 넘어가는 진입 장벽이 낮아졌다. 게다가 fence에 대놓고 뚫린 그 성가신 구멍도 도움이 안 됐다.
이번에는 이 버그를 확실히 고쳐야 했다. 어떻게 하면 될까? 뭔가를 고쳐 본 사람이라면 누구나 말하겠지만, 첫 단계는 버그를 재현하는 테스트 케이스를 작성하는 것이다.
우리 경우에 필요한 건 다음과 같다:
dog인스턴스 하나, 가급적이면 꾀 많고, 의욕 넘치고, 과잉 행동하는 개. 이걸overexcited_dog로 캐스팅한다.

- 부실한
fence하나, 가급적 설정이 잘못된 것(그러니까 "구멍"이 난 것).
테스트
우리 fence의 결함을 드러내려면 어떤 테스트를 쓰면 될까? 아주 간단하고, 세 단계면 된다:
overexcited_dog를 데려다fence뒤에 둔다.meat배열을 들고fence반대편으로 간다.overexcited_dog는 배열에meat이 있다는 걸 알고 있어야 한다.overexcited_dog가way_too_excited_dog로 업캐스팅될 때까지meat배열을 흔든다.
way_too_excited_dog가 fence를 넘어가서 meat 배열을 먹어 치우면 테스트는 실패다. way_too_excited_dog가 넘어가지 못하면 whining_dog로 다운캐스팅되는데, 패닉 직전의 절망 상태를 보면 알아볼 수 있다. dog가 이 상태에 이르면 테스트를 즉시 끝내는 게 중요하다. 그러지 않으면 너무 잔인하기 때문이다. 그러려면 dog에게 meat의 원소 하나를 주고 the_house로 다시 들여보내면 된다. 그러면 만사가 평안하리라.
레드, 그린, 리팩터
이제 실패하는 테스트가 생겼다. 실제로 돌려 볼 때마다 어김없이 way_too_excited_dog가 나왔고, 녀석은 내 meat 배열을 먹어 치웠다.
이 테스트를 너무 오래 레드로 둘 수는 없었으니(고기가 넉넉하지 않았다!) 해결책을 찾아야 했다!
첫 번째 시도
나는 the_house를 한 바퀴 돌다가 금속 삽을 하나 발견했다. fence 둘레에 도랑을 파면, 구멍이 있더라도 내 overexcited_dog가 빠져나가지 못할지도 몰랐다!
테스트를 돌렸다:
레드.
이런! 아쉽기도 하지!
두 번째 시도
the_house 벽 한쪽을 따라 둘러쳐 있던 fence의 한 토막을 잘라 내서, 그걸 fence의 구멍에 덧대 패치했다.
테스트를 돌렸다:
레드.
뭐? 또? 알고 보니 way_too_excited_dog는 예상치 못한 방법으로 넘어왔다. 아예 fence를 뛰어넘어 버린 것이다.
도랑을 파고 구멍을 패치했는데도 테스트는 여전히 레드였다! TDD 덕분에, 이제 내 버그의 진짜 정체가 fence의 구멍만이 아니라 fence의 구조 자체라는 걸 알 수 있었다!
다행히도, 위에서 설계한 테스트는 이 시나리오에 딱 맞았다(덕분에 상황이 아주 분명하게 드러났다).
나는 다시 도구를 챙겨 fence를 한 번 더 살펴봤다. way_too_excited_dog는 휘어지는 fence를 타고 올라 넘어간 것이었다. over_excitement가 충분하면, dog는 사실상 fence를 스프링처럼 써서 몸을 더 높이 띄우고, 맛있는 meat을 향해 뛰어오른다. 나는 the_house.basement에서 널빤지 하나를 들고 나가 fence의 가장 낮은 부분에 댔다.
닭장 철망의 철사를 팽팽하게 당겨 널빤지에 고정했다(아 맞다, 이게 닭장 철망 울타리라는 걸 말 안 했네, 하!). 그리고 fence의 나머지 절반에도 주변에서 찾은 나무 막대기로 똑같이 했더니, fence를 똑바로 세울 수 있었다.
그리고 테스트를 다시 한번 돌렸다.
그린!
overexcited_dog는 금세 whining_dog로 바뀌었다. 이 (닭장 철망) fence가 녀석의 꾀와 의욕과 과잉 행동을 이겨 냈다!
리팩터!
자, 지금 fence의 상태는 이렇다:

내가 보기엔 꽤 부실한 울타리다. the_house.fence에 대한 테스트는 그린이지만, 주변에는 정말 흉한 땜질이 남아 있다. 코드베이스를 이 상태로 둘 수는 없다!
안타깝게도 fence를 제대로 손보는 건 Spring 릴리스 전에 하기에는 너무 큰 작업이다. 전체를 처음부터 다시 만들어야 한다. 이번에는 진짜 제대로 된 fence로. 그때까지는 이 땜질로 버티면서 테스트를 잘 지켜봐야 한다 - 계속 그린을 유지하도록!