'Commit Build'에 해당하는 글 1건

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
앞선 글에서 영향력 검토, 검증방법, 검증시점이 리팩토링 후 검증에 있어 필수적으로 고려해야 하는 부분이며 검증비용에도 영향을 미친다고 얘기했습니다. 이에 따라 단위 테스트를 분류 해보자면 검증방법으로써 분류될 수 있습니다. 하지만 단위 테스트의 탁월한 점은 단위 테스트가 검증방법으로써 가장 탁월한 선택 중 하나가 되면서 동시에 영향력 검토와 검증시점에도 긍정적 영향을 미친다는 점입니다. 위 세 가지 관점에서 단위 테스트를 평가해보았습니다.

첫째 단위 테스트는 영향력을 줄이는 데 도움이 됩니다. 단위 테스트를 하게 되면 자연스레 의존성에 대해 생각하게 됩니다. 어떤 대상에 대해 테스트를 하려면 해당 대상을 테스트 가능한 상태로 준비시켜야 하는데 이때 의존성을 없애고 단위 테스트로서의 초점을 좀 더 명확하게 하기 위해 의존성 중지(Dependency Breaking)를 하게 됩니다. 즉, 데이터베이스에 질의하는 부분을 담당하는 DAO(Data Access Object)를 사용하고 있다면 테스트 시 관심초점이 아닌 데이터베이스에 질의하는 부분을 실제로 수행하기보다는 해당 DAO를 데이터베이스에 성공적으로 질의한 것처럼 행동하는 가짜(Stub, Fake Object, Mock 등)로 바꾸는 것입니다. 이런 과정을 통해 개발자는 여러 의존성을 느끼게 됩니다. 너무 많은 의존성이 있다고 느끼는 것은 그만큼 연결된 곳이 많다는 증거이고, 이는 영향 범위를 다시 생각해보라는 신호가 될 수 있습니다. 만약 이 신호에 귀를 기울인다면 영향범위를 좀더 줄여볼 수 있습니다. 이것은 OOP(Object Oriented Programming)에서 강조하는 의존성 최소화(Loosed Coupling)와 같은 맥락입니다.

둘째 단위 테스트는 검증비용을 줄여줍니다. 검증비용을 줄이는 데에는 크게 두 가지 부분이 작용합니다. 단위 테스트는 매우 빠르게 수행된다는 점과 한번 작성해놓은 단위 테스트는 큰 노력 없이 다시 실행할 수 있다는 점입니다. 이 부분은 사람이 테스트를 수행하는 것과 비교할 때 좀 더 명확해집니다. 사람이 어떤 기능 10개를 테스트 할 때 하나에 1분씩 본다고 해도 10분이 걸립니다. 또한 나중에 같은 테스트를 해보려면 또 10분을 투자해야 합니다. 하지만 단위 테스트를 그렇지 않습니다.

마지막으로 단위 테스트는 검증시점을 앞당겨 줍니다. 예전에 여러 코드에 존재하는 잘못된 코드 관례(Code Convention)를 수정해야 했습니다. 이 작업을 수동으로 하기엔 작업범위가 넓었기 때문에 정규표현식을 이용하여 한꺼번에 수천 개의 파일을 수정했습니다. 수정 후 특별히 컴파일 오류가 발생하지 않았고 저는 잘 되었다고 생각하고 커밋(Commit)을 했습니다. 그런데 몇 분 후 CI서버에서 메일이 한 통 왔습니다. 내용을 보니 특정 단위 테스트가 실패했다는 메일이었습니다. 자세히 살펴보니 방금 수정하면서 오류가 새롭게 생겨 발생한 문제였습니다. 저는 바로 코드를 롤백(Rollback)했고, 정규표현식을 고쳐 다시 파일을 수정한 후 일을 마무리 지을 수 있었습니다. 만약 단위 테스트가 없었더라면 어떤 결과가 일어났을까요? 코드 관례에 관계 되어 다소 부담이 적은 수정이었고 컴파일 오류 또한 발생하지 않았기 때문에 십중팔구 그냥 모르고 지나갔을 것입니다. 게다가 너무 광범위한 수정이라 QA를 받기 어려워 QA를 따로 받지 않았을 것입니다. 결국 서비스 중에 문제가 발생했을 테고 고객문의가 들어왔을 것입니다. 하지만 단위 테스트가 있었고 CI가 커밋이 되는 순간 테스트를 수행하고 실패한 경우 메일을 보내주었기 때문에 문제를 조기에 인식할 수 있었습니다.

이렇게 단위 테스트는 모든 방면에 있어 탁월함을 자랑하며 이로 인해 리팩토링에 대한 투자비용을 상당부분 줄여줍다. 다만 단위 테스트를 작성하는 데는 다소의 초기비용이 필요합니다. 하지만 리팩토링 뿐 아니라 어떤 형태로든 코드는 계속 변한다는 점을 생각해볼 때 단위 테스트를 작성해놓는 것은 계속적으로 도움을 줄 것이고 시간이 가면 갈수록 현명한 투자라고 느끼게 될 것입니다.

