내 생산성의 장애물

프로그래밍하다보면 부드럽게 달리다가 턱에 퍽하고 걸린다고 느낄 때가 있다.

  • 유닛 테스트(혹은 기능 테스트)를 돌렸는데 끝이 안난다거나
  • 컴파일러 메시지가 쭉 뜨고있거나 혹은 링커/코드 생성 메시지에서 끝이 안나고 있다거나
  • 빌드 서버에서 끝없이 노란 불(=빌드 중)이 들어가 있거나
  • 안티바이러스 스캐닝이 하드 디스크를 긁고 있거나[1]

여하튼 이런 일에 신경이 쓰이면 뭔가 발이 잡힌 것 같고, 집중이 퍽하고 깨진다.

개인적으로는 이거에 대해 약간의 우회책을 넣어 대처하고 있다(=성격 나빠지고 있다).

우선 빌드 속도와 테스트 케이스 실행 속도.
google test 를 써서 작성한 테스트인 경우 실행 파일 생성 시간이 바뀌면 자동으로 테스트를 실행하고 실패하면 화면 오른쪽 아래에 커다란 빨간 팝업을 뜨게 했다. 클릭하기 전에는 안 사라지는 그런 팝업임.

대충 여기 화면 오른쪽 아래에 나오는 식.[2] 반대로 모든 테스트가 성공하면 쬐끄마한 녹색 팝업을 수 초쯤 띄우고 사라지게 했다. 이전에 빨간 팝업이 있었으면 그것도 지우고…
상대적으로 좀 덜 기다리고 (문제가 생기면 알려주고) 다른 작업을 할 수 있게;

이전 포스팅에서 말했던 원래 문제(긴 테스트 시간)도 약간은 견딜만한 문제정도로 바꿨다. 다만 이 방법도 항상 통하는건 아니다. 코드 작성 -> 빌드 -> 실행 -> 보고의 한 싸이클이 너무 길면 큰 도움이 안된다. 테스트가 다 돌기도 전에 다음 싸이클의 코드 작업이 끝나고 빌드 버튼을 누르면 이미 실패. 엄한 링크 오류나 보게 된다.

그덕에 직전 포스팅에 나온 작업에 착수. 테스트 실행시간을 줄이고나니 어지간해선 — 멍청한 안티바이러스라거나가 빌드 시간을 잡아먹지 않는다면 — 견딜 수 있는 범위 내로 어떻게 어떻게 되더라.

 

빌드 서버 결과기다리는 것도 예전엔 상태 보면서 기다렸는데, 요즘은 신경 끄고 딴 일하다 실패 팝업 뜨면 그 때 보기는 방식으로 가고 있다. 문제는 내가 고친 프로젝트 말고 다른 사람 프로젝트 깨지면 그것도 신경쓰게 된다는 거지만;

 

하지만 이런 짓하면 성공적으로 전진하는 경우야 좋지만 실패했을 때의 페널티가 크다. 내가 뭘 생각하고 있었나도 돌아봐야하고, 문제를 풀고나서도 다시 원래있던 곳으로 사고를 돌려야하니…

 

여하튼 세 줄 요약

  • 작업 싸이클에 드는 시간이 짧아야 집중해서 일할 수 있다
  • 특정 단계가 너무 길다 싶으면 비동기로 바꾸고, 보고할만한 내용이 있으면 눈에 잘 띄게 알려주자(소리나 불빛 덜 방해되는 영역의 팝업)
  • 이래도 안되면[3] 하드웨어에 투자해야. 뭐 난 그게 안되서 테스트 코드를 최적화했다만(…)
  1. 물론 이 경우에는 정말 아무 것도 할 수 없기 때문에, 수단과 방법을 가리지 않고 안티 바이러스를 종료한다. 안 꺼지면 파워 버튼을 연타 []
  2. 화면 오른쪽 아래 좀 탁하게 찍힌 화면이 그것. 화면 해상도가 2048 by 1152 임을 감안하고 보실 듯 []
  3. 사실이게 제일 싼 방법 같은데 []

NDC 참관기: Server Burner: 범용 네트워크 스트레스 테스트 툴

NDC 2011 2일 차(5/30)에 있었던 하재승 군의 세션.

여기에는 하재승 군과 같은 팀에 있어서(…) 좀 더 우호적인 관점의 내용이 있다는 점을 생각하며 읽어주기 바람;

이하는 내용 요약

게임 오픈 혹은 대규모 패치 후에 서버가 폭주(?)하는 사례가 많다.

네트워크 스트레스 테스트 툴이 필요하다?

  • 예상되는 스트레스 상황 테스트 / 반복 실행
  • 미리 문제를 찾고 해결하자

