티스토리 뷰
1. 실용주의 철학
실용주의 프로그래머는 태도와 스타일 그리고 문제와 해법에 접근하는 철학에 차이가 있다. 그들은 직면한 문제 너머를 생각하며, 문제를 항상 더 큰 맥락에 놓고, 항상 더 큰 그림을 보려 한다.
그들의 또 다른 성공의 열쇠는 자신이 하는 모든 일에 책임을 진다는 점이다. 책임감이 있기 때문에 프로젝트가 방치된 채로 끝장나는 걸 게으르게 옆에서 지켜보고만 있지 않는다.
[ 고양이가 내 소스코드를 삼켰어요 ]
실용주의 철학의 초석 중 하나는 경력 향상, 프로젝트, 일상 업무의 면에서 자신과 자신의 행동에 대해 책임을 지는 것이다. 자신의 무지나 실수를 인정하기를 두려워하지 않고, 전문가답게 정직하고 솔직해지려고 노력한다. 자신의 능력에 대해 자부심을 가질 수 있지만, 실수나 무지와 같은 단점에 대해서도 정직해져야 한다.
“가장 큰 약점은 약점을 보일 것에 대한 두려움이다.”
모든 부분을 직접 통제할 수는 없다. 통제 불가능하거나 위험요소가 너무 큰 상황은 책임지지 않을 권리가 있다. 따라서 개인적으로 최선을 다하는 것 외에도 통제 밖의 위험 요소에 대해 분석해야 한다.
결과에 대한 책임을 받아들이면, 그 결과에 대해 나중에 해명해야 한다. 실수를 저지르거나 잘못된 판단을 내렸다면, 정직하게 인정하고 다른 방안을 제안하라. 다른 사람이나 다른 무언가를 비난하거나 변명하지 말라. 프로그래밍 언어, 경영진 혹은 동료 때문이라고 떠넘기지 말라. 이들 모두 한몫씩 했을 수 있지만 변명이 아닌 해결책을 제공해야 한다. 만약 위험요소가 있다면 그에 대한 대책을 세워야 한다. 소스코드와 디스크가 다 망가져 버렸는데 백업이 없다면, 그것은 여러분의 잘못이다.
변명 대신에 대안을 제시하라. 안된다고 하지 말고 상황을 개선하기 위해 무엇을 할 수 있는지 설명하라. 어설픈 변명을 늘어놓기 전에 그 변명거리를 없애도록 노력해보라.
“어설픈 변명을 만들지 말고 대안을 제시하라.”
[ 소프트웨어 엔트로피 ]
도시 부패를 연구하는 사람들은 깨진 유리창 이론을 발견했다.
오랜 기간 수리하지 않고 방치된 창문 하나가 거주자들에게 버려진 느낌을 스며들게 한다. 그래서 다른 창문이 하나 더 깨진다. 사람들은 이제 어지르기 시작하고 낙서가 등장하며 심각한 구조적 손상이 시작된다. 꽤 짧은 시간 안에 소유주가 그걸 고치려는 의지를 넘어설 정도로 건물이 손상되고, 결국 버려진 느낌은 현실이 된다.
깨진 창문(나쁜 설계, 잘못된 결정, 혹은 형편없는 코드)을 고치지 않은 채로 내버려두지 말고, 발견하면 바로 고쳐라. 고칠 시간이 충분치 않다면 판자로 덮는 것만이라도 하라. 불쾌한 코드를 주석처리 하거나, 아직 구현되지 않았다는 메세지를 표시하거라. 더 이상의 손실을 예방하기 위해 어떤 조치든 취하고, 현 상황을 잘 관리하고 있음을 보여줘라.
깨끗하고 잘 기능하는 시스템의 창문이 깨지기 시작하면 급속도로 악화된다. 방치는 다른 어떤 요인보다도 소프트웨어의 부패를 더 가속시킨다. 소프트웨어의 부패에서 가장 중요한 것은 심리적 또는 문화적 요소다.
깨진 창문이 꽤 있는 프로젝트를 한다면, “나머지 코드가 전부 쓰레기니까 나도 그렇게 하지 뭐” 라는 사고로 빠져들기 너무도 쉽다. 반면 코드가 아름다운(깨끗하고, 잘 설계되었으며 우아한) 프로젝트와 팀이면, 특별한 주의를 기울여서 엉망으로 만들지 않도록 노력할 확률이 높다. 데드라인, 배포 날짜, 시사회 데모 등이 있더라도 엉망진창으로 만드는 첫 사람이 자신이 되는 것만은 피하려 한다.
[ 돌멩이 수프와 삶은 개구리 ]
무엇을 어떻게 해야 하는지 정확히 아는 상황이 있다. 하지만 일을 착수하려고 허락을 구하는 때부터 뭔가가 지연되고 복잡해진다. 모든 사람이 각자 자신의 자원을 지키려고 할 것이다.
이럴 때면 먼저 큰 무리 없이 제공할 수 있는 것을 잘 개발해라. 일단 되면, 사람들에게 보여주고, 그들이 경탄하게 하고는 “만약 …를 추가하면 더 나아지겠죠?” 하고 말하라. 그게 그다지 중요하지 않은 것처럼 가장하라. 그리고 물러나 앉아 여러분이 애초에 원했던 그 기능을 추가해달라고 그들이 부탁하기 시작할 때까지 기다려라. 그들에게 미래를 살짝이라도 보여주면 그들은 원조를 위해 집결할 것이다.
“변화의 촉매가 되라.”
[ 적당히 괜찮은 소프트웨어 ]
실세계에서는 진정 완벽한 것을 만들어 내기란 불가능하다. 완벽할 수는 없지만 “적당히 괜찮은” 소프트웨어를 만들도록 자신을 단련할 수 있다. 자신은 더 생산적이 되고 사용자는 한층 더 행복해 할 것이다. “적당히 괜찮은”을 결정하는 과정에는 사용자가 참가할 기회를 주어야 한다.
많은 사용자들은 완성된 소프트웨어를 위해 일 년을 기다리는 것보다 오늘 당장 조금 불편한 소프트웨어를 사용하고 싶어한다. IT 담당 부서들도 이에 동의할 것이다. 따라서 오늘의 훌륭한 소프트웨어는 많은 경우, 내일의 완벽한 소프트웨어보다 낫다. 또한 사용자들에게 뭔가 직접 만져볼 수 있는 것을 일찍 준다면, 피드백을 통해 더 나은 솔루션에 도달할 수 있을 것이다.
[ 지식 포트폴리오 ]
여러분의 지식과 경험이야말로 가장 중요한 전문가적인 자산이지만 그것들은 소진하는 자산이다. 새로운 기술, 언어, 환경이 등장함에 따라 지식은 옛것이 되고, 변화하는 시장은 경험을 퇴물이나 별 소용없는 것으로 만들 수 있다. 여러분의 지식 가치가 점점 떨어짐에 따라, 회사나 클라이언트에 대한 여러분 자신의 가치 역시 떨어진다.
지식 포트폴리오
우리는 성공적으로 경력을 쌓기 위해 아래와 같은 지침으로 지식 포트폴리오를 관리해야만 한다.
- 주기적인 투자: 지식을 쌓기 위해 주기적으로 학습해야 함
- 다각화: 더 많은 기술에 익숙하다면 변화에 더 잘 적응함
- 리스크 관리: 리스크가 큰 기술과 그렇지 않은 기술을 관리해야 함
- 싸게 사서 비싸게 팔기: 새롭게 떠오르는 저평가 기술을 빠르게 배우는 것은 이익이 클 수 있음
- 검토 및 재조정: 동적으로 변하는 사업인 만큼 조정이 필요함
투자를 계속하는 것이 중요하다. 새로운 언어나 어떤 기술에 대해 익숙할 정도가 되면 다음으로 나아가라.
“지식에 대한 투자가 언제나 최고의 이윤을 낸다.”
학습의 기회
누군가 질문을 했는데 모른다면 허물없이 그걸 인정하라. 그리고 거기에서 멈추지 말고, 답을 찾기 위한 개인적인 도전으로 생각하여 검색을 하고 책을 읽어라. 스스로 답을 못찾겠거든, 답을 찾아줄 사람을 찾아라. 중단하지 마라. 다른 사람들과 이야기함으로써 개인 네트워크를 구축하는데 도움이 되기도 하고, 답을 찾는 도중에 별로 관련이 없어 보이는 문제들에 대한 해답을 찾아서 놀라는 일도 생길 것이다. 게다가 포트폴리오는 그 사이 계속 커져 간다.
이 모든 독서와 연구는 시간이 걸리므로 미리 계획해야 할 필요가 있다. 할 일 없는 시간을 위해 늘 읽을거리를 준비하라. 병원에서 진료를 기다리느라 허비되는 시간은 밀린 독서를 할 훌륭할 기회다.
비판적 사고
마지막으로 중요한 점은 “비판적으로” 생각하는 것이다. 자신의 포트폴리오에 있는 지식이 정확하고, 외부의 과대광고에 흔들림이 없도록 확실히 해야 한다. 자신의 신념이 유일한 답이라고 주장하는 열광자들을 주의하라.
[ 소통하라 ]
개발자로서 우리는 여러 층과 소통해야 한다. 회의를 하고, 듣고 말하며 여러 시간을 보낸다. 하루 중 많은 시간을 소통하며 보내기 때문에, 이를 잘해야 할 필요가 있다. 최고의 아이디어, 최상의 코드 혹은 실용주의적인 사고 등이 있다고 해도 다른 사람들과 소통할 수 없다면 아무 소용이 없다.
말하고 싶은게 무언지 알아라
무엇을 말할지 미리 계획하라. 개요를 작성하고 자문하라. “이게 내가 말하고자 하는 것을 잘 전달하는가?” 그렇게 될 때까지 다듬어라.
청중을 알아라
정보를 전달하고 있는 경우에만 소통하고 있다고 말할 수 있다. 그러기 위해서는 청중의 요구와 관심, 능력을 이해할 필요가 있다. 우리는 개발자가 어떤 난해한 기술의 장점에 대해 읊조리는 동안, 앉아 있는 마케팅 부사장의 눈이 점점 흐리멍텅해지는 회의에 참석해봤다. 이것은 소통이 아니다. 단지 지껄임일 뿐, 짜증나는 일이다.
때를 골라라
청중이 무엇을 듣기 원하는지 이해하기 위해서는, 그들의 우선순위를 알아야 한다. 가끔 “…에 대해 이야기할 좋은 때일까?”라는 간단한 질문을 해보는 것만으로도 충분하다.
스타일을 골라라
누군가는 좀 격식 있는 “그저 사실만 전달하는” 브리핑을 원한다. 또 누군가는 본론에 들어가기 전에 길고 폭넓은 한담을 원한다. 뭐가 좋을지 모르겠거든 물어봐라. 전달하는 스타일이 청중에 어올리도록 조정하라.
하지만 여러분이 의사소통의 나머지 반쪽이라는 사실을 기억하라. 누군가가 뭔가를 간명하게 설명해 달라고 하는데, 그 설명을 줄일 수 있는 방법이 없다는 생각이 들면, 사실이 그렇다고 말하라. 이런 종류의 피드백 역시 의사소통의 한 가지 형태임을 기억하라.
멋져 보이게 하라
여러분의 아이디어는 중요하다. 청중에게 전달하기 위한 멋있는 매개물이 필요하다. 많은 개발자들은 문서를 만들 때에 내용에만 집중하는데, 이것은 실수다.
청중을 참여시켜라
가능하다면 문서 초안에 독자가 참여하도록 하라. 피드백을 받고, 그들의 머릿속을 도용하라. 더 좋은 관계를 형성하게 될 것이고, 그 과정에서 더 나은 문서를 만들게 될 것이다.
청자가 되어라
다른 사람들이 여러분이 하는 말을 경청해주길 바란다면 사용할 수 있는 기법이 하나 있다. 바로 경청하는 것이다. 모든 정보를 갖고 있어도 사람들이 하는 말을 귀울여 듣지 않는다면, 그들 역시 여러분의 말을 귀기울여 듣지 않을 것이다.
질문을 해서 사람들이 이야기를 하도록 복돋우거나, 여러분이 한 말을 그들이 요약하도록 하라. 회의를 대화로 바꾸면 생각을 좀 더 효과적으로 전달할 수 있을 것이다.
응답하라
누군가에게 질문을 했는데 아무런 응답도 없다면 그가 무례하다고 느낄 것이다. 응답이 단순히 “다음에 답해 드리겠습니다.” 이라도 언제나 답을 해라. 늘 사람들에게 응답을 해주면 때때로 일어나는 실수에 훨씬 더 관대해질 것이고, 여러분이 아직 그 사항을 잊지 않았다는 느낌을 줄 것이다.
진공 속에서 작업하지 않는 이상 우리는 의사소통을 해야 한다. 그 소통이 더 효과적일수록 좀 더 많은 영향력을 갖게 될 것이다.
2. 실용주의 접근법
소프트웨어 개발의 모든 차원에 적용가능한 팁과 요령이 있고, 거의 공리와 같은 아이디어들이나 사실상 보편화된 프로세스들이 있다. 하지만 이런 접근법들은 문서로 만들어진 경우가 드물다. 주로 설계나 프로젝트 관리, 코딩 등의 논의 중에 간혹 몇 문장 적혀있는 것을 볼 수 있을 것이다. 이 장에서는 이런 아이디어들과 프로세스들을 한데 모아 보려고 한다.
[ 중복의 해악 ]
소프트웨어를 신뢰성 높게 개발하고, 이해하고 유지보수하기 쉽게 만드는 유일한 길은 우리가 DRY 원칙을 따르는 것 뿐이라 생각한다.
“DRY(Do not Repeat Yourself) - 반복하지 마라”
DRY를 따르지 않는다면, 똑같은 것을 여러 곳에 표현해야 한다. 하나를 바꾼다면 나머지 것들도 바꿔야 한다. 이것은 기억하느냐 마느냐의 문제가 아니라 언제 잊어버릴 것인가 하는 문제다.
“모든 지식은 시스템 내에서 단일하고, 애매하지 않고, 정말로 믿을만한 표현 양식을 가져야 한다.”
어떻게 중복이 생기는가?
- 강요된 중복: 개발자들은 다른 선택이 없고, 환경(문서, 코드, 개발환경 등)이 중복을 요구하는 것처럼 보인다.
- 부주의한 중복: 개발자들은 자신들이 정보를 중복하고 있다는 것을 깨닫지 못한다.
- 참을성 없는 중복: 중복이 쉬워 보여서 개발자들이 게을러져서 중복을 하게 된다.
- 개발자 간의 중복: 한 팀(혹은 다른 팀)의 여러 사람들이 동일한 정보를 중복한다.
코드 내의 문서화
DRY 원칙은 저수준의 지식은 코드에 놔두고, 주석은 고수준의 설명을 위해 아껴두라고 말한다. 그러지 않으면 지식을 중복하게 되며, 변경할 때마다 매번 코드와 주석 모두를 바꾸어야 한다.
문서화와 코드
우리 모두는 마감일이 다가오면 문서의 갱신을 뒤로 미루기 쉽다는 걸 안다. 문서와 코드 모두는 동일 지식에 대한 표현이므로 테스트를 통해 문서 자체를 자동 생성되도록 할 수 있다.
[ 직교성 ]
직교성이란?
직교성(Orthogonality)은 일종의 독립성 혹은 결합도 줄이기를 의미한다. 하나가 바뀌어도 나머지에 어떤 영향도 주지 않으면 서로 직교한다고 할 수 있다. 잘 설계된 시스템에서는 데이터베이스 코드가 사용자 인터페이스에 대해 직교할 것이다.
직교성의 장점
비직교적인 시스템은 본질적으로 변경이 어렵다. 시스템의 컴포넌트들이 고도로 상호 의존적인 경우, 특정 부분만 수정하는 방법이 없다.
우리는 독립적이며, 단일하고 잘 정의된 목적을 가진 컴포넌트, 즉 자족적인(self-contained) 컴포넌트를 설계하기 원한다. 컴포넌트들이 각기 격리되어 있으면 어느 하나를 바꿀 때 해당 컴포넌트의 외부 인터페이스를 바꾸지 않는 한 나머지 것들을 걱정하지 않아도 된다.
이렇듯 직교적인 시스템을 작성하면 생산성 향상과 리스크 감소라는 두 가지의 큰 장점이 있다.
프로젝트 팀
직교적인 팀은 더 효율적이다. 팀 내 업무가 겹치는 영역이 많다면 구성원들은 책임 영역에 대해 혼동하게 된다. 뭘 하나 바꾸려고 해도 그들 중 누구라도 영향을 받을 수 있기 때문에 전체 팀원이 모여야 한다.
변경을 위해 토론에 참여하는 사람이 많을수록 팀의 직교성은 낮다. 물론 직교성이 높음과 별개로 서브 팀들끼리 지속적인 의사소통할 것을 장려한다.
설계
직교적인 설계를 테스트하는 손쉬운 방법이 있다. 컴포넌트들을 나누었을 때 다음과 같이 스스로에게 물어보라. “특정 기능에 대한 요구사항을 극적으로 변경했을 경우, 몇 개의 모듈이 영향을 받는가?” 직교적인 시스템에서는 답이 하나여야 한다. 또한 현실 세계의 변화와 설계 사이의 결합도를 얼마나 줄였는지에 대해서도 스스로에게 물어보기 바란다. 자신의 힘으로 제어할 수 없는 속성에 의존하지 마라.
툴킷과 라이브러리
외부 도구를 도입할 때, 시스템의 직교성을 보존할 수 있는지 주의 깊게 살펴보고, 현명하게 선택해야 한다. 이것이 여러분의 코드에 있어서는 안될 변화를 강요하고 있지는 않은지 검토해보라.
코딩
코드를 작성하고 있다면 언제나 애플리케이션의 직교성을 떨어뜨릴 수 있는 위험에 노출되어 있다. 현재 코딩하는 부분 뿐 아니라 애플리케이션의 큰 맥락을 끊임없이 살피지 않으면 의도치 않게 중복 기능을 추가하거나 동일한 지식을 두 번 표현하게 될 수 있다.
자신이 작성하는 코드를 항상 비판적으로 검토해보는 습관을 기르기 바란다. 기회가 있을 때마다 코드의 구조와 직교성을 향상시키기 위해 노력하라. 이것이 리팩터링이다.
테스트
직교적으로 설계, 구현한 시스템은 테스트하기 더 쉽다. 시스템 컴포넌트 간의 상호작용이 형식화되고 제한되기 때문에 시스템의 많은 부분을 독립적인 모듈 수준에서 테스트 할 수 있기 때문이다.
단위 테스트를 만든다는 것 자체가 직교성을 테스트해 볼 수 있는 흥미로운 작업이다. 만약 시스템의 상당 부분을 끌어들여 테스트 케이스를 만들고 컴파일 혹은 링크해야 한다면 이는 결합도를 적절히 줄이지 못했다는 증거다.
버그 수정은 시스템의 직교성을 총체적으로 점검해볼 수 있는 값진 시간이다. 문제가 발생했다면 버그 수정이 얼마나 지역화 되어 있는지 평가해보라.
문서화
직교성은 놀랍게도 문서에도 적용할 수 있다. 내용과 표현이 두 축이 된다. 정말 직교적인 문서라면 내용 변화 없이 표현을 극적으로 바꿀 수 있을 것이다.
직교적으로 살아가기
직교성은 DRY 원리와도 밀접한 관계가 있다. DRY 원리는 시스템 내부의 중복을 최소화시키고, 직교성은 시스템 컴포넌트 간의 상호의존도를 줄인다. 당연한 말이겠지만 DRY 원리로 무장하고 직교성 원리를 충실히 사용한다면 개발하고 있는 시스템이 더 유연하고, 이해하기 쉽고 또한 디버그, 테스트, 유지도 쉬워질 것이다.
[ 가역성 ]
“이 방법으로만 해결할 수 있어” 같은 근시안적인 생각으로 인해 예상치 못한 경우에 한숨지을 일이 많을 것이다. 많은 팀들이 프로젝트를 진행하면서 어쩔 수 없이 고통스럽게 그들의 근시안을 조금씩 수정하게 된다. 만약 여러분이 어떤 사실을 굳게 믿고 그 사실에 전적으로 의존하고 있다면, 거의 100% 그것이 변하게 될 거라고 얘기해주고 싶다.
“당신이 가진 생각이 딱 하나밖에 없다면, 그것만큼 위험한 것은 없다.”
가역성
DRY 원리, 결합도 줄이기, 메타데이터 사용하기 등을 따른다면 중요하면서도 되돌릴 수 없는 결정을 가능한 한 줄일 수 있다. 이는 우리가 프로젝트 초기에 항상 최선의 결정을 내리는 것이 아니라는 점에서 매우 유용하다.
“최종 결정이란 없다.”
유연한 아키텍처
많은 사람들이 코드를 유연하게 유지하려고 노력한다. 하지만 아키텍처, 배포, 벤더 통합 영역의 유연성에 대해서도 관심을 기울일 필요가 있다. 누구도 미래에 대해서는 알 수 없으며, 우리라고 예외는 아니다. 여러분의 코드가 이것도 할 수 있고 필요한 경우 다른 것을 할 수도 있게 하라.
[ 예광탄 ]
막연한 요구사항에서 시스템을 돌아갈 때까지 세세히 명세화하는 것이 전형적인 반응이다. 모든 불확실한 점을 잡아매고, 환경 조건을 제약하고, 모든 요구사항을 일일이 항목으로 만들어서 몇 상자나 되는 명세서를 만든다. 목표물의 위치를 추측해서 총을 쏜다. 상당한 양의 계산을 우선 하고 나서, 그 다음엔 발사하고, 맞기를 비는 것이다.
하지만 실용주의 프로그래머는 그렇지 않다.
어둠 속에서 빛을 내는 코드
요구사항으로부터 최종 시스템의 일부 측면에까지 빨리, 눈에 보이게, 반복적으로 도달하게 해줄 무언가를 찾아야 한다. 이렇게 만들어진 코드는 나중에 버리려고 만드는 것이 아니다. 계속 사용할 코드다. 상용 코드와 마찬가지로 모든 에러 검사, 구조화, 문서화, 자기 검사가 포함된다. 단지 아직 완전한 기능이 들어있지 않을 뿐이다.
하지만 시스템을 구성하는 요소를 모두 연결해 놓은 후라면 목표물에 얼마나 가까이 다가섰는지 확인할 수 있으며, 필요하다면 조정도 할 수 있다. 그렇게 해서 일단 목표물을 맞춘다면 기능을 추가하는 일은 쉽다.
이는 점진적인 접근 방법으로, 일종의 거대 공학적 접근 방식과 대비된다. 점진적인 코드 접근 방법에는 여러 장점이 있다.
- 사용자들은 뭔가 작동되는 것을 일찍부터 보게 된다.
- 개발자들은 들어가서 일할 수 있는 구조를 얻는다.
- 통합 작업을 수행할 기반이 생긴다.
- 보여줄 것이 생긴다.
- 진전 상황에 대해 더 정확하게 감을 잡을 수 있다.
예광탄이 언제나 목표물을 맞추는 것은 아니다.
이러한 기법은 일이 어떻게 될지 100% 확신할 수 없는 상황에서 사용된다. 그러므로 처음 몇 번 시도 때 목표에 맞지 않더라도 놀랄 필요가 없다. 사용자들이 “이건 내가 말했던 게 아닌데”라고 이야기하거나, 필요할 때 여러분이 원하는 데이터가 준비되어 있지 않을 수도 있다. 성능에 문제가 있을 확률도 높다. 지금 있는 것을 목표물에 가까이 가져가려면 어떻게 바꾸어야 할지 생각해내고, 가벼운 개발 방법론을 선택했다는 사실에 감사하라. 코드의 크기가 작으면 관성 역시 약하므로 빠르고 쉽게 바꿀 수 있다.
예광탄 코드 대 프로토타이핑
프로토타입은 최종 시스템의 어떤 특정한 측면을 탐사해 보는 것이 목표다. 먼저 일어나는 정찰과 정보 수집인 것이다. 진짜 프로토타입 방식을 따른다면, 어떤 개념을 구현해 보려고 시도할 때 대충 끼워맞춘 것들을 모두 버린 다음, 실험 과정에서 얻은 교훈을 바탕으로 다시 코드를 만들게 된다. 즉, 나중에 버릴 수 있는 코드를 만든다.
반면 점진적인 코드 접근 방법은 다른 종류의 문제에 대한 대응 방법이다. 애플리케이션이 전체적으로 어떻게 연결되는지를 알고 싶다. 사용자들에게 실제로 애플리케이션의 요소들이 상호작용하는지 보이고 싶고, 개발자들에게는 코드를 붙일 아키텍처적 골격을 제시하고 싶다. 이는 기능은 별로 없지만 완결된 코드이며, 최종 시스템 골격의 일부를 이룬다.
프로토타입과 포스트잇
프로토타입은 위험 요소를 분석하고 노출시키며 이를 매우 저렴한 비용으로 바로잡을 기회를 얻기 위해 만든다. 프로토타입은 꼭 코드가 아니여도 된다.포스트잇은 작업 흐름과 애플리케이션 로직과 같은 동적인 것들을 프로토타이핑해 볼 수 있는 훌륭한 도구다. 사용자 인터페이스는 화이트보드에 그려보는 등 기능은 구현하지 않고 인터페이스만을 그려보는 방법으로 프로토타입을 만들 수 있다.
프로토타입은 제한된 몇 가지 질문에 답할 목적으로 설계되기 때문에 실제 제품보다 훨씬 적은 비용으로 빠르게 개발할 수 있다. 당장 중요하지 않은 세부사항 등은 코드에서 무시할 수 있다. 하지만 만약 세부사항을 포기할 수 없는 환경에 처해있다면, 실제로 프로토타입을 만들고 있는 것인지에 대해 스스로 물어보라. 아마도 이런 경우에는 점진적인 스타일의 개발이 더 적절할 것이다.
프로토타입의 대상
프로토타입을 통해 조사할 대상은 위험을 수반하는 모든 것이다. 또한 이전에 해본 적이 없는 것, 최종 시스템에 매우 중요한 것 등이 프로토타입의 대상이 된다. 증명되지 않았거나, 실험적이거나, 의심이 가는 것, 심적으로 편하지 않은 것 모두가 프로토타이핑의 대상이 될 수 있다.
- 아키텍처
- 기존 시스템에 추가할 새로운 기능
- 외부 데이터의 구조 혹은 내용
- 써드파티 도구나 컴포넌트
- 성능 문제
- 사용자 인터페이스 설계
프로토타이핑은 학습 경험이며, 프로토타입의 가치는 생성된 코드에 있는 것이 아니라 이를 통해 배우게 되는 교훈에 있다. 이것이 프로토타이핑의 진정한 핵심이다.
“프로토타입을 통해 학습하라.”
프로토타입을 어떻게 사용할 것인가?
프로토타입을 만들 때 무시해도 좋은 세부사항은 다음과 같다.
- 정확성: 적절히 가짜 데이터를 사용할 수 있다.
- 완전성: 선정된 데이터와 특정 메뉴에서만 작동하면 되기 때문에 제한된 기능 만을 제공하기도 한다.
- 안전성: 미리 정의된 방법대로 실행시키지 않으면 에러가 생기며, 에러 검사는 불완전할 수도 있고, 무시될 수도 있다.
- 스타일: 프로토타입을 통한 경험의 결과를 문서화할 수는 있지만, 프로토타입 자체에는 많은 문서가 필요 없다.
아키텍처 프로토타이핑
많은 프로토타입들이 고려 중인 전체 시스템을 모델링하기 위해 만들어진다. 앞선 점진적인 시스템과 달리 프로로타입 시스템의 모듈이 꼭 기능을 가져야 하는 것은 아니다. 사실 코드를 작성하지 않고 화이트보드, 포스트잇, 인덱스카드 등을 사용해도 된다. 프로토타입에서 기대하는 것은 전체적으로 시스템이 어덯게 동작할지에 대한 감을 잡는 것이다. 다시 말하지만 세부 사항은 무시한다. 다음은 아키텍처 프로토타입에서 규명할 만한 사항이다. 이 중 마지막 항목이 가장 놀랍고도 값진 결과를 내놓기 쉽다.
- 주요 컴포넌트의 책임이 잘 정의되었고 적절한가?
- 주요 컴포넌트 간의 협력관계가 잘 정의되었는가?
- 결합도는 최소화가 되었는가?
- 잠재적 중복을 찾아낼 수 있는가?
- 인터페이스 정의와 제약 사항은 수용할만한가?
- 각 모듈이 실행 중에 필요로 하는 데이터에 접근할 수 있는 경로를 갖고 있는가? 모듈은 데이터를 필요로 할 때 데이터에 접근할 수 있는가?
어떻게 프로토타입을 사용하지 않을 것인가?
프로토타입을 코드로 만들 때는 항상 모든 사람에게 여러분이 폐기처분할 코드를 작성하고 있다는 사실을 이해시켜야 한다. 만약 적절한 정도만 기대하도록 해두지 않으면 겉으로 완벽해보이는 프로토타입 시연을 보고 프로젝트 후원자 혹은 관리자가 프로토타입 자체 혹은 이를 보완한 결과를 배포하자고 주장하기 쉽다.
만약 여러분이 작업하는 환경이나 문화에서 프로토타입 코드의 목적이 잘못 해석될 가능성이 크다고 느낀다면, 점진적인 접근 방식을 취하는 게 나을 것이다.
[ 도메인 언어 ]
어떤 시스템을 제안하는 사용자의 말을 듣다보면, 그들이 정확히 시스템이 어떻게 동작해야 하는지 우리에게 말해주는 경우도 있다. 사용자들이 이렇게 정리가 잘 된 진술을 많이 해준다면, 여러분은 그들이 원하는 내용을 정확히 표현하는 도메인 언어를 만들 수 있다. 그리고 이를 통해 애플리케이션 도메인에 훨씬 가깝게 프로그래밍할 수 있다.
[ 추정 ]
추정에 대한 지식을 배운 후에 경험을 통해 추정 능력을 계발하고, 어디에 직관적인 느낌을 적용해야 할지 알게 된다면, 무언가의 가능성을 가늠할 수 있는 마술과 같은 능력을 발휘할 수 있게 될 것이다. 그러면 직관적으로 이것이 가능한지 판단할 수 있게 된다. 코딩할 때에도 어떤 서브시스템을 최적화해야 하고, 어떤 시스템을 그대로 남겨두어도 될지를 판단할 수 있는 능력을 갖게 될 것이다.
“추정을 통해 놀람을 피하라.”
얼마나 정확한 것이 충분히 정확한 것인가?
추정에서 한 가지 재미있는 사실은 사용하는 단위가 결과의 해석에 차이를 가져온다는 것이다. 만약 무언가를 끝내는 데 130일 정도를 일해야 한다고 말한다면, 듣는 사람은 상당히 가까운 시일 내에 끝날 것이라 생각하며 정확한 일정을 기대하게 된다. 하지만 “대략 6달 정도 걸리겠군요”라 말한다면 지금부터 5~7달 사이 언젠가 끝날 것이라 여길 것이다. 두 숫자는 같은 기간을 이야기하지만, “130일”은 여러분이 느끼는 것보다 더 높은 정확성을 내포한다. 우리는 여러분이 기간을 추정할 때 다음과 같은 단위를 사용하기를 추천한다.
1~15일 | 일 |
3~8주 | 주 |
8~30주 | 달 |
30주 이상 | 추정치를 말하게 전에 다시 한번 생각해보라. |
그러므로 추정에 필요한 일들을 끝내고, 프로젝트에 대략 125근무일이 소요될 것이라는 것을 알았다면 “대략 여섯 달”이 걸릴 거리고 이야기하라.
추정치는 어디에서 오는가?
기본적인 추정 기술은 이미 그 일을 해본 사람에게 물어보는 것이다. 똑같은 일을 해본 사람을 찾기는 어렵겠지만, 놀라울 정도로 자주 다른 사람들의 경험을 통해 성공적인 추정치를 낼 수 있다는 것을 알게 될 것이다.
무엇을 묻고 있는지 이해하자
어떤 종류의 추정이건 연습의 첫 단계는 상대방이 무엇을 묻고 있는지에 대해 이해하는 것이다. 앞서 이야기한 정확도만이 아니라 도메인의 범위에 대해 감을 잡을 필요가 있다. 보통 이는 질문에 명시적으로 드러나지 않지만, 추정을 하기 전에 미리 생각하는 습관을 기르는 것이 좋다.
답을 계산하라
문제가 매우 단순한 경우에만 추정치가 하나의 답을 갖게 될 것이다. 하지만 시스템이 복잡해지면 여러 답에 대해 양다리 걸치기를 하고 싶어질 것이다.
계산 단계에서 언뜻 이상해 보이는 답을 얻을 수도 있다. 이때 이 값을 너무 쉽게 버리면 안된다. 만약 계산이 정확하다면, 아마도 문제를 잘못 이해했거나 모델이 잘못되었을 것이다. 이는 귀중한 정보다.
추정치를 기록하는 용기
여러분이 계산한 추정치를 기록해 놓고, 이 값이 실제 결과에 얼마나 가까운지를 평가해 보는 것은 좋은 생각이다. 만약 전체 추정치가 몇 개의 하위 추정치의 계산을 포함하고 있다면 이 역시 기록하라. 여러분은 추정치가 꽤 좋다는 것을 자주 발견할 것이고, 점차로 이를 기대하게 될 것이다.
추정치가 잘못되었더라도 움츠리거나 도망가지 마라. 왜 여러분의 추측과 실제 값이 달라졌는지 원인을 찾아야 한다. 원인이 무엇이든, 시간을 들여 이를 규명하라. 다음 추정치는 훨씬 나아질 것이다.
프로젝트 일정 추정하기
추정에 대한 일반 법칙은 변화하는 애플리케이션의 복잡성과 변덕스러움 앞에 화해되기 쉽다. 프로젝트의 일정을 정할 수 있는 유일한 방법은 진행하는 해당 프로젝트를 경험하는 것뿐이란 사실을 알게 된다. 모순이라고? 다음과 같은 단계를 반복하는 점증적 개발을 연습한다면 꼭 그런 것만은 아니다.
- 요구사항 체크하기
- 위험 분석하기
- 설계, 구현, 통합
- 사용자와 함께 검증하기
프로젝트 초기에는 얼마나 많은 반복이 필요할지에 대해 막연히 생각할 수 밖에 없을 것이다. 어떤 방법론은 반복 횟수를 초기 계획의 일부로 포함시킨다. 하지만 소규모 프로젝트가 아니라면 이는 오히려 실수가 될 수 있다. 전에 같은 팀원에 같은 기술을 사용하여 비슷한 애플리케이션을 만들어본 경험이 없다면 단순한 추측에 불과하다.
그러므로 초기 기능의 구현과 테스트를 마친 후, 이를 첫 번째 반복의 끝으로 삼아라. 이 경험에 기반해 반복의 횟수와 각 반복에서 무엇을 할지에 대한 초기 추측을 담을 수 있다. 이런 정제는 각 반복이 끝날 대마다 나아질 것이고, 일정에 대한 확신도 이와 함께 증가할 것이다.
“코드와 함께 일정도 반복하며 조정하라”
이 방법은 경영자들에게 별로 인기가 없다. 경영자들은 보통 프로젝트가 시작하기도 전에 하나의 정확한 숫자를 원하기 때문이다. 여러분은 그들이 팀, 팀의 생산성 그리고 환경이 일정을 결정한다는 사실을 이해하도록 도와야 한다. 이를 공식화하고 일정 정제를 각 반복 과정의 일부로 삼았을 대, 그들에게 여러분이 할 수 있는 가장 정확한 일정 추정치를 건네줄 수 있을 것이다.
누군가 추정에 대해 물으면 무엇이라 대답해야 할까?
나라면 “나중에 전화드릴께요”라 말할 것이다. 프로젝트 도중 숨을 고르고, 잠시 시간을 내어 이번 항목에서 기술한 단계를 밟아나간다면 더 정확한 추정치를 알려줄 수 있다.
3. 기본적인 도구
도구는 재능을 증폭시킨다. 도구가 더 훌륭하고, 그걸 어떻게 사용하는지 더 잘 알수록, 여러분은 더 생산적일 수 있다. 일을 하는 데에 더 나은 방법이 없는가 늘 주변을 살펴라.
[ 디버깅 ]
디버깅의 심리
다른 사람의 버그를 발견한 후, 그 버그를 만들어낸 부정한 범죄자를 비난하는 데에 시간과 노력을 들이는 수가 있다. 어떤 회사에서는 비난이 문화의 일부고 거기서 카타르시스를 얻을 수도 있다. 하지만 그것을 비난하기 보다는 문제를 고치는 데에 집중하는 것이 중요하다. 버그가 누구의 잘못인지는 그리 중요한게 아니다.
“비난 대신 문제를 해결하라”
디버깅 전략
버그를 고치는 최선의 첫 단계는 그 버그를 재현할 수 있게 만드는 것이다. 만약 재현할 수 없다면 어떻게 그 버그를 고쳤다는 것을 알 수 있겠는가?
고무 오리
문제의 원인을 찾는데 매우 단순하지만 꽤나 유용한 기법으로, 누군가에게 그걸 설명하는 단순한 방법이 있다. 누군가에게 문제를 설명하게 되면 혼자 코드를 살펴볼 때는 당연히 여기고 지나갈 것을 명시적으로 이야기해야 한다. 이런 가정들 몇 가지를 발화하게 되면, 문제에 대한 새로운 통찰을 별안간 얻을 수 있다.
놀람의 요소
어떤 버그로 놀라게 될 때, 믿고 있던 진실들을 재평가해야만 한다. 모든 경계 조건을 테스트 했던가? 수년간 사용하고 있는 코드는 어떤가? 버그가 있지는 않을까?
뭔가 잘못될 때 놀라는 정도는 실행되는 코드에 갖는 신뢰와 믿음의 정도에 정비례한다. 무언가 예상치 못한 “놀라운” 실패를 대면했을 때 자신이 세운 가정들이 잘못되었다는 것을 깨달아야 한다. 버그에 관련한 루틴이나 코드가 제대로 작동한다고 “안다”고 해서 대충 얼버부려 지나치지 말고 증명하라.
놀라운 버그를 마주치면, 단순히 그걸 고치는 것을 넘어서, 왜 이 실패가 더 일찍 발견되지 않았을까 생각해 볼 필요가 있다. 버그를 미리 잡을 수 있도록 단위 테스트나 다른 테스트를 수정할 필요가 있는지 고려하라.
이런 작업을 하는 중, 이것과 동일한 버그가 있을 여지가 있는 다른 코드가 있는가? 그것들을 찾아서 고쳐야할 때는 바로 지금이다. “뭐가 일어나든지 간에” 다시 발생하면 그 사실을 알 수 있도록 하라.
이 버그를 고치는 데 긴 시간이 걸린다면 왜 그런지 자문하라. 다음번에는 이 버그를 좀 더 쉽게 고칠 수 있도록 할 수 있는 뭔가가 있을까? 더 나은 테스팅 훅을 만들어 넣거나, 로그 파일 분석기를 작성할 수도 있겠다.
마지막으로, 만약 버그가 누군가가 내린 잘못된 가정의 결과라면, 이 문제를 전체 팀과 함께 토론하라. 한 사람이 오해했다는 것은 여러 사람이 그럴 수 있다는 이야기다.
4. 실용주의 편집증
컴퓨터 역사를 통틀어 어느 누구도 완벽한 소프트웨어를 만들지 못했다. 이것을 기정 사실로 받아들이지 않는다면, 불가능한 꿈을 뒤쫓으며 시간과 노력을 낭비하게 될 것이다.
“완벽한 소프트웨어는 만들 수 없다.”
이런 암울한 현실 하에서 우리는 방어적으로 코딩하도록 가르침 받았다. 만약 조금이라도 의심이 들면 모든 정보를 확인한다. 잘못된 데이터를 찾아내기 위해 단정문을 사용한다. 일관성을 확인하고 데이터베이스 컬럼에 제약을 걸면서 대부분은 스스로 만족해한다.
하지만 실용주의 프로그래머들은 여기서 한 걸음 더 나아간다. “그들은 자기 자신 역시 믿지 않는다.” 어느 누구 심지어는 자기 자신도 완벽한 코드를 작성할 수 없음을 알기 때문에 실용주의 프로그래머는 자신의 실수에 대비해 방어적으로 코드를 짠다.
[ 죽은 프로그램은 거짓말을 하지 않는다 ]
가능한 한 빨리 문제를 발견하게 되면, 좀 더 일찍 시스템을 멈출 수 있다는 이득이 있다. 게다가 프로그램을 멈추는 것이 할 수 있는 최선일 때가 많다.
자바 언어와 라이브러리는 이 철학을 포용했다. 런타임 시스템에서 뭔가 예상하지 못한 것이 발생하면 RuntimeException을 던진다. 만약 이 예외가 잡히지 않으면 프로그램의 최상위 수준까지 스며 나올 것이고, 결국 스택 트레이스를 출력하며 프로그램을 멈춰버릴 것이다.
분명 실행 중인 프로그램을 그냥 종료해 버리는 것은 때로 적절치 못하다. 해제되지 않은 자원이 남아 있을 수도 있고, 로그 메시지를 기록할 필요가 있을 수도 있고, 열려 있는 트랜잭션을 청소해야 하거나, 다른 프로세스들과 상호작용해야 할 필요가 있을지도 모른다. 하지만 방금 불가능한 뭔가가 발생했다는 것을 코드가 발견한다면, 프로그램은 더 이상 유효하지 않다고 할 수 있다. 이 시점 이후로 하는 일은 모두 수상쩍은 게 된다. 되도록 빨리 종료해야 할 일이다. 일반적으로, 죽은 프로그램이 입히는 피해는 절름발이 프로그램이 끼치는 것보다 훨씬 덜한 법이다.
[ 단정적 프로그래밍 ]
모든 프로그래머가 자기 경력을 쌓는 초기부터 기억해야 하는 주문이 있는 것 같다.
“이런 일은 절대 일어날 리 없어”, “이 코드를 지금부터 30년 동안이나 사용하지는 않을 테니까, 연도에 두 자리 수를 사용해도 괜찮아”, “이 애플리케이션을 외국에서 사용하는 일은 절대 없을 텐데 뭐하러 국제화하지?”, “count는 음수가 될 수 없어”, “이 printf는 실패할 수 없어”
특히 코딩할 때는 이런 류의 자기 기만을 훈련하지 말아야 한다.
“하지만 물론 그건 절대 일어나지 않을 거야”라는 생각이 든다면, 그걸 확인하는 코드를 추가하라.
[ 언제 예외를 사용할까 ]
예외가 프로그램의 정상 흐름의 일부로 사용되는 일은 없어야 한다. 예외는 일종의 연쇄 goto 문과 같다. 예외를 정상적인 처리 과정의 일부로 사용하는 프로그램은 고전적인 스파게티 코드의 가독성, 관리성 문제를 전부 떠안게 되며, 캡슐화 역시 깨트린다. 예외 처리를 통해 루틴과 그 호출자들 사이의 결합도가 높아져버린다.
[ 리소스 사용의 균형 ]
메모리, 트랜잭션, 쓰레드, 파일, 타이머 등 어떤 제한이 있는 모든 종류의 리소스를 사용한다. 대개의 경우, 리소스 사용은 예측할 수 있는 패턴을 따른다. 리소스를 할당하는 루틴은 해제 역시 책임져야 한다.
실용주의 프로그래머는 자신을 포함해서 아무도 믿지 않기 때문에, 정말로 리소스가 적절하게 해제되었는지 실제로 점검하는 코드를 늘 작성하는 것이 언제나 좋다고 생각한다. 이 말은 대부분의 애플리케이션에서 보통 리소스의 종류마다 래퍼를 만들고 그 래퍼들이 모든 할당과 해제의 기록을 유지하는 것을 뜻한다. 코드 속에서 프로그램 논리에 따르자면 자원들이 반드시 이런 상태에 있어야 한다고 말할 지점들이 있을 것이다. 래퍼들을 사용해서 정말 그런지 점검하라.
5. 구부러지거나 부러지거나
현대의 미친듯이 빠른 변화 속도에 맞추기 위해서는 가능한 느슨하고 유연한 코드를 작성하기 위해 노력해야 한다. 그렇지 않으면 코드는 금세 낡고 수정하기 어렵다.
유연함을 유지하는 한 가지 좋은 방법은 가능한 적은 양의 코드를 작성하는 것이다. 코드를 수정한다는 것은 새로운 버그가 생길 가능성에 문을 열어 놓는 것이다.
[ 결합도 줄이기와 디미터 법칙 ]
코드를 모듈로 구성하고, 이들 간의 상호작용을 제한하라. 그러면 한 모듈이 변경되거나 교체된다 하더라도 다른 모듈들은 변경 없이 수행될 수 있다.
결합도 줄이기
의존의 증가가 나쁜 이유는 시스템 어딘가의 무관한 변화가 여러분의 코드에 영향을 미칠 수 있는 위험이 커지기 때문이다. 직접 객체 간의 관계를 헤집고 다닌다면 의존 관계가 조합적으로 폭발하게 될 수 있다.
디미터 함수 법칙
디미터 함수 법칙은 모듈 간 결합도를 최소화하려 시도한다. 이 법칙은 한 객체가 제공하는 메서드에 접근하기 위해 또 다른 객체들을 통하는 것을 허용하지 않는다.
“모듈간의 결합도를 최소화하라”
확실히 차이를 낳는가?
여느 기술과 마찬가지로 디미터 법칙 역시 장점과 단점을 잘 고려해야 한다. 디미터 법칙과 반대로 여러 모듈의 결합도를 높힘으로써 중요한 성능향상을 꾀할 수도 있다. 해당 모듈들이 서로 결합하고 있음을 잘 알고, 또 그것을 받아들일 수 있다면 아직 괜찮은 설계라 할 수 있다. 하지만 이런 경우가 아니라면 깨지기 쉽고, 유연하지 않은 미래를 향해 가고 있는 것이다.
[ 메타프로그래밍 ]
세부사항을 코드에서 몰아내라. 이렇게 함으로서 우리의 코드는 매우 설정가능하게 되고 “소프트”해진다. 즉 변화에 쉽게 적응할 수 있게 되는 것이다.
동적 설정
우선 시스템을 되도록 설정가능하게 만들기 바란다. 배경 색, 프롬프트 텍스트 뿐 아니라 알고리즘의 선택, 사용할 데이터베이스 제품, 미들웨어 기술, 사용자 인터페이스 스타일 등 시스템의 심층까지 말이다.
“통합하지 말고 설정하라”
메타데이터 주도 애플리케이션
가능한 마지막 순간까지 세부 정의를 피하고, 세부사항을 소프트하게, 변화하기 쉽게 남겨두라. 빠르게 변화할 수 있는 해결안을 강구함으로써 많은 프로젝트에 범람하는 방향 전환이란 홍수에 보다 유연하게 대처할 수 있게 될 것이다.
비즈니스 로직
비즈니스 로직은 다른 어떤 부분보다 변화하기 쉽기 때문에 이를 유연한 포맷을 통해 유지보수하는 것이 좋다.
[ 단지 뷰일 뿐이야 ]
우리는 전부터 프로그램을 커다란 하나의 덩어리로 짜지 말고, “나눠서 정복하기” 방법을 써서 여러 모듈로 나누어 짜야 한다고 배웠다. 모듈들마다 자기만의 책임이 있다. “잘 정의된 단 하나의 책임만 가지는 것”이 모듈(또는 클래스)에 대한 좋은 정의가 된다.
하지만 프로그램을 책임에 따른 여러 모듈로 나눈 다음에도 새로운 문제가 생긴다. 런타임에 객체들이 어떻게 서로 이야기하게 만들어야 하나? 모듈 사이의 논리적 의존성은 어떻게 관리해야 하나? 즉, 여러 다른 객체들의 상태 변화(또는 갱신된 데이터 값)를 어떻게 동기화해야 할까?
이 일은 유연하고 깔끔한 방식으로 이루어져야 한다. 우리는 객체가 서로에 대해 너무 많이 알기를 원하지 않는다. 우리는 모듈이 마치 위의 노래에 나오는 사람처럼 자기가 보고 싶은 것만 보도록 만들고 싶다.
이벤트를 이렇게 이용하면 객체들 사이의 결합을 최소화할 수 있다. 이벤트의 전송자는 수신자에 대해 아무런 직접적인 지식을 가질 필요가 없다. 사실, 저마다 중요하게 여기는 사항들이 전혀 다른 여러 수신자가 있을 수도 있다 (다행히도 전송자는 여기에 대해 아무것도 몰라도 된다)
하지만 이벤트를 이용하려면 약간 조심스러워야 한다. 분명 유지보수나 개선을 하기 쉽게 해주는 길은 아니다.
6. 코딩하는 동안 해야 할 일들
코딩은 기계적인 작업이 아니다. 코딩할 때는 매분마다 결정을 내려야 하는데, 프로그램이 정확하고 생산적으로 작동하면서 천수를 누리도록 하기 위해서는 사려 깊은 생각과 판단을 통한 결정이 필요하다.
[ 우연에 맞기는 프로그래밍 ]
우리는 우연에 맡기는 프로그래밍, 어쩌다 성공하는 코드에 의존하는 프로그래밍을 하지 말아야 한다. 대신 의도적으로 프로그래밍 해야 한다.
우연적 구현
우연적 구현은 단순히 코드가 지금 작성된 방식이 그렇기 때문에 생기는 일들을 가리킨다. 결국에는 문서화되지 않은 에러나 입력값이 특정한 조건에서만 돌아가는 경우와 마주치게 된다.
암묵적인 가정
모든 차원에서 사람들은 마음속에서 많은 것들을 가정하고 작업한다. 하지만 이런 가정들은 개발자들마다 다른 경우도 많다. 확고한 사실에 근거하지 않은 가정은 어떤 프로젝트에서든 재앙의 근원이 된다.
“우연에 맡기는 프로그래밍을 하지 말라”
[ 의도적으로 프로그래밍하기 ]
우리는 되도록 개발 초기에 에러를 발견하고 고쳐서 시간을 절약하고 싶다. 애초부터 에러를 적게 만들면 더 좋다. 이때 의도적으로 프로그래밍하는 것이 도움이 된다.
어떤 것이 잘 돌아가는 것처럼 보이기는 하는데 그 이유를 모를 경우, 그것이 우연은 아닌지 반드시 확인해야 한다.
[ 알고리즘의 속도 ]
가장 빠른 알고리즘이 언제나 가장 좋은 알고리즘은 아니다. 입력값의 규모가 작다면 삽입 정렬도 퀵정렬과 비슷한 성능을 내지만, 디버깅 시간은 퀵 정렬보다 적다.
따라서 성급한 최적화를 조심하라. 어떤 알고리즘을 개선하느라 여러분의 귀중한 시간을 투자하기 전에 그 알고리즘이 정말로 병목인지 먼저 확실히 해두는 것은 언제나 좋은 생각이다.
[ 리팩터링 ]
프로그램이 발전해가면, 초기에 내린 결정을 다시 고려하고 코드의 일부를 재작성하는 일이 점점 더 필요해진다. 코드는 정적인 존재가 아니므로 이것은 지극히 자연스러운 과정이다. 코드를 다시 작성하기, 다시 작업하기, 다시 설계하기는 총괄해서 “리팩터링”이라고 알려져 있다.
불행하게도, 소프트웨어 개발을 비유하는 가장 흔한 메타포는 건축이다. 버트란드 마이어는 “소프트웨어 건축”이라는 표현을 사용한다. 하지만 소프트웨어는 이런 식으로 돌아가지 않는다. 딱딱하기보다는 유기적인 존재다. 소프트웨어는 건축보다 정원일(gradening)에 훨씬 더 가깝다. 계획한 대로 잘 되지 않는 것들은 잡초 제거나 가지치기를 하는 것처럼, 어떤 루틴이 너무 크게 자라거나 너무 많은 것을 하려고 하면 둘로 나눠야 한다.
리팩터링은 언제 해야 하는가?
코드의 어떤 것이든 “잘못” 되었다고 생각될 때, 그것을 변경하는 일을 주저하면 안 된다. 언제나 바로 지금이 최적기다.
코드를 리팩터링하는 것은 사실 고통 관리를 실천하는 것이다. 소스코드를 이것저것 변경하는 것은 굉장히 고통스러운 작업일 수도 있다. 그럼에도 불구하고 현실을 피하지 말자.
현실 세계의 복잡한 문제들
리팩터링을 하지 않는 핑계로 자주 사용되는 이유가 일정의 압박이다. 하지만 이것은 설득력 있는 이유가 되지 못한다. 지금 리팩터링을 하지 않으면, 일이 더 진척되었을 때, 곧 신경 써야 할 의존성이 더 많이 생겼을 때, 문제를 고치기 위해 훨씬 더 많은 시간을 투자해야 한다. 그때가 되면 일정에 더 여유가 생길까? 그런 일은 없다.
“일찍 리팩터링하고, 자주 리팩터링하라.”
리팩터링 목록을 만들고 유지하라. 지금 당장 리팩터링하기 힘들다면, 일정에 리팩터링할 시간을 확실히 포함시킨다. 그 코드를 사용하는 사람들이 코드가 조만간 리팩터링될 것이라는 사실과 그것이 코드에 어떤 영향을 주게 될지 인지하도록 만들어야 한다.
리팩터링은 어떻게 하는가?
리팩터링의 본질은 재설계다. 그렇다고 규모가 거대한 코드에서 아무 부분이나 닥치는 대로 삭제해 버리면서 산산조각으로 분해한다면, 나중에는 일을 시작하기 전보다 더 좋지 않은 처지에 놓일지도 모른다. 리팩터링은 천천히, 신중하게, 조심스럽게 진행해야 하는 작업이다. 마틴 파울러는 손해보다 이득이 큰 방향으로 리팩터링을 하기 위한 다음 몇 가지 간단한 조언을 제공한다.
- 리팩터링과 새로운 기능 추가를 동시에 하지 말라.
- 리팩터링을 시작하기 전 든든한 테스트 집합이 있는지 먼저 확인한다. 할 수 있는 한 자주 테스트를 돌려본다.
- 단계를 작게 나누어서 신중하게 작업한다.
탄탄한 회귀 테스트 집합을 유지하는 것이야말로 확신을 가지고 리팩터링하기 위한 비결이라는 것이 마틴 파울러가 지적한 요점이다.
[ 테스트하기 쉬운 코드 ]
소프트웨어를 만들 때 맨 처음부터 테스트 가능성을 만들어 넣고, 코드들을 서로 연결하기 전에 코드를 하나하나 철저하게 테스트해야만 한다.
단위 테스트
단위테스트는 각 모듈의 동작을 검증하기 위해 다른 것들과 분리시켜 놓고 테스트가 이루어진다. 모듈을 통제된 환경에서 철저하게 테스트하고 나면, 더 넓은 환경에서 그 모듈이 어떻게 행동할 것인지 더 감을 잡을 수 있을 것이다.
나중에 이런 “소프트웨어 IC”들을 모아 완결된 시스템을 구성하기 위해 조립할 때에도, 우리에게는 개별 부분이 기대대로 잘 동작할 것이라는 믿음이 있을 것이다. 조립한 다음에 시스템 전체를 하나로 보고 테스트할 때도 이 단위 테스트 기능들을 그대로 똑같이 이용할 수 있다.
계약을 잘 지키는지 테스트 해보기
테스트는 우리에게 두 가지 사실을 알려준다. 하나는 코드가 계약을 지키는지 여부고, 다른 하나는 코드로 표현된 계약의 의미가 우리가 생각한 것과 일치하는지 여부다.
단위 테스트 작성하기
단위 테스트는 찾기 편한 곳에 위치해야 한다. 테스트 코드를 쉽게 접근할 수 있게 해놓은 것은 다른 개발자들에게 매우 귀중한 두 가지 자원을 제공하는 것이다.
- 모듈의 기능을 어떻게 이용해야 하는지 보여주는 예제
- 코드 변경시 검증하기 위한 회귀 테스트를 구축할 수 있는 수단
테스트 문화
여러분이 작성하는 모든 소프트웨어는 언젠가는 테스트된다. 여러분이나 팀이 테스트하지 않으면, 결과적으로 사용자들이 테스트하게 된다. 따라서 소프트웨어를 철저하게 테스트할 계획을 세우는 것이 좋다. 약간의 선견지명으로 유지보수 비용과 고객 지원실에 걸려오는 전화 횟수를 줄이는데 장족의 발전을 이룰 수 있다.
“소프트웨어를 테스트하라. 그렇지 않으면 사용자가 테스트하게 될 것이다.”
[ 사악한 마법사 ]
마법사를 사용한다고 평범한 개발자가 자동으로 전문가가 되지는 않는다. 물론 자신이 꽤 잘한다고 느낄지는 모르겠다. 방금 엄청난 양의 코드와 상당히 깔끔하게 보이는 프로그램을 완성했고, 이제 애플리케이션의 구체적인 기능을 추가하기만 하면 출시할 준비가 끝나는 샘이다.
하지만 만들어진 코드를 정말로 이해하지 못하는 한, 그건 자기 자신을 속이는 것이다. 그는 우연에 맡기는 프로그래밍을 하고 있다. 마법사가 만들어 준 코드를 변경해야 할 필요가 생긴다면, 혼자 힘으로 해야 한다.
마법사 자체에 반대하는 것은 아니다. 하지만 마법사를 사용했는데 마법사가 만들어 준 코드를 모두 이해하지 못했다면, 애플리케이션의 주인이 아니다. 그 애플리케이션을 유지보수하지도 못할 것이고, 디버깅해야 할 때 고생하게 될 것이다.
“자신이 이해하지 못하는, 마법사가 만들어 준 코드는 사용하지 말라.”
7. 프로젝트 전에
[ 요구사항의 구렁텅이 ]
많은 책과 튜토리얼들을 보면 “요구사항 수집”은 프로젝트의 초기에 이뤄지지만 현실은 그렇지 않다. 보통 요구사항은 가정과 오해, 정치의 지층들 속 깊이 묻혀져 있다.
“요구사항을 수집하지 말고 채굴하라.”
요구사항 채굴하기
요구사항은 어떤 것이 성취되어야 한다는 진술이다. 다음은 좋은 요구사항의 예이다.
- 직원 기록은 지명된 사람들만 볼 수 있다.
- 실린더헤드 온도는 임계값을 넘으면 안 되며, 이는 엔진마다 다르다.
- 에디터는 편집하는 파일의 종류에 따라 별도로 선택된 키워드를 강조 표시한다.
하지만 대개의 요구사항은 이렇게 분명하게 주어지지 않기 때문에, 요구사항 분석은 더욱 복잡해진다. 또한 정책은 수시로 바뀐다. 따라서 요구사항은 최대한 일반적 진술로 만들어야 한다.
만약 요구사항이 “인사과에서만 직원 기록을 열람할 수 있다.”는 식으로 되어 있다면 개발자는 애플리케이션이 이 파일들을 접근할 필요가 있을 때마다 그것이 가능한지 확인하는 코딩을 할 것이다. 하지만 “권한이 부여된 사용자만이 직원 기록에 접근할 수 있다”는 식으로 요구사항이 만들어졌다면 개발자는 아마도 접근 관리 시스템을 설계하고 구현할 것이다.
사용자들이 어떤 작업을 현재 어떻게 하느냐는 것을 알아채는 것보다, 왜 그것을 하는지 그 내재적 이유를 알아내는 것이 더 중요하다. 개발을 통하여 마지막에는 그들이 진술한 요구사항을 충족하는 것이 아니고, 그들의 실질적 비즈니스 문제를 해결하는 것이다.
사용자의 요구사항 내면 깊이 들어갈 수 있는 단순한 기법이 있다. 사용자가 되어 보는 것이다. 시스템이 실제로 어떻게 사용될지 통찰을 얻을 수 있을 뿐 아니라, “일하시는 데 일주일만 옆에 앉아 있어도 될까요?”라고 요청하는 것이 사용자와 신뢰를 구축하고 또 그들과의 의사소통에 기본이 될 수 있따는 점에 놀라게 될 것이다. 단 방해되지 않도록 주의하라.
“사용자처럼 생각하기 위해 사용자와 함께 일하라.”
요구사항 채굴 과정은 사용자 층이 바라는 시스템이 무엇인지 배우면서 사용자 층과 관계를 설정하기 시작하는 단계다.
요구사항 문서화
이제 여러분은 사용자와 함께 앉아서 그들의 진정한 요구사항을 캐기 시작한다. 그 애플리케이션이 해야 할 것을 묘사하는 몇 가지 그럴싸한 시나리오를 얻게 된다. 전문가라면 이것들을 기록해서 다른 사람들(개발자, 최종 사용자, 프로젝트 후원자)이 토른을 할 때 기초자료로 사용할 수 있도록 문서화하려 할 것이다.
그 문서의 독자층은 정말로 폭이 넓다.
이바 야콥슨은 요구사항을 갈무리하는 유스 케이스의 개념을 제안했다. 유스 케이스는 시스템의 특정한 사용을 설명한다. 사용자 인터페이스의 차원에서가 아니고, 좀 더 추상적인 차원에서의 얘기다.
유스 케이스를 보는 하나의 관점은 목적 지향성을 강조하는 것이다.
지나치게 자세한 명세
요구사항 문서를 만들 때 생기는 큰 위험은 지나치게 자세히 서술하는 것이다. 좋은 요구사항 문서는 추상적이다. 요구사항에 관한 한 비즈니스에 필요한 사항을 정확히 반영하는 가장 간단한 진술문이 최고다. 그렇다고 해서 모호하게 하라는 말은 아니다. 밑에 깔려 있는 의미론적 불변식을 요구사항으로 잘 갈무리하고, 구체적인 작업관행이나 현재의 작업관행을 정책으로 문서화 해야 한다.
딱 하나만 더…
불행히도 많은 프로젝트가 요구사항을 적극적으로 추적하지 않는다. 이는 범위의 변화에 대해서 보고할 길이 없다는 의미가 된다. 여기서 범위의 변화란 누가 기능을 요청했고, 누가 승인했으며, 승인된 요구사항은 몇 개인가 등이다.
요구사항 증가 관리의 핵심은, 새 기능이 일정에 미칠 영향을 프로젝트 후원자에게 인식시키는 것이다. 프로젝트가 초기 예측에서 1년 뒤쳐져 있고 비난이 오고갈 즈음이라면, 요구사항이 언제 어떻게 늘어났는지에 대해 정확하고 완전한 그림을 갖고 있는 것이 크게 도움될 것이다.
“기능 하나만 더“라는 소용돌이 속으로 빨려 들어가기는 무척 쉽다. 하지만 요구사항을 추적함으로써, “기능 하나만 더”가 실은 이번 달에 추가된 15번째 새 기능이었다는 사실을 분명히 알 수 있게 된다.
용어사전 유지하기
프로젝트 용어사전(glossary)를 만들고 유지하라. 그것은 프로젝트에서 사용되는 모든 용어와 어휘를 모아 놓은 단일한 장소여야 한다. 최종 사용자에게서 보조 직원까지 프로젝트에 참가하는 모든 사람이 일관성을 위해 동일한 용어 사전을 사용해야 한다.
사용자와 개발자가 동일한 것을 다른 이름으로 가리키는 프로젝트가 성공하기는 매우 힘들며, 같은 이름으로 다른 것을 지칭하는 상황에서는 더더욱 어렵다.
[ 불가능한 퍼즐 풀기 ]
퍼즐을 푸는 비법은 실제의 제약 조건을 알아내고, 그 속에서 해법을 찾는 것이다. 어떤 제약 조건은 절대적이지만, 다른 것들은 단순히 지례 짐작한 것들에 불과하다. 절대적 제약 조건에 대해서는, 그것이 아무리 불쾌하거나 멍청해 보이건 간에 경의를 표해야만 한다. 잔면에 그럴싸해 보이는 제약 조건들이 사실은 실질적 제약 조건이 아닐 수도 있다. 많은 소프트웨어 문제들이 이런 속임수 같은 것일지도 모른다.
자유의 정도
“생각의 틀을 벗어나라”는 유행어가 있다. 여기서 “틀”이 제약과 조건들의 경계선을 의미한다면, 우리는 그 틀을 파악해야 한다. 틀은 여러분의 생각보다는 꽤 넓을 것이다.
따라서 모든 선입견을 의심해보고 그것이 명백한 진짜 제약인지 가늠해 봐야 한다. 이것은 틀을 벗어나고 벗어나지 않고의 문제가 아니라 틀을 찾는 것, 곧 정말로 제약인 것들을 찾는 일이다.
[ 준비가 되어야만 ]
어떤 일을 뛰어나게 수행하는 사람들은 공통점이 하나 있다. 그들은 언제 시작해야 하고 언제 기다려야 하는지 안다. 만약 앉아서 키보드를 치기 시작했는데 마음 속에 어떤 의심들이 자꾸 거슬린다면 그 느낌을 따르라. 정확히 어떤 것이 문제라고 손가락으로 짚지 못하더라도, 시간을 좀 주면 여러분의 의심은 아마 좀 더 단단한 것, 대응책을 생각할 수 있는 무엇으로 구체화될 것이다. 소프트웨어 개발은 아직 과학이 아니다. 여러분의 수행 능력에 직감이 일조하도록 놓아두라.
“준비가 되었을 때 시작하라.”
[ 명세의 함정 ]
실용주의 프로그래머로서, 여러분은 요구사항의 수집, 설게, 구현을 훌륭한 시스템을 전달한다는 과정의 서로 다른 측면으로서 보아야 한다. 요구사항을 수집하고, 그 후 명세를 쓰고, 그런 다음 코딩을 시작하는 등의 모든 것이 고립되어 진행되는 환경을 믿지 마라. 명세와 구현은 단지 동일한 과정, 즉 요구사항을 포착해서 명문화하는 노력의 다른 측면일 뿐이다. 어떤 측면에서든 인공적인 경계없이 다른 측면으로 흘러갈 수 있어야 한다. 여러분은 건강한 개발 과정은 구현과 테스트에서 나온 반응이 명세화 과정으로 다시 돌아가는 것을 장려한다는 것을 깨닫게 될 것이다.
[ 동그라미와 화살표 ]
방법론이 제값을 하는가?
새로운 도구와 방법론을 채택하는 데 들어가는 비용을 절대 과소평가하지 말라. 새로운 기법을 사용하는 최초의 프로젝트를 학습 경험으로 여길 마음의 준비를 하라.
우리가 형식적 방법을 사용해야 할까?
물론이다. 하지만 형식적 방법은 단지 도구 상자 속의 또 다른 도구일 뿐이라는 사실을 늘 기억하라. 만약, 주의 깊게 분석한 후에 어떤 형식적 방법을 사용할 필요를 느꼈다면, 기꺼이 그렇게 하라.
하지만 누가 주인인지 분명히 기억해라. 절대 방법론의 노예가 되지 말라. 동그라미와 화살표들은 좋은 주인이 못된다. 실용주의 프로그래머들은 방법론을 비판적인 시각으로 바라본 다음, 각각의 방법론에서 가장 좋은 것만 뽑아 매달 점점 좋아지는 자신의 작업 실천방법의 집합 속에 녹여 넣는다. 이것이 핵심이다. 여러분은 자신의 공정을 개선하고 다듬기 위해 끊임없이 노력해야 한다. 절대로 어떤 방법론의 환고한 경계를 여러분 세계의 한계로 받아들이면 안된다.
“비싼 도구가 더 좋은 설계를 낳지는 않는다.”
8. 실용주의 프로젝트
한 프로젝트에 참여하는 사람이 한 명을 넘어서면서부터는, 기본적인 룰 몇가지를 정립하고 거기에 맞게 프로젝트의 각 부분을 위임할 필요가 있다. “실용주의 팀”에서는 실용주의 철학을 살리면서 이를 어떻게 이룰 수 있는지 보여주도록 하겠다.
[ 실용주의 팀 ]
개인이 더 나은 프로그래머가 되게끔 도와주는 실용주의 기법들은 팀에도 적용된다.
개인이 실용주의적이 되는 것에도 이득이 있지만, 그 개인이 실용주의 팀에서 일한다면 이득은 몇 배가 된다. 훌륭한 환경에서 일하고 있는 실용주의 개발자 그룹이 있기만 하다면, 그들은 자신에게 맞는 팀 동역학을 재빨리 만들어 내고 또 다음을 것이다.
깨진 창문을 없애라
품질은 팀의 이슈다. 만약 가장 부지런한 개발자라 해도 품질에 무심한 팀에 배치된다면 귀찮은 문제를 고치는 그의 열정은 줄어들 것이다. 개발자가 이런 수정 작업을 하느라 시간 보내는 것을 팀이 적극적으로 방해하고 나선다면 문제는 더욱 커진다.
팀 전체가 깨진 창문을 용납하지 않고, 품질에 대해 책임을 져야만 한다. 팀은 깨진 창문을 없애라는 철학을 이해하는 개발자들을 지원하고 그렇지 못한 개발자들은 이해하도록 격려해야 한다.
삶은 개구리
모든 사람이 적극적으로 환경 변화를 감시해야 한다. 무엇이건 간에 애초 합의사항에 있지 않았던 것들을 항상 점검하도록 하라. 새 요구사항에 대해서는 수치를 보유하라. 이미 일어난 변화를 거부할 필요는 없다. 단지 그런 일이 벌어지고 있다는 것을 알고 있으면 된다.
소통하라
팀 역시 나머지 세상과 명확히 의사소통해야 할 필요가 있는 존재다. 바깥의 사람들에게 무뚝뚝하고 과묵해 보이는 팀이야말로 최악이다. 그런 팀은 회의를 아무 체계 없이 하고, 나서서 말하는 이도 없다. 문서는 엉망진창이다. 어떤 문서도 닮은 데가 없고, 각기 다른 용어를 사용한다.
흘룽한 프로젝트 팀은 뚜렷한 특성을 갖는다. 모든 사람이 좋아할 만한 잘 준비된 퍼포먼스를 보게 될 걸 알기 때문에 사람들은 회의를 기대한다. 그들이 생산해 내는 문서는 깔끔하고 정확하며 일관적이다. 팀은 한 목소리로 이야기한다. 심지어 유머 감각도 있을 것이다.
팀이 하나로서 의사소통하게 도와주는 간단한 마케팅 비결이 있다. 프로젝트를 시작할 때 이름을 지어주는 것이다. 유별난 이름이라면 더 좋겠다. 바보같이 들리지만, 팀은 정체성 확립의 기반을 얻을 것이고, 세상은 기억할 만한 뭔가를 얻게 될 것이다.
반복하지 마라
중복은 노력을 낭비하게 하고, 결국 유지보수의 악몽을 끌어들일 수도 있다. 여기에는 훌륭한 의사소통이 분명 도움이 된다. 하지만 그 외에도 다른 것이 필요한 경우가 있다.
어떤 팀은 팀원 한 명을 프로젝트 사서로 임명하여 문서와 코드 저장고를 관리하는 책무를 맡긴다. 다른 팀원들은 어떤 정보를 찾을 때 이 사람을 첫 번째로 찾아간다. 훌륭한 사서는 그들이 다루는 자료를 읽으면서 임박한 중복을 찾아낼 수도 있을 것이다.
직교성
프로젝트의 여러 활동(분석, 설계, 코딩, 테스팅)이 독립적으로 이루어진다는 것은 불가능하다. 따라서 팀을 기능적으로 분리하라. 사름들을 작은 팀으로 나누고, 각 팀은 최종 시스템의 특정한 기능 측면에 대해 책임지도록 한다.
팀이 잘못 조직되었음을 경고해주는 신호가 보일 때가 있다. 두 개의 서브팀이 동일한 모듈이나 클래스에 대해 작업하는 것이다.
이렇게 하면 어떤 변화가 생기더라도 전체가 영향받는 일이 없게 된다. 또한 개인 작업 간의 상호작용 횟수를 급격히 감소하게 해주며, 시간 척도를 줄이고, 품질을 향상시키며 오류를 감소시킨다. 그리고 개발자들이 더욱 헌신적인 집단이 되도록 해준다. 특정 기능에 대한 책임이 오직 자신에게 있다는 것을 알기 때문에, 자신의 산출물에 대해 주인의식을 더 많이 느낀다.
그러나 이 기법은 책임감 있는 개발자들과 강력한 프로젝트 관리가 있을 경우에만 효과가 있다. 자율적인 팀을 여럿 만들고 리더십 없이 그냥 제멋대로 내버려두면 재앙이 벌어진다. 프로젝트에는 최소한 “우두머리”가 둘 필요하다. 하나는 기술을 담당하는 수석, 나머지 하나는 관리를 담당하는 수석.
자동화
일관성과 정확성을 보장하는 훌륭한 방법은, 팀이 하는 모든 일을 자동화하는 것이다. 자동화는 모든 프로젝트팀에게 필요불가결한 요소다.
덧칠을 언제 멈출지 알아라
팀은 개인들로 이루어진다는 사실을 명심하라. 각 팀원이 자신의 방식대로 빛나게 해 주어라. 그들을 지원하기에, 그리고 프로젝트가 요구사항에 맞게 이루어지기에 딱 좋을 만큼의 구조를 제공하라.
[ 유비쿼터스 자동화 ]
빌드와 릴리스 등을 포함해 프로젝트에서 거듭 발생하는 어떤 종류의 작업이건 간에 그것은 모두 자동화되어야 한다. 이를 통해 우리는 프로젝트에서 일관성과 반복가능성을 확보할 수 있다. 수작업이란 것은 일관성을 운에 맡기므로 반복가능성을 보장받지 못하는 것이다.
[ 가차 없는 테스트 ]
개발자 대부분은 테스트를 싫어한다. 코드가 어디에서 깨지는지 무의식적으로 알고 약한 지점을 피해 다니면서 살살 테스트하려 한다. 실용주의 프로그래머들은 다르다. 지금 당장 버그를 찾도록 내몰리지만, 그 대신 나중에 다른 사람이 자기 버그를 발견하게 되는 수치를 피할 수 있다.
“일찍 테스트하고, 자주 테스트하라. 자동으로 테스트하라.”
버그가 빨리 발견될수록 고치는 비용이 적어진다. “코드 조금, 테스트 조금”은 유명한 격언이다. 우리는 제품 코드를 만드는 것과 동시에 테스트 코드를 만들어야 한다.
사실, 훌륭한 프로젝트에는 제품 코드보다도 테스트 코드가 더 많을지 모른다. 테스트 코드를 만들기 위해 소요되는 시간에는 그 노력만큼의 가치가 있다. 길게 보면 이쪽이 훨씬 더 비용이 싸다. 이 외에도 테스트를 통과했다는 것은 그 코드가 “완료되었다”고 말할 수 잇는 높은 수준의 확신을 갖게 한다.
“모든 테스트가 통과하기 전엔 코딩이 다 된게 아니다.”
우리는 프로젝트 범위에서 이루어지는 테스트의 세 가지 주요 면모를 살펴보아야 한다. 무엇을 테스트할지, 어떻게 테스트할지, 그리고 언제 테스트할지.
무엇을 테스트할지
- 단위 테스트
- 하나의 모듈을 테스트하는 코드
- 그 자체로 제대로 작동하지 않는다면, 합쳐졌을 대도 역시 제대로 작동하지 않음
- 관련 모듈이 각각의 개별 테스트를 통과하면, 시스템 전체를 통틀어 상호작용 테스트해야 함
- 통합 테스트
- 주요 서브시스템이 다른 부분과 제대로 작동하는지 보여줌
- 계약이 제대로 되어 있고 테스트가 잘 되어 있다면, 어떤 통합 문제건 쉽게 발견할 수 있음
- 단위 테스트의 확장으로, 단지 전체 서브시스템이 계약을 제대로 지키는지 테스트하는 것
- 유효성 평가와 검증
- 사용자들이 필요로 하는 것인지, 시스템의 기능적 요구사항을 충족하는지도 테스트해야 함
- 버그 없는 시스템일지라도 잘못된 질문에 답한다면 그다지 유용하지 못함
- 최종 사용자의 접근 방식에 대해, 그리고 개발자 테스트 데이터와 어떻게 다른지 봐야 함
- 자원 고갈, 에러, 그리고 복구
- 이상적인 상황 뿐만 아니라 실세계의 상황에서 어떻게 작동할지도 알아야 함
- 실세계에서 프로그램은 무한한 자원을 보장받지 못함
- 성능 테스트
- 소프트웨어가 실세계 조건에서 성능 요구사항들을 만족하는지 자문해야 함
- 초당 예상 사용자 및 접속 혹은 트랜잭션 숫자를 염두에 두어야 함
- 사용편의성 테스트
- 사용편의성 테스트는 앞서 논의했던 테스트 유형과 차이가 있음
- 실제 환경의 조건 하에서 실제 사용자들이 시행하며, 인간적 요소라는 측면에서 바라봐야 함
- 보정할 시간이 있을 때에 되도록 일찍 시행해야 하며, 전문가들을 영입해야 할 수도 있음
어떻게 테스트할까?
- 회기 테스트
- 이전 값과 현재 테스트의 출력을 비교함
- 오늘 고친 버그가 어제 작동하단 것들을 망치지 않는다고 확신할 수 있음
- 앞선 테스트들은 회귀 테스트로써 실행될 수 있음될 수 있음
- 테스트 데이터
- 테스트들을 실행할 데이터는 실세계 데이터와 합성 데이터로 구분할 수 있음
- 실세계 데이터는 현실에서 오며, 전형적인 사용자 자료임
- 합성 데이터는 어떤 통계적 조건 하에서 인공적으로 생성됨
- GUI 시스템 구동
- GUI 비중이 큰 시스템이라면 GUI 테스트도 할 수 있음
- 테스트를 테스트하기
- 완벽한 소프트웨어를 작성할 수 없기 때문에, 완벽한 테스트 역시 작성할 수 없음
- 어떤 버그를 의도적으로 생기도록 한 다음 테스트가 실패하는지 확인할 수 있음
- 이를 통해 실제로 버그가 생겼을 때 테스트가 그걸 잡아낼 것이라고 확신할 수 있음
- 철저히 테스트하기
- 테스트가 올바르다는 확신이 들고, 버그를 찾아내도 충분히 철저하게 테스트했음을 알 수는 없음
- 하지만 커버리지 분석 도구 등을 사용하면 도움을 받을 수 있음
- 이때 라인 커버리지보다 컨디션 커버리지를 확인하는 것이 좋음
언제 테스트할까
실제 제품에 들어갈 모든 코드는 나오자마자 테스트해야 한다. 또한 테스트는 대부분 자동화되어야 한다.
물론 어떤 테스트들은 그렇게 자주 실행하기 쉽지 않을 수 있다. 예를 들어 부하 테스트 같은 경우 특별한 설정과 장비, 지원이 필요할 수 있다. 만약 자동화될 수 없다면, 해당 태스크에 모든 필요 자원을 투입해서 일정표에 넣어두라. 이 테스트들이 주기적으로 일정에 따라 실행되어야 한다.
그물 조이기
현존하는 테스트의 그물을 빠져 나가는 버그가 있으면, 다음 번에는 그걸 잡아낼 수 있도록 새 테스트를 추가해야 한다. 이것은 테스트에서 가장 중요한 개념이며, 뻔한 것이고, 거의 모든 교과서에서 이렇게 하라고 말한다. 하지만 무슨 이유에서인지 대다수 프로젝트에서 지켜지지 않는다.
[ 결국은 모두 글쓰기 ]
실용주의 프로그래머들은 문서화를 전체 개발 프로세스의 필요불가결한 부분으로 포용한다. 노력을 중복하거나 시간을 낭비하지 않고, 문서를 늘 손에 닿는 가까이에 두면 문서 작성이 쉬워진다.
코드 내의 주석
주석은 어떤 트레이드 오프나 결정의 이유, 어떤 대안을 버렸는지 등 다른 곳에서 문서화할 수 없는, 바로 프로젝트에서 잘 빠져 나가는 부분들을 문서화하기 위한 완벽한 기회가 된다.
[ 위대한 유산 ]
현실적으로 프로젝트의 성공은 사용자들의 기대를 얼마나 잘 충족하는가에 따라 측정된다. 그들의 기대에 못 미치는 프로젝트는 이론적인 면에서 결과물이 얼마나 훌륭하건 간에 상관없이 실패로 간주된다.
기대를 상호 소통하기
사용자는 처음에 그들이 원하는 바에 대한 어떤 비전을 갖고 여러분을 찾아온다. 불완전하고 모순이 있거나 혹은 기술적으로 불가능할 수도 있겠지만 그 기대는 사용자의 것이며, 어떤 감정을 부과하고 있다. 그냥 무시해 버릴 수 없다.
그들의 필요에 대한 이해가 깊어질수록, 그들의 기대가 충족될 수 없는 영역이나 혹은 그들의 기대가 너무 보수적으로 보이는 영역을 발견하게 될 것이다. 이런 것들을 소통하는 것이 여러분의 역할 중 일부이기도 하다. 사용자들과 함께 일해서 장차 여러분이 어떤 것을 넘겨줄 것인지 그들이 정확히 이해하도록 하라. 그리고 개발 과정 전체에 걸쳐 그렇게 하라. 애플리케이션이 해결하기로 한 비즈니스 문제에 대해 절대로 눈을 떼지 마라.
몇몇 컨설턴트들은 이 과정을 “기대 관리”라고 부른다. 사용자가 시스템에서 무엇을 얻기를 기대해야 할지에 대해 적극적으로 제어하는 것을 일컫는 말이다.
하지만 이것은 좀 엘리트주의적인 입장이다. 우리의 역할은 사용자들의 희망을 제어하는게 아니다. 그들과 잘 협동해서, 그들이 아직 이야기하지 않은 기대까지도 포함해서, 개발 과정과 최종 전달물에 대한 공통된 이해에 도달하는 것이다.
한 계단 더
그들이 기대하는 것보다 조금만 더 해주어라. 약간의 노력을 들여 시스템에 사용자 편의를 위한 기능을 추가한다면, 관계가 두고두고 좋아질 것이다.
[ 오만과 편견 ]
실용주의 프로그래머들을 책임을 회피하지 않는다. 그 대신 도전을 수용하고 자신의 전문적 지식이 널리 알려지는 것을 기뻐한다. 만약 설계 혹은 코드에 대해 책임을 맡는다면, 스스로 자랑스러워할 만한 일을 해낸 것이다.
우리는 소유권에 대한 긍지를 보고 싶다. “내가 이걸 만들었고, 내 작품의 품질을 보증합니다.” 여러분의 서명이 품질의 보증수표로 인식되게 해야 한다. 사람들이 코드에 붙여진 여러분의 이름을 보고 그것이 튼튼하고 잘 작성되고 제대로 테스트되었으며 또 훌륭히 문서화되었을 것이라고 기대하도록 만들자.
'나의 공부방' 카테고리의 다른 글
[취업준비] 개발자 취업 또는 이직 준비 방법(합격 이력서로 살펴보는 이력서 작성법) (31) | 2024.04.16 |
---|---|
[문화] Project Oxygen, 관리자의 필요성에 대한 구글의 실험(How Google Sold its Engineers on Management) (1) | 2023.11.21 |
[Slack] JIRA 티켓 생성 워크플로우(Workflow) 만들기 (2) | 2023.11.07 |
[개발서적] 프로그래머의 뇌 핵심 내용 정리 및 요약 (9) | 2023.10.31 |
[Post Mortem] 로블록스 장애 포스트모템(Roblox Return to Service 10/28-10/31 2021) (2) | 2023.09.12 |