이어 단위 테스트는 인터페이스 변경 없이 내부코드만 변경이 있을 경우 그 영향력이 아무리 넓다 하더라도 영향력을 깨끗이 제거해줄 수 있습니다. 변경범위의 측면에서 내부코드의 변경은 인터페이스 변경과는 확연하게 구분되는 중요한 특징을 가집니다. 그것은 인터페이스 변경은 영향을 받는 곳 즉 해당 인터페이스를 사용하는 곳의 변경도 필요한 반면, 내부코드 변경의 경우는 영향 받는 곳의 코드변경은 없다는 점입니다. 이 점을 통해 알 수 있는 사실은 만약 우리가 변경되는 내부코드의 변경 전/후의 동일성을 확실히 보장한다면 내부코드 수정으로 말미암아 영향 받는 부분에 대해서는 더는 신경 쓰지 않아도 된다는 것입니다. 왜냐하면 변경이 발생한 곳은 내부코드뿐이고 내부코드의 동작은 전과 동일하기 때문입니다. 그리고 이것을 가능하게 해주는 것이 단위 테스트입니다. 그렇다면 단위 테스트를 이용하여 변경 전/후의 동일성을 확실히 보장하기 위해서는 어떻게 해야 할까요? 이 부분은 무척 어려운 주제라 생각하지만 본인의 개인적인 경험을 이야기하면 약간의 참고가 될 수 있으리라 봅니다.

저는 지난 리팩토링 프로젝트에서 리팩토링을 시작하기 전, 즉 변경 전 기준으로 단위 테스트를 신중하게 작성했습니다. 가장 먼저 주요하게 자주 실행되는 부분(주 흐름, Main Flow)에 대해서 테스트를 작성했습니다. 만약 테스트가 통과하면 예외적 흐름에 대해서도 테스트를 작성했습니다. 제가 인지하고 있는 모든 흐름을 테스트 한 후에는 커버리지(Coverage) 측정 도구를 수행해보았습니다. 커버리지 측정 도구는 아래 그림처럼 실행 된 코드 경로는 녹색으로 보여주었고 단위 테스트가 실행하지 않은 부분은 빨간색으로 보여주었습니다. 그리고 만약 빨간색 부분이 보이면 그 부분이 실행되도록 단위 테스트를 더 보강하였습니다.


그림#1. 실행 된 부분과 실행되지 않는 부분을 구분할 수 있다. 위 화면은 Undercover를 이용하여 생성했다.

이렇게 했던 이유는 단위 테스트에 의해 실행되지 않는 부분이 테스트 되지 않아 동일성 보장에 허점이 생기는 것을 방지하기 위함이었습니다. 이렇게 변경 전 기준으로 테스트 작성을 완료하고 나서야 리팩토링을 시작했습니다. 조금 고치고 테스트 수행을 해서 통과하면 다시 조금 고치는 것을 반복했습니다. 그리고 모든 리팩토링이 완료 되면 QA 부서에 리팩토링 완료를 알렸습니다. 하지만 때때로 영향력 범위 등의 문제로 QA가 검증하는 것이 합리적이지 않다고 판단하면 QA부서를 통하지 않고 리팩토링을 바로 완료시키기도 했습니다. 이것은 제가 무모해서가 아니라 단위 테스트에 의해 동일성이 보장되었다는 자신감이 있었기 때문에 가능했던 일입니다.

동일성을 보장했음에도 QA단계를 거쳤던 것은 계속적으로 강조했던 것처럼 이중검증을 해서 단위 테스트에서 동일성 보장을 하려 했음에도 실수 등으로 인해 생길 수 있는 문제를 발견하기 위해서였습니다. 이 판단은 틀리지 않았는데 아무리 신경 써서 단위 테스트를 만들어도 이후에 때로 잘못된 부분이 발생되곤 했기 때문입니다. 이로 인해 단위 테스트만으로는 부족함이 있다는 것을 깨달았으며 인수(Acceptance Testing) 테스트의 필요성을 느꼈습니다.

추가로 이렇게 많은 도움을 주는 단위 테스트를 만드는 것도 중요하지만 제가 코드 관례를 수정할 때 경험했던 것처럼 단위 테스트를 실제로 실행하여 잘못된 부분에 대한 피드백(Feedback)을 빠르게 받는 것도 매우 중요합니다. 하지만 큰 규모의 환경에서 순수한 단위 테스트와 데이터베이스 혹은 외부 네트워크와 통신하는 통합 테스트가 섞여 있을 때 로컬 개발 환경에서 무엇인가를 수정 할 때마다 모든 테스트를 수행해보는 것은 실행속도 때문에 개발자에게 부담스러운 일이 됩니다. 따라서 빠른 개발을 위해 본인이 현재 고치고 있는 부분에 대해서만 테스트를 수행하게 되는 경향이 있습니다. 하지만 내가 부분적으로 고치고 있다고 생각하는 부분이 다른 곳에도 영향을 주는 경우는 많습니다. 이런 경우 CI 서버의 커밋빌드를 활용하면 좋습니다.

유명한 CI 서버 대다수가 커밋빌드 기능을 제공합니다. 커밋빌드란 소스가 저장소에 커밋 되는 순간 CI 서버가 알아차리고 빌드와 테스트를 수행하는 기능입니다. 만약 컴파일이나 테스트가 실패하면 CI 서버는 커밋한 개발자에게 메일이나 SMS를 발송하여 실패를 알립니다. 개발자는 이런 알림을 이용하여 빠르게 문제발생을 인지할 수 있고 결국 검증시점이 앞당겨지는 것과 동일한 효과를 취하게 됩니다.

수정 이력
2010/05/07 : 이 주제가 여러 편에 걸쳐 게시되었기 때문에 내용이 중복되지 않고 읽는이가 부드러운 흐름을 타게하기 위해 내용을 다소 수정합니다.


WRITTEN BY
차민창
르세상스 엔지니어가 사회를 이끌어나가는 상상을 하며!

,