Pros.

  • 쉽게 테스트 작성
  • 대량의 테스트 진행
  • 테스트 진행 중 데이터 자동 수집 / 모니터링

미리 작성된 테스트 시연

간단한 채팅 서버를 가지고,

  • 1 명 들어와서 채널에 메시지 말하고 나가기
  • 100 명 들어와서 같은 동작
  • 테스트 동안 모니터링한 결과 보여주기

잘 알려진(…) 올해의 게임 Minecraft를 가지고,

  • 여러 마리의 봇이 로그인해서,
  • 봇들은 (미리 지정한) 유저를 따라다니고, 유저가 삽질(마우스 왼쪽 클릭)하는걸 따라함(…)
  • 100 명 수준이 되어가면 서버가 심하게 랙이 걸리는 걸 볼 수 있음[1]
  • 더불어, 이 동안의 시스템 모니터링한 그래프를 보여주기

테스트 자동으로 작성하기

  • 클라이언트 -> 복수의 서버 군(로그인, 채널, 게임, 챗, 몬스터, …)의 통신을 캡쳐해서 자동으로 테스트를 작성
  • 상대적으로 쉽고 간편하게 + 버그 재현에 써먹기
  • 바로 캡쳐해서 쓸 수는 없다 -> 툴을 이용해서 쉽게
  • 미리 만든 스트레스 테스트 용 아이디 (특정 prefix + 숫자)가 필요함

툴을 이용해서 테스트 스크립트 수정

한 눈에 패킷 보기, 패킷에 들어가는 값을 특정 전역 변수랑 연동/편집, 스크립트 내보내기… 를 처리하는 GUI 편집툴 제공

Client.Send_LoginRequest(A, "test"..index(), "testpass")
local pkt = Recv(A)
if pkt.__id__ ~= Server.LoginResult then fail() end

…같은 형식의 스크립트가 최종적으로 나옴.

결과 분석

  • 가상 유저 별로 받은 패킷 순서, 전달된 패킷의 개별 값, 특정 메시지 간 시간, 특정 행동(로그인)에 걸린 시간 값 등등을 측정
  • 서버 자원 모니터링

요약

