샘플 – 21장

21장. 차이는 다른 결과를 만든다

네 머신에서만 코드가 작동하면, 괜찮아. 어느 플랫폼에서 작동하든 누가 신경 쓰겠어? 어차피 네겐 그게 없는데.

   “잠깐, 그것 때문에 차이가 생기지는 않을 겁니다.” 업체 사람이나 동료가 이런 불후의 명언을 말한다면, 여러분은 그들이 틀렸다고 장담할 수 있다. 무언가가 다르다면, 그 차이는 다른 결과를 만든다.

벤캣은 프로젝트에서 이 교훈을 힘들게 배웠다. 동료가 벤캣의 코드에 결함이 있다고 불평했다. 이상하게도 그 상황은 벤켓의 머신에서는 통과했던 테스트 케이스 하나와 동일한 상황이었다. 한 머신에서는 작동하나 다른 머신에서는 그렇지 않았다.

   벤캣과 동료는 다른 플랫폼에서 .NET API의 작용 상태가 달랐던 것이 원인이라는 사실을 마침내 알아냈다. 윈도우 XP와 윈도우 2003에서 말이다.(주석8) 플랫폼이 달랐고 그 차이는 다른 결과를 만들었다.

   벤캣과 동료가 우연히 문제를 발견했기에 그나마 행운이었다. 그렇지 않았다면 제품이 릴리스되고 나서야 알게 되었을지도 모른다. 이런 문제를 늦게 발견하면 매우 많은 비용이 들 수 있다. 애플리케이션을 릴리스하고 나서, 당연히 지원해야 하는 플랫폼 가운데 하나에서 제품이 작동하지 않는다는 사실을 발견했다고 상상해 보자.

   앤디가 말하길…

  하지만 내 머신에서 작동하는데……

   OS/2 시스템에서 더 나은 성능을 원하는 고객이 있었다. 그래서 과감한 개발자 가운데 한 명이 OS/2 운영체제 스케줄러를 어셈블리 언어로 처음부터 다시 작성하기로 결정했다.

   그리고 스케줄러는 정말로 작동했다. 얼마간은 말이다. 원래 개발자의 머신에서 실제로 잘 작동했지만, 개발자들은 다른 머신에서 스케줄러를 돌리지도 못했다. 심지어 개발자들은 같은 벤더로부터 동일한 하드웨어를 구매해서 같은 버전의 운영체제, 데이터베이스, 다른 툴들을 설치하는 데까지 나갔다. 그러나 운이 없었다.

   개발자들은 같은 시각에, 머신을 같은 방향에 두고, 삶은 돼지머리를 올려놓고 고사까지 지냈다고 했다.(물론 삶은 돼지머리로 고사 지낸 부분은 꾸며낸 얘기지만, 나머지는 사실이다.)

   팀은 결국에 스케줄러를 만드는 시도를 단념했다. 문서화되지 않은 운영체제의 내부를 건드리는 것은 애자일한(agile) 기술이 아니라, 망가지기 쉬운(fragile) 기술인 것 같다.

   지원하는 모든 플랫폼에서 애플리케이션을 테스트해 달라고 QA 팀에게 요청할지도 모른다. 그러나 QA 팀의 수작업 테스트가 가장 신뢰할 만한 접근 방식은 아닐 것이다. 우리에겐 훨씬 더 개발자 지향적인 접근 방식이 필요하다.

코드를 시험하는 단위테스트를 미리 작성하기 때문에, 코드를 수정하거나 리팩터링할 때마다 코드를 체크인 하기 전에 테스트 케이스를 시험한다. 따라서 여러분이 해야 할 작업은 지원하는 플랫폼이나 환경마다 테스트 케이스를 시험하는 것이다.

   다른 운영체제(MacOS, 리눅스, 윈도우 등)나 심지어 같은 운영체제의 다른 버전(윈도우 2000, 윈도우 XP, 윈도우 2003 등)에서 애플리케이션이 실행될 것을 예상한다면 모든 운영체제, 버전에서 테스트해야 한다. 다른 버전의 자바 가상 머신(Virtual Machine, VM)이나 .NET 공통 언어 런타임(Common language runtime, CLR)에서 애플리케이션이 작동하길 바라면, 마찬가지로 이곳에서도 테스트해야 한다.

 

