TDD: 개와 울타리
당신은 Test Driven Development: By Example(Kent Beck)이 TDD를 소프트웨어 실무에 어떻게 적용하는지 완벽하게 보여 주는 성서적 ouvrage라고 생각했는가? 천만에! 오늘, 나는 더 나은 예제가 있다고 주장한다.
뭔 소리?
지난 토요일부터 the_house의 상시 메인테이너가 휴가 중이다. 미국 남부를 돌아다니며 플로리다의 해변에 잠복하며 지내 왔다. 비행기를 타기 전, 나에게 자기 프로젝트를 유지 보수하고 모든 게 원활하게 돌아가는지 확인해 달라고 부탁했다. 할 일이 많지 않으니 딱히 문제 될 건 없다.
적어도 나는 그렇게 생각했다!
메인테이너가 자리를 비운 때만큼 the_house가 크래시 나기 좋은 때가 또 있겠는가?
설정
이틀쯤 전 최소한 /16 대역에 걸친 구역에 가비지가 잔뜩 쌓이기 시작했는데(코로케이션된 이웃 몇몇은 그걸 "snow"라고 부른다), 그걸 치울 뚜렷한 수단이 없었다. 제대로 된 사람이라면 누구나 그러하듯, 나는 그 쓰레기의 상당 부분을 메인 메모리에서 손수 꺼내 the_house 앞의 스왑으로 옮기는 사업에 착수했고, 방해가 되지 않게 치워 cars와 pedestrians가 제대로 실행될 자리를 확보했다.
실패의 첫 징조
몇 시간 동안은 아무 문제 없었다. 그러다 이틀 전 아침, 예지력 있는 실패의 징조가 나타났다. another_house의 코로케이션된 관리자가 나에게 이메일을 보냈는데, 꽤 이상하게도 그걸 출력해서 the_house.door에 박아 놓았다(참 이상한 인간이다). 아무래도 우리 dogs 두 마리가 우리 로컬 네트워크 밖으로 탈출한 모양이었다.
여기서 "대체 dogs 두 마리로 뭘 하고 있는 거야"라고 궁금해할지도 모르겠다. 사실 the_house의 사용자들은 dogs와 어울리는 걸 좋아해서 자기네 로컬호스트의 경계(적어도 그들은 그렇게 생각했다!) 안에서 두 마리를 기른다. 온갖 상식을 거슬러 그렇게 하는데, 제대로 된 인간이라면 누구나 그런 짜증웨어가 house를 온갖 가비지로 어지럽히거나 심지어 the_house의 일부를 파괴할까 두려워 식견 있게 피하는 법이다.
그러니 자기네 the_house 인스턴스를 그렇게 위험에 빠뜨리는 건 전적으로 그들 마음이지만, dogs가 이웃 시스템으로 빠져나가 그 환경을 파괴하는 일만은 막아야 한다!
이번 특정 사건에서 그런 일은 전혀 일어나지 않았지만, 그래도 그날 아침 dogs 두 마리가 fence 방화벽을 넘었다.
처음에는 잘못된 설정이라고 생각했다. fence 주위를 살펴보다가 파라미터 때문에 생긴 무해한 누수를 발견했다. 간단한 핵으로 고쳤다. the_house.trash 어딘가에서 그 누수를 막아 줄 법해 보이는 폐자재 조각을 찾아냈다.
껌이지!
...라고 나는 속으로 생각했다!
저 사악한
dogs는 당분간 못 나갈 거야!
쳇! 잘난 척한 대가는 열두 시간 남짓 뒤에 실패 한 방으로 돌아왔다.
에러, 에러, 에러!
그러니까 그 최초의 실패는 잘못 설정된 dotfile보다 아마 조금 더 복잡했다. 그날 저녁 the_house에 다시 로그인해 보니 dogs 두 마리가 사라지고 없었다. 듣자 하니 녀석들은 또다시 달아난 모양이었다. 무슨 미친 이유에서인지 우리 /24 대역의 가장 가까운 이웃이 탈출한 dogs의 신병을 확보하기로 했다. the_house의 suppléant 메인테이너, 그러니까 내가 돌아올 때까지 맡아 두겠다는 것이었다.
이거 참, 꽤 이상한 일이었다. 내가 누수 앞에 놓아 둔 폐자재 조각이 사라졌고, 그 주변 어디에서도 찾을 수 없었다(코로케이션된 house의 kids 두 명이 fence에 대한 권한을 남용해서 dogs에게 만족이라곤 모르는 자기네 play 프로그램을 돌리려고 일부러 그 폐자재 조각을 누수에서 떼어 내 풀어 놓은 게 아닌가 싶다).
아니나 다를까 문제는 내가 생각한 것보다 컸다. 이 fence를 제대로 고쳐야 했다. 안 그러면 이 코로케이션 서버에서 the_house가 위태로워지고, 머신을 같이 쓰는 사람들을 빡치게 만들 판이었다.
이쯤 되면 인생이 나에게 뼈아픈 지혜를 뇌에 곧장 주사하고 있다고 생각할지도 모르겠다. 그런데 천만에.
내 뇌는 강철보다 단단하다
다음 날(오늘) 아침, 잠에서 깨어 샤워하러 갈 준비를 한다. 내가 씻고 녀석들을 다시 안으로 들여놓는 고작 15분 사이에 dogs가 누수를 뚫고 빠져나갈 리 없다고, 나도 모르게 무심히 믿어 버린다.
역시나 녀석들은 또 달아났다. 아주 보란 듯이 탈출해서는, 이번에는 /16 대역 이웃들을 습격하기로 했다. 꽤나 창피해하면서 어느새 나는 그 구역 구석구석에 핑을 날리며 dogs가 어디 있는지 찾고 있었다. 다행히 너무 멀리 가지는 않았고, 15분 뒤 녀석들을 집으로 데려왔다.
나는 녀석들을 우리가 crate라고 부르는 가상 네트워크 뒤에 유휴 상태로 둔다. 그러고 하루 일을 보러 나선다. 목적지로 가는 길에 the_house에 대한 버그 리포트를 작성하고 확인됨으로 표시한다.
마침내 상식이 이긴다
TL;DR
울타리에 진짜 문제가 있다는 걸 내가 깨닫기(또는 인정하기) 전에 개들은 집에서 세 번 탈출했다. 나는 내 TL;DR을 그 망할 텍스트 한가운데에 놓았는데, 이보다 더 무의미할 수 없다. 다른 데서 그걸 보고 "저건 TL;DR을 놓기에 꽤 무의미한 위치다"라고 생각했다. 이제 당신도 당했다!
참 좋은 사람이군!
이 짐승의 진짜 본성
알고 보니 이른바 snow라는 엄청난 양의 쓰레기가 압력 수준을 높여 놓은 탓에, 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가 이 상태에 이르면 즉시 테스트를 끝내는 것이 중요한데, 그러지 않으면 너무나 잔인하기 때문이다. 그럴 땐 그냥 meat에서 엔트리 하나를 dog에게 주고 the_house 안으로 다시 들여보내면 된다.
레드, 그린, 리팩토링
이제 우리에겐 실패하는 테스트가 있다. 실제로 돌려 보면 어김없이 way_too_excited_dog가 튀어나와 내 meat 배열을 먹어 치운다.
이 테스트를 너무 오래 레드로 둘 수는 없으니(고기가 모자란다!), 나는 고칠 방법을 찾아야 한다!
첫 번째 시도
나는 the_house를 한 바퀴 돌아 쇠 삽을 찾아낸다. 어쩌면 fence 둘레에 참호를 파면 내 overexcited_dog가 (구멍이 있는데도) 뚫고 나가지 못할지도!
나는 테스트를 실행한다:
레드.
우와! 이런, 아깝기도 하지!
두 번째 시도
the_house의 벽 한 면을 덮고 있던 fence에서 한 조각을 잘라 낸 다음, fence의 구멍 위에 갖다 대는 작업에 들어간다. 말하자면 패치를 붙인 셈이다.
나는 테스트를 실행한다:
레드.
뭐? 또? 과연 way_too_excited_dog는 예상치 못한 방식으로 뚫고 나갔다: 아예 fence를 뛰어넘어 버린 것이다.
참호를 파고 구멍을 패치했는데도 테스트는 여전히 빨간색이다! TDD 덕분에 이제 내 버그의 진짜 정체가 벽의 구멍만이 아니라 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는 봄 릴리스 전에 착수하기에는 너무 큰 사업이다. 전체를 처음부터 다시 지어야 할 것이다. 이번에는 진짜 제대로 된 fence를 써서 말이다. 그동안 우리는 이 핵을 감수하고 우리 테스트를 주시해야 한다 — 계속 녹색으로 지켜 내자!