프로토콜 파일 작성

  • 캡쳐/스트레스 테스트에 이용
  • 클라이언트 — 서버 간 패킷 내용 정의 ;(Python, lua, C#, C++ 코드 생성)
  • 패킷 정의가 없으면 해당 프로젝트에 쓰는 방식을 분석하고 정의 파일 만들기
  • 구조체 기반인 경우 아예 C++ 헤더를 파싱해서 만들기도 함
  • 헤더/푸터,암호화,체크섬 등 처리 가능

UDP 통신 / HTTP 통신 / 네트워킹 모듈 기능 테스트

계획

  • 복잡한 테스트 수행 (장시간 반복하기; 현재는 쉘 스크립트로 대체)
  • 테스트 결과 분석 강화
  • 네트워크 상태 시뮬레이션 (패킷 로스, 지연 상황)

촌평.

온라인 게임으로 한정할 경우, 많은 네트워크 테스트 도구가 갖는 문제가 뭐냐하면,

  • 부적당한 추상화 수준 : TCP/IP 패킷 레벨, HTTP 메시지, …
  • 부하 테스트 시나리오를 생성할 방법
  • 부하 테스트 시나리오를 유지 관리할 방법
  • 충분한 수준의 부하를 생성할 방법
  • 복수의 서버군이 있을 때 이에 대한 추상화는 어떻게 할지

등등?

이런 문제에 대한 접근법으로, 적당한 도메인 분리 + 개별 공략은 타당한 선택지라고 판단된다. 대략 서버 버너 구조 자체가,

  • 프로토콜 데이터의 별도 표현(yaml)
  • 메시지 캡쳐 / 편집 프로그램
  • (수동) 추가적인 스크립트 편집
  • 복수의 머신에서 동시에 부하 스크립트 동시 시작/종료/통계 처리

로 쪼개져 있다. 그리고 이 중 수동으로 스크립트를 추가 수정하는 부분만 빼면 많은 경우 그다지 복잡할 게 없다.

우선 추상화 수준의 문제.

사실 게임…이라고 해도 장르 자체가 다르면 기술적인 의미에서 추상화 수준이 서로 다르다. 그래서 ‘메시지’라는 좀 저수준이지만 공통(?)일 수 있는 수준을 잡고, 여기에 대해 UDP/TCP/혹은 응용프로그램 수준 메시지로 잘 쪼개서 처리하는 접근은 적당하다고 생각한다. 대략 게임이라면 TCP, TCP 기반의 메시지 (HTTP라거나? 전용 포맷?), UDP (전용 p2p 프로토콜 류) 정도를 생각할 수 있는데, 이거에 대해서 프로토콜 데이터만 기술해주면 대략 이 수준의 추상화는 메서드 호출처럼 표현할 수 있다.[2] 메시지를 특정 형태로 기술하는 일은, 팀 내에서야 이게 공통 포맷이라(…) 시간이 0이 걸리고, 사내 다른 팀 껄로도 거의 전환 시간 없이 가능했던걸 생각하면 어찌어찌 가능은 할 듯 하다. 대략 엔지니어 하나 붙여놓고 하루 안에 적용이 가능했으니…[3]

예를 들어 인증 메시지 보내는 것은: SendAuthMessage(utf-8-string-id, bytes-credential, bytes-session-key) 수준 정도로?

다음으로 메시지 캡쳐, 편집.

일단, 부하테스트 시나리오 생성을

  • 패킷을 아예 캡쳐하고, 이걸 적당히 조작(시연 동영상을 보면 좀더 이해가..) 하거나,
  • 아예 중간에 이걸 릴레이하는 서버 (개발팀이라면 언제라도 가능한) 를 두고 모든 메시지를 가로채거나
  • 서버가 여러 대인 경우에도 이걸 적당한 스트림으로 보여주고, 일부를 병합하거나

하는 식으로 실제 메시지를 가지고 만들어내고, 이걸 lua 스크립트 수준에서 처리하면 된다는 점이…

복수 머신에서 부하 자체를 생성하는 일

머신 한 대에서 생성하는 부하에는 한계가 있다. 왠만큼 시간을 들여 짜고 + 튜닝하지 않고서야, 머신 당 유저 수는 크게 제한된다. 차라리 가짜 유저 간 상호 작용을 줄이고 머신 붙인 만큼 유저를 더 만들어 내는게 대략 맞는 방향인 거 같다.
부하자체는 테스트 스크립트를 시작하는 프로세스와, 이 테스트 스크립트를 받아 실제로 메시지를 보내는 k개의 프로세스를 써서 보내기 때문에 (물론 머신도 k개에 준하게…) 물리 머신 수만 충분하다면 원하는 수준의 부하를 만들 수 있다. 다만 밑단의 lua 스크립트는 짜야겠지만(…).

Q/A 팀에서 일일이 부하테스트를 짜는건 약간 힘들지 몰라도, 서버 프로세스를 짜는 프로그래머라면 큰 무리 없이 쓸 수 있는 수준의 도구라고 생각한다.

  1. 테스트할 때 쓴 서버는 대략 Win7 x64, RAM 4GiB, Q8600 정도 []
  2. 다만 UDP는 별도로 .dll을 짜서 python/lua 바인딩을 제공해줘야 한다… []
  3. 강연 때도 나온 얘기지만 아예 xml 기반의 프로토콜이나 압축, 별도 체크섬 같은거 붙이는건 큰 일이 아님… []

프로그래머의 일상: 테스트, C#, 스테이징, …

테스트

내가 게임 서버를 만들고 있는 모 게임의 사내 테스트가 있었다. 아직 서버에 별 기능이 안 들어가서 그런지 죽지 않고 무사히 끝났다. 스테이징의 개념이 없는 ??? / ??? 덕분에 아주 심장이 쫄깃해지는 경험을 몇 번했지만 무사히 끝났다.

하루 테스트가 끝날 때마다, log4cxx를 이용해서 디스크에 저장하는 log를 통째로 해석하는 작업도 했다. Python의 re[1] 로 일부 통계 정보에 해당하는 부분만 걸러낸 뒤, 해석해서 csv로 저장하게 했다. 소규모의 사내 테스트라 그런지 대략 수 메가 수준의 로그였고, 일반적인 데스크 탑에서 수 초안에 결과가 나온다.

스테이징 서버

테스트 기간 중간에 정말 심장이 쫄깃쫄깃해지는 경험을 했는데, 이건 외부 요인. 얼마 전에 스테이징 서버에 관한 얘기를 했는데, 이게 전혀 안 지켜지고, 테스트도 부족한 조합을 만나서 정말 괴로웠다. 모 서비스의 업데이트가 두 개 서버 군에 대해, A 군 업데이트(1시간 소요), 그 직후에 B 군 업데이트(역시 1시간 소요) 하는 형식의 작업을 하더라(이 작업은 야밤에 진행). 그리고 다음날 아침에 출근했더니 해당 서비스 연결부가 동작을 안 함… 그리고 그 날과 다음 날 동안 해당 서비스 연결부가 불안한 동작을 해서 편두통이 매우 심해졌다. 제발 스테이징 서버 두고 테스트 좀 하라고 + 딴 데서 쓰는 서버 업데이트는 안정화 되고 해줬으면 하는 바램이… Orz

생각해보니 내년 이 때쯤? 보다 이전에는 또 그거 쓸 일은 없으니 신경 끄고 있을까.

Windows Server 2008 R2의 .net framework

Windows Server 2008 R2가 깔린 빌드 머신 4대가 드디어 입고(?)되었다. 필요한 툴들을 설치하는데, .net framework이 말썽. 기본적으로 설치된 툴은 2.0(…)이더라고;; 내가 쓰려는 툴은 내가 .net framework 4.0(…)을 가지고 만든 거라 Orz

근데 VisualStudio 2008 설치하면서 3.5가 설치되게 하려 했더니, 인스톨러가 오류를 내면서 죽더라. 서버 기능 추가 메뉴로 설치하는 것만 되더군;;;

  1. regular expression package []

프로그래머의 일상: 다시 ActionScript 삽질

사실 이건 ActionScript 2.0 이라서 하는 삽질이지만.

예전에 했던 작업을 동면에서 깨워, 다시 진행해야하는 상황이 되었다(이미 2.x주 전의 얘기). 그래서 요즘 C++ + ActionScript(2.0; Flash 8) 으로 작업하고 있는데, 나는 플래시 저작툴 라이센스가 하나도 없어서 MotionTwin 의 툴셋을 쓰고 있다. 덤으로 ASUnit 2.5 도 다시 끄집어 내서 사용 중.

예전에 Flex 로 액션스크립트 테스트를 한다는 글을 읽고, 다음에 이 쪽을 또하면 이용해보자란 생각을 했었다. 그렇지만 여전히 ActionScript 2.0 (≠Flex SDK)라서, 이걸 사용하는건 불가능.

일단 현재 테스트하는 환경/상태는 다음과 같다.

  • 수 개의 .as 파일에 대해, 이 파일 중 변경 사항이 있으면[1] , msbuild 를 실행시켜서 .swf 를 빌드해낸다.
  • msbuild는 .swf를 빌드하고, 이걸 특정 디렉토리로 복사한다.
  • python으로 작성한 웹 서버는 특정 디렉토리에 있는 파일들을 서비스하게 동작.
  • 웹 페이지 하나에 .swf 들을 embed 로 연결하고, 이 페이지에 http meta tag 중 refresh 태그를 이용해서 30초마다 다시 실행

그리고 조그맣게 띄워놓은 웹 브라우져로 실행 결과를 지켜보고 있다. 그러니까 아직 CI 툴에 붙일 수 없는 상태(=커맨드라인 실행이 불가능한 수준).

이에 대한 앞으로의 개선책(?)은 아래 정도. 처음 두 개는 @oscarplex 님의 조언을 듣고 정리한 것.

  • 테스트 결과를 받는 (python) 웹 서버를 하나 짜고, 이 서버가 정해진 형태로 유닛테스트 이름과, 결과를 받아서 (xml) 파일로 내보내는 방법
  • (웹브라우져에서) 출력하는 부분을 API hooking해서 내보내는 방법
  • 웹브라우져에서 드래그&복사는 되는걸로 봐서, 이걸 어떻게 자동화해버리는 방법

사실 1이 간단해보이는데(AS 2.0 통신용의 각종 모듈은 다 짜놨으니), 이거 다음주에 전달해주고나면 끝날지도 모르는데?[2]

고향에 놋북 들고왔으면 후다닥 짜버리면 땡인데 안들고왔으니 Orz.

ps. 사실 시간이 한 4주쯤 여유가 있으면 Python으로 ActionScript 코드 토해내게 짜버리고 싶다(…).

  1. 사실 파일 변경 시간(mtime)이 정해진 몇 개의 .swf 보다 최신인 파일이 있다면 []
  2. 하지만 난 2 년 쯤 전에도 이 생각을 했지 []

리뷰: Coders at Work

Peter Seibel 의 coders at work 완독. 현업에 있는 / 있었던 유명 프로그래머 – 이쪽 업계에선 여러가지 의미로 슈퍼 스타 – 들의 인터뷰 15개를 엮어놓은 책이다.

인터뷰한 사람들의 면면을 살펴보면, 학계에 있던 사람부터 시작해서 (Knuth 교수라거나 haskell 의 Simon. Peyton-Jones, Guy Steel 등등), 업계에서 처음부터 지금까지 일해온 사람들(Live Journal을 만든 Fitzpatrick, 넷스케이프의 Zawanski, JSON의 Crockford, JavaScript의 Eich, Java Collections 의 저자 Bloch 등등) 혹은 양쪽 다에서 업적을 쌓은 사람들(AI 책의 저자이면서 구글의 리서치 디렉터인 Peter Norvig, UNIX와 C의 Ken Tompson 등등) 을 포함한다.

사실 이렇게 넓은 스펙트럼을 다루고 있어서인지, 프로그래밍에 관한 관점, 프로그래밍에 요구하는 관점, 어떤 언어가 필요한지, 자기 자신을 과학자/공학자/장인(craftsman)/예술가 중 무엇으로 보는지, 얼마나 수학을 해야하는지에 대한 생각이 각자 다르다. Literate Programming에 관해서는 대부분 괜찮지만 난 Knuth 수준으로 안해봤다가 대세였음…

일단 각 사람들이 다루는 분야가 프로그래밍 언어(C, Haskell, Smalltalk, Erlang, JavaScript, Fortress) 나 그에 밀접한 영역(Compiler, Interperter, EMACS(?), Java Collections)이거나, 아니면 현재 우리가 사용하는 시스템의 밑단(UNIX, ARPANET) 인 경우가 대부분이다. 어떤 의미로는 우리 시대의 – 적어도 컴퓨터 공학/전산학 분야에서는 – rocket science 인 듯…

프로그래머가 가져야하는 자질로,

  • 간단함, 명료함에 대한 추구
  • 자세한 것을 다룰 것
  • 추상화 해서 생각할 것
  • 수학적인 지식(분석적인 면 말고, 구성적(synthetic)인 면에서)
  • 새로운 분야에 대해 호기심을 가질 것

등을 말하고 있다.

다만 실제로 프로그래밍에 임하는 자세에는, “실제로 코드가 동작하고 이를 유지한다” 쪽에 치중하는 계열이 있고, “(API 적인) 명료한 인터페이스를 생각하고, 이를 잘 지지할 테스트를 만든다” 라는 사람들도 있었다. 그리고 정말 극단적으로는, 모든 코드를 종이 위에 작성하고 이걸 만들었더니 거의 버그가 없었다는 Knuth의 TeX도 있다. 허허허(…)

그리고 자기가 일하는 형태에 따라 자기가 과학자 혹은 공학자로 생각하는 경우가 많았고, 대부분 장인과 예술가 적인 면을 가지고 있다고 말했다. 많은 사람들이 C++ 의 거대해진 면에 대해서 비판적이었고, C/C++/Java 그리고 묵시적인 면에서는 python 등도 포함해서 포인터 기반의 연산을 가지고 있는 자체에 대해서 불만을 표시하기도 했다 – 그리고 이 인터뷰 내용을 다른 인터뷰 당하는 사람한테 말하고 반론이 나오기도 해서 꽤나 흥미로웠다(…).

인상적인 구절이 너무 많아서 – 예전에 Peter Norvig이 말한건 간단히 포스팅 했다 – 인용 + 인상 정리로 한 달은 우려먹을 수 있을 것 같지만 이건 이 책 읽을 이들의 즐거움을 위해서 패스하고, 내 맘에 든(…) 구절을 몇 개 정리해보겠다.

Zawanski: I was kind of freaked outwhen I realized that there are people graduating with CS degrees who’d never written C.

Joel Spolsky 가 말하는 거랑 비슷한 의미.

Fitzpatrick: The thing that annoys me about Java is that everyone has such a strong aversion to JNI stuff. Sometimes a library is in C++. The Python people – in the outside community and inside Google – don’t care. They’re like, “Oh, we’ll, SWIG-wrap-it.” They get on their way and they’re happy. Python gets support for something right away if it’s in C++ because they’re not religious about what the source language is.

Java people are like, “Must be pure Java. We cannot use JNI because then if the JVM crashes, we don’t know why.” The problem with that is you end up writing everythin twice, once for C++ and Python and all the other languages, and the once for Java. So if they could come up with a good embedding story or get over this fear of JNI, then I wouldn’t mind it.

Java를 쓰는 사람들이 “그냥 SWIG 래핑해서 쓰지”하는 Python 계열의 사람들과 달리, “JNI가 아니라서 크래시 나면 어딘지 알수 없잖아”라고 해서 라이브러리를 두번 작성한다고 불평. 다만 Java가 더 좋은 임베딩 방법을 찾거나, JNI 문제에 덜 신경쓰게 되면 좋겠단다… 이래서 내가 C++/Python 패러다임에 살고 있는지도.

Bloch: Even today, writing unit tests for concurrency utilities is an art form.

Mutex 버그로 고생했던 기억을 회상하면서, “병행성을 다루는 도구의 유닛 테스트를 작성하는건 아직도 예술의 영역이다”라고 말하고 있다 ㅠㅠ. 회사에서 내가짜는 유닛 테스트 중에 병행성 테스트는 없다(…). 그냥 부하 잔뜩 걸어서 테스트해보는 정도?-0-

Erlang 의 저자인 Joe Armstrong은 코드 재사용성의 문제가 OOP에 있다고 생각한다.

I think the lack of reusability comes in OO langueages, not in functional languages. Because the problem with OO languages is the’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

코드 재사용성을 (어느정도는) 강조하는 OOP지만, 실제적으론 묵시적으로 들고다니는 환경 때문에 너무 많은 의존성을 낳아 오히려 재사용이 안되고 함수형 언어에는 그런 문제가 없다는 것. 덤으로, 난 erlang을 함수형 언어로만 생각했는데, Armstrong 인터뷰에서 가장 많은 영향을 준 언어는 prolog 라고 말한다. PL 시간에 쌩고생 시킨 그 언어가 Orz

적절히 인용할 부분을 찾진 못하겠지만(…), SSA (컴파일러 이론의 static single assignment) 를 생각해내기도하고, 첫번째 튜링 어워드를 받은 여성인 Fran Allen은 “C의 성공 때문에 컴파일러가 최적화할 여지가 없거나 적은 언어들이 주류 언어가 되어버렸다”라고 비판했다. 즉, 좀 더 고수준의 언어를 가지고 만들어야 컴퓨터 공학이 발전할텐데, 시스템 프로그래밍 언어였던 C가 크나큰 성공을 거둬서 앞으로 갈 길이 너무 멀고, 발전이 너무 더디다라고 말한다.

이건 Python 도 저수준 언어의 기능 – 명시적인 메모리 공간에 대한 모델 – 이 있기에 겪을 수 있는 문제가 있는걸 생각하면, 약간은 수긍이 가는 듯도…

여기에 대해 ARPANET의 초기 구현을 작성한 Bernie Cossell은, 어셈블리어 프로그래밍을 하던 사람들에게 C의 표현력은 신선한 공기와 같아서 어쩔 수 없었고, 그 표현력과 저수준 지원 때문에 C는 다른 모든 언어 – 심지어 DoD가 우리와 계약하려면 Ada를 써라! 라고 했는데도 C가 더더욱 번성해버릴 정도로 – 보다 더 널리 쓰였다고 변호한다.

여튼 더 인용하고 싶은 문장은 매우 많지만, Bloch의 인터뷰를 인용해서, 내가 이 직업에 있는 이유를 설명해놓고 리뷰를 끝낸다.

What we’re doing is an aesthetic pursuit. It involves craftsmanship as well as mathematics and it involves people skills and prose skills – all of these things that we don’t necessarily thik of as engineering but without which I don’t hink you’ll ever be a really good engineer. So I thik it’s just something that we have to remind ourselves of. But I think it’s one of the most fun jobs on the planet. I think we’re really lucky to have grown up at the time that we did when these skills led to these jobs. I don’t know what we would have been doing a few generations back.

우리가 하고 있는 일은 미적인 추구다. 우리가 하는 일은 장인 정신 뿐만 아니라 수학과 사회적인 기술과, 글 쓰는 기술을 모두 필요로 한다. 이 모든게 공학에 꼭 필요한 거라고 생각되진 않지만, 이것 없이는 정말 뛰어난 공학자가 될 수 없다. 그래서 난 이것들이 우리가 상기해야할 무언가라고 생각한다. 그렇지만 난 이 일이 세상에서 가장 재밌는 일 중 하나라고 생각한다. 우리가 이 기술을 가지고 이 직업을 갖게만든 시대에 태어난걸 행운이라 생각한다. 만약 수 세대 전에 태어났다면 난 내가 뭘 하고 있을지 상상도 못하겠다.

컴퓨터 공학을 전공하고, “뭔가를 만들어보고 싶다”는 열망이 있다면 꼭 한 번 읽어보라고 권하고 싶은 책이다. 다만 다루는 내용으로 볼 때 이거 한국어판이 나올 수 있을까?-_-;

라이브러리를 사용하는 프로젝트를 테스트할 때의 seam

Working Effectively With Legacy Code에서 말하는 seam이란 개념이 있다.

A seam is a place where you can alter behavior in your program without editing in that place

프로그램에서 해당 지점을 수정하지 않고도 동작을 바꿀 수 있는 지점.

흔히 API가 정의되는 지점인 library 경계에서 seam을 사용하려니까 link seam이 참 편하다는걸 최근 몸으로 느꼈다. 요즘 작성하는 프로그램에서, 팀 내에서 작성한 몇 몇 라이브러리를 사용한다. 근데 이걸 test-harness 밑에서 돌리려니 mock 객체를 만들어야겠고, 그러다보니 적당한 seam을 찾아야하는데, 결국 도달하는 곳은 항상(…) link seam 이었다.

일단 link seam 자체를 설명하자면, 말그대로 link 시간에 이뤄지는 symbol resolution을 속여(이용해?) 테스트 프레임웍 밑에 있을 때엔 mock 객체/함수가 호출되게 하는 것. C/C++ 이라면 말 그대로 link 할 때 전달하는 object file을 바꾸면되고, Java/C# 같은 류라면 classpath나 dynamic linking을 해서 바꾸면 될 것이다. 여튼 요는,

  • Library의 API를 확인하고,
  • 이에 대해 똑같은 linkage를 같는 fake object(mock object; 이 문맥에서만 말하자면 mock-library 정도) 를 작성하고,
  • 실제 바이너리는 실제 library binary를 링크하고,
  • 테스트 프레임웍엔 mock object를 링크

하면 된다 정도?

그리고 이걸 많이 쓰게된 이유를 꼽자면, 굳이 virtual function이 아니더라도 쉽게 그 동작을 바꿀 수 있어서? 즉, C++의 모든 클래스 멤버 함수들은 기본적으로 non-virtual 이다보니 아무래도 이 link seam을 애용하게 되는듯. 아니었다면 적당한 factory + object seam을 사용했을지도 모르지만, 테스트 환경에서 동적으로 실제 라이브러리를 선택할 일도 없고하니 아무래도 link seam을 많이 쓰게 된다.

사실 지금 팀에서 쓰는 서버 프레임웍의 제일 밑단은 테스트할 때마다 링크해야하니 아예 저런 link seam을 위한 mock object를 만들어놓은 셈이지만; 여튼 비침습적인(non-intrusive) 접근 자체는 내 취향인듯(…). 그래서인지 최근 작업에 쓴 각 라이브러리(래봐야 4개?)는 다 이런 형태로 mock 을 구현해놨다.

그래서인지, 대부분의 프로젝트(Visual Studio 쪽 용어로는 solution) 구성이,

  • Library A – seam 을 찾아 쪼개지 않아도 되는 것들
  • Library B – seam을 찾아 쪼개야하는 것들.
  • Mock library B – library B의 mock 구현
  • 필요한 라이브러리들을 링크해서 사용할 프로그램 모듈
  • Test-Runner
  • Executable – 프로그램 모듈을 동작시키는 드라이버(?)

로 이뤄지게 되었다. 실제로 executable project 안에는  프로그램 모듈을 띄우기 위한 최소한의 정보만 들어가게 작성하게 되고, Test-runner 도 비슷하게 구성되서 프로그램 모듈을 테스트하게 작성하게 된다. 그러니까 실제로 프로그래머가 작성하는건 4~6번째?

내가 사랑해마지않는(…) python 처럼 모듈 단위에서 main 을 만들 수 있다면 좋겠지만, 그럴 순 없으니 이런 구조가 나오는건 어쩔 수 없나 싶기도하다. 하지만 다른 좋은 방법이 있다면 누가 좀 힌트라도 Orz

Peter Norvig 이 생각하는 TDD

최근 출퇴근 길에 “Coders at Work”의 eBook을 읽고 있다. 거의 내 맘대로 순서를 정해서 보고있는데, 오늘 출근 길에 읽은 Peter Norvig[1] 의 인터뷰 중에, 어떻게 프로그램을 짜는게 좋은가에 관한게 나온다.

이 이하의 인용은 Norvig 이 인터뷰한 내용. 모든 저작권은 Seibel 및 출판사에 있다.

Norvig: I thik one of the most important things is being able to keep everythin in your head at once. If you can do that you have a much better chance of being successful. That makes a small program easier. For a bigger problem, you need extra tools to be able to handle that. It’s also import to know what you’re doing. When I wrote my Sudoku solver, some bloggers commented on that. They said, “Look at the contrast – here’s Norvig’s Sudoku thing and then there’s this other guy, whose name I’ve fogottent, one of these test-driven design gurus. He starts off and he says, “Well, I’m going to do Sudoku and I’m going to have this class and first thing I’m going to do is write a bunch of tests.” But then he never got anywhere. Hehad five different blog posts and in each on he wrote a little bit more and wrote lots of tests but he never got anything working because he didn’t know how to solve problem.

한 TDD 구루와 자신이 작성한 수도쿠 솔버를 가지고 벌어진 논쟁에 대한 언급.[2] 피터 노빅이 보기에 가장 중요한 것은 모든 것을 한 번에 머릿속에서 굴리는 것, 그리고 자신이 뭘 하고 있는지를 아는 것. 그래서 TDD 가 도구가 아니라 목적이 되면 – 즉 테스트에 너무 집착하면 – 정작 해야하는 일, 문제 풀이에 집중하지 않게 되고 원하는 결과를 얻지 못할 수도 있다는 것.

물론 TDD 자체에 대한 반감은 아니라고 언급한다.

Then bloggers were arguing back and forth about what this means. I don’t think it means much of anything – I think test-driven design is great. I do that a lot more than I used to do. But you can test all you want and if you don’t know how to approach the problem, you’re not going to get a solution.

내 생각에도, 모든 소프트웨어 공학툴이 어떤 은탄환이 아니듯, TDD 역시 하나의 도구일 뿐이다. 도구가 강력하다고 모든 문제를 해결해주는 것이 아니니, 결국 우리가 해야하는 일은 “문제를 풀 수 있도록 자신을 갈고 닦는 일도 병행”하는 수 밖에 없다. 즉, 좋은 도구니까 잘 쓰면 좋을게다. 여기에 집착만 하지 않으면…

PS. Coders at work를 읽고나서 나랑 비슷한 글을 쓴 사람들이 꽤 있다.

  1. 피터 노빅은 현재 Google의 연구 디렉터(Director of Research)다. []
  2. 이 논쟁글은 Learning from Sudoku solver, How to not solve a sudokuSolving Sudoku with TDD 에 있다. 여기서 피터 노빅이 이름을 기억하지 못한다는 TDD 구르는 론 제프리스(Ron Jeffries)로, XP의 창안자 중 한 명이며, XP installed의 저자다. []

내가 원하는 test-runner 의 조건

주말에 작성한 google test 용 GUI runner를 오늘까지 4일간 회사에서 사용해봤다. 개밥먹기(…)답게 몇 가지 문제점을 파악할 수 있었고, 내가 원하는 것들이 뭔지도 좀 더 알 수 있게 되었다. 일단  적당히 적어보자면,

Test 가 변화하면 자동으로 샐행되야 한다. 일일이 실행하는건 참 귀찮은 일이다(…). 기왕이면 JUnitMax 처럼 좀 더 지능적으로 실행 순서까지 골라주면야 좋겠지만..

Test filtering이 되어야 한다. 아주 간단한 형태로 실행할 테스트의 부분집합을 고를 수 있어야 한다. 이건 사실 PyUnit gui runner의 “Enter test name” 의 입력창 값을 가져다가 gtest_filter 에만 넘겨줘도 될듯함. 적당히 집어넣고 돌려본걸론 잘 돌긴하네…

Pre-run hook이 있어야 한다. Unit-test 만 있는게 아니라 functional-test 가 거의 존재해서, 프로그램 코드에서 초기화하기 힘들거나/번잡한 작업을 수행하는 일부 스크립트를 테스트 케이스 실행 전에 돌릴 수 있으면 좋겠다. MSBuild 로 전체 테스트 돌릴 때는 테스트 DB를 리셋하거나 하는 일을 하는데, 지금은 그렇지 못해서 깨지는 테스트들이 있다. Orz

실행 시간의 역순으로 실행하는 기능이 있으면 좋겠다. 일부 테스트는 수행 시간이 꽤 길다. 수 초 수준? 짧은 테스트를 먼저 실행하면 좋을 것 같다. 이건 희망사항?

개밥먹기였지만 맘에 드는걸 쓰자면, 수 초(사용할 때는 10초로 했음) 마다 실행 파일 군을 뒤져서 테스트 결과물(XML 출력물)보다 실행 파일이 새 것이면 실행하게 해놨더니, 내가 빌드할 때마다 적절히 실행되는 것은 맘에 들더라.

사실 이것 때문에 몇 가지 python exception 을 처리해야하긴 했지만…[1]  여튼 나름대로 시간대비 잘 써먹는 툴이 되었다. 다만 지금 큰 문제 중 하나가 abort() 같은 걸로 죽을 때 status code 처리가 잘 안되고 있는데 Orz. 이건 최종적으로 test-suite 단위로 실행하지 말고 test-case 단위로 실행하게 해서 fail-fast 하고 좀 더 specific하게 만들어놔야 할듯 Orz

그렇지만 이미 만들어진 GUI runner가 있다. 그냥 VisualStudio 에 붙여서 쓰는 용도라면, 쑥갓 님의 VUTPP[2] 를 쓰는게 더 나은 듯 하다. 다만 내가 원한건 좀 더 비침습적이고, 초기에 설정이 없는걸 원해서여서, 그냥저냥 만들어놓곤 요긴하게 쓰긴하고  있다. 일단 이번 주말까지 – 혹은 그 다음 주말까지 – 좀 더 수정하고, 공개…해야겠다.

  1. 맨처음 겪은 예외는 이렇다. VisualStudio가 실행 파일을 만들려고 링크하는 단계에서는 해당 결과물을 exclusive-lock을 걸어 버린다. 그래서 mtime 을 얻어오는 gui-runner가 맛이 가더라(…). 그래서 1초씩 쉬면서 계속 폴링하게 했다… []
  2. 처음에는 UnitTest++ 용 애드인이어서 이름이 저렇다. 현재는 UnitTest++, google test, boost::test 등 몇 가지 C++ testframework을 지원하고 있다. []