시간을 줄이기 위해서 자동화하자 그러나 시간에 대한 압박을 이미 느끼고 있다. 다수의 플랫폼에서 테스트를 돌릴 시간을 어떻게 마련할 수 있을까? 지속적 통합(Continuous Integration)(주석 10)이 해결책이다.

좥코드를 릴리스할 수 있게 유지하라좦에서 살펴보았듯이 지속적 통합 툴은 주기적으로 버전 관리 시스템(source control system)에서 소스를 가져와서 코드를 시험한다. 테스트가 하나라도 실패하면 관련된 개발자에게 알려 준다. 이메일, 호출기, RSS 피드, 다른 창조적인 접근 방식으로 이 메시지는 전달된다.

   다수의 플랫폼을 테스트하기 위해서, 각 플랫폼에 지속적 통합 시스템을 간단히 설정한다. 여러분이나 동료 개발자가 코드를 체크인하면, 다수의 플랫폼에서 테스트는 자동으로 실행될 것이다. 코드를 체크인한 뒤 몇 분 안에 어떤 플랫폼에서 실패 메시지를 받는 것을 상상해 보자! 지속적 통합 시스템은 자원을 현명하게 사용하는 방법이다.

   빌드 머신에 사용하는 하드웨어 비용은 개발자 작업 시간에 비하면 단지 몇 시간에 불과하다. 필요하다면 VMware나 가상 PC와 같은 제품을 사용해서 운영체제의 다른 버전을 실행하고, 서버 하나에서 VM과 CLR을 실행해서 하드웨어 비용을 더욱 줄일 수 있다.

 

차이는 다른 결과를 만든다.

지속적 통합 툴을 사용해서, 지원하는 플랫폼과 환경의 조합마다 단위테스트를 실행하자. 문제가 여러분을 부르기 전에 능동적으로 문제를 찾자

 

어떻게 느껴야 하는가?

단순한 단위테스트라고 느끼겠지만 한층 더 넓은 의미가 있다. 다양한 세계를 넘나드는 단위테스트다.

균형 유지하기
  • 하드웨어는 개발자 작업 시간보다 더 저렴하다. 지원하는 플랫폼과 설정이 많다면, 사내에서 자체적으로 어떤 플랫폼과 설정을 테스트할지 능동적으로 선택해야 할 것이다.
  • 모든 플랫폼에 존재하는 버그가 스택 레이아웃의 차이, 워드 엔디안(word-endian)의 차이 때문에 드러날 수 있다. 따라서 솔라리스보다 리눅스를 사용하는 고객이 아무리 많다 하더라도 여전히 양쪽에서 테스트하는 것이 필요하다.
  • 에러 하나 때문에 메시지 융단 폭격을 받고 싶지 않을 것이다(이것은 이중 과세와 비슷하고, ‘이메일 피로증’을 일으킨다). 빌드가 실패하면 주요 빌드를 수정할 충분한 시간을 주기 위해서 주요 플랫폼/설정에서 통합 빌드의 주기를 줄이거나, 편리한 보고서 하나로 에러들을 모으자.

 

(주석 8)  .NETGothas에서 Gotcha 74번(역주 9)을 살펴보자.

(역주 9)  Gotcha는 문서대로 작동하지만 직관적이지 않은 시스템, 프로그래밍 언어의 특징을 말한다. C에서 if (a=b) code; 코드는 작동은 하지만 작성자가 의도한 것이 아니다. 작성자는 a와 b가 같을 때 code가 실행되기를 원했지만, 이 코드는 a에 b를 할당하고  code를 실행한다. 따라서 작성자의 의도를 반영하기 위햐서는 코드를 다음같이 수정해야 한다. if (a==b) code;

(주석 10) http://www.martinfowler.com/articles/continuousIntegration.html에서 마틴 파울러(Martin Fowler)의 “지속적 통합”이라는 중요한 기사를 읽어 보자.