개발자라면 꼭 알아야 하는 지식 #2 TDD와 CI

ednadev·2020년 9월 15일
0

디버깅

개발자가 되면 일하는 시간의 90%가 왜 코드가 작동하지 않는지 고민하다가 지나갈 것이다.

디버깅이란 코드 베이스에서 문제의 근원을 찾아서 그 문제를 일으킬 만한 요인을 가려내고, 여러 가설을 시험해보는 과정을 통해 뿌리가 되는 진정한 원인을 찾고 그 원인을 제거한 후, 다시는 그 문제가 일어나지 않도록 하는 것이다.

즉, 버그를 수정하는 과정이다.

  • 디버거는 최후의 수단으로 남겨둬야 한다.
    디버거에 손도 대기 전에 해결 할 수 있는 버그가 많다.

  • 디버깅하기 전에 버그부터 재현한다. 버그를 재현해서 진짜 버그가 발생한 게 맞는지 확인부터 한다. 재현할 수 없는 문제는 디버깅 할 수 없다.

  • 문제를 일으킬 만한 원인이 무엇인지 생각한다. 시스템이 어떻게 작동하는지, 무엇이 지금 보고 있는 이상을 일으켰을지 생각한다.

  • 자신이 세운 가설을 테스트할 단위 테스트를 작성한다. 하나씩 통과할 대마다 가능성을 제거한다.

  • 디버깅이 잘 안 풀릴 때, 문제를 반으로 자를 방법을 찾아보는 것도 좋다. 아니면 최대한 큰 덩어리를 떼어내는 것도 좋다.

  • 문제를 고쳤다면 애초에 문제를 일으킨 원인이 정확히 무엇이었는지, 어떻게 고친 것인지 제대로 이해한다. 마구잡이로 시스템을 수정하고 코드 여기저기를 대충 손보다가는 온갖 문제를 일으킬 수 있다.

테스트

테스트의 핵심 목표는 위험 부담을 줄이는 것이다. 소프트웨어를 사용할 고객에게 가장 큰 영향을 미칠 문제를 사전에 찾아 제거함으로써 위험을 감소시키는 것이 목표다.

단위 테스트

코드를 작은 단위로 실행하는 테스트를 작성하는 것

단위 테스트는 특정한 코드의 단위에 특정 상황에서 특정 입력을 넣을 때 어떤 결과가 출력되어야 하는지 명시한다. 진정한 단위 테스트는 대부분의 프로그래밍 언어에서 최소로 작은 코드 단위인 클래스를 테스트한다.

단위 테스트 뿐 아니라 모든 테스트의 마지막에는 테스트 통과 여부를 결정하는 확인 장치가 있어야 한다. 단언문이 바로 이러한 확인 장치다.

테스트 주도 개발 (TDD)

TDD의 기본 개념은 코드를 작성하기 전에 단위 테스트부터 작성해서 그 코드가 해야 할 일을 명확하게 정의한다. 단위 테스트가 코드 개발을 '주도'한다.

소스 제어

개발 프로젝트의 소스 코드를 잘 관리할 수 있게 도와주는 것이다.

  • 저장소 : 코드를 저장해두는 장소. 소스 코드를 쓸 때는 저장소에서 코드를 가져와서 작업한 후 수정 사항을 체크인한다.

  • 코드 체크아웃 : 로컬 버전 코드를 가져와서 수정하려면 저장소에서 코드를 체크아웃해야 한다. 체크아웃한 코드가 로컬 버전이 되고 자신이 만든 수정 사항은 자신의 컴퓨터나 로컬 저장소에만 저장된다.

  • 리비전 : 소스 제어에 저장된 파일의 이전 버전. 코드를 망쳤을 경우 이전 리비전으로 되돌리거나 그 리비전을 체크아웃한다. 리비전 기록을 살펴보고 시간이 지남에 따라 파일이 어떻게 변화하는지 수정 사항을 비교해볼 수 있다.

  • 분기 : 대부분의 소스 제어 시스템은 기존의 코드 베이스에서 브랜치를 만들어 트렁크로부터 독립적으로 진화한 새로운 코드 베이스를 만들 수 있게 해준다.

  • 병합 : 어떤 코드에 있는 변경 사항을 다른 코드에 합치는 것을 가리킨다.

  • 충돌 : 병합 도중 충돌이 발생한다. 같은 파일의 같은 부분을 수정한다면 결국 수작업으로 두 변경 사항을 조정해야 한다. 컴퓨터는 어떤 수정 사항이 우선인지 모른다.

중앙 집중형 소스 제어 (CVS, 서브버전)

중앙 서버에는 한 개의 저장소가 있다. 그 코드를 가지고 작업하는 모든 개발자가 그 저장소를 통해 필요한 파일의 사본을 구하고 수정 사항을 체크인한다. 각 개발자에게는 중앙 저장소에 코드를 체크인, 체크아웃하는 것을 관리하는 소스 제어 클라이언트가 있다. 파일의 모든 버전 기록과 리비전은 이 중앙 저장소에 저장된다.

  1. 작업 중인 코드의 로컬 사본을 저장소로부터 업데이트하기
  2. 수정하기
  3. 중앙 저장소에 수정 사항 커밋하기 (그리고 충돌 사항 해결하기)

분산 제어형 소스 제어 (깃, 머큐리얼)

각 개발자가 자신의 컴퓨터에 전체 저장소의 완전한 사본을 가지고 있다. 프로젝트의 저장 시스템 역할을 하는 저장소 중앙 버전 혹은 마스터 저장소는 있어야 한다.

로컬 저장소에 수정 사항을 내려받고 자신이 수정한 사항을 마스터 저장소를 비롯한 다른 아무 저장소에나 푸시하면 된다.

지속적 통합 (CI)

소프트웨어를 빌드하고 이를 배포하기 위해 테스트하고 패키징하는 과정은 느리고 고통스럽고 따분한 데다 에러도 많이 난다. CI는 이 과정을 자동화해준다.

처음 한 번만 체크인해두면 단위 테스트, 정적 코드 분석기 같은 코드 품질 평가 실행이 동시에 이루어지는 수준까지 진화했다. 개발자들이 각기 작업 중인 코드를 더 자주 병합시키기 때문에 병합 지옥에 빠지지 않게 막아준다.

작업 흐름

  • 코드 체크인 : 메인 저장소에 코드를 체크인하기 전에 로컬 컴퓨터에서 빌드를 실행하고 모든 단위 테스트를 실행해야 한다.

  • 새 빌드 시작 : 빌드 서버에 있는 CI 소프트웨어가 모니터링하던 소스 제어 브랜치의 변화를 막 감지한다. CI 서버가 새로운 빌드를 시작한다.

  • 코드 체크아웃 : 최신 수정 사항부터 가져온다. 자신이 넣은 코드를 비롯해 브랜치에 일어난 모든 변화를 내려받고 작업 디렉터리에 넣는다.

  • 코드 컴파일 : 빌드 스크립트가 실제로 코드를 컴파일하고 빌드하기 시작하는 시점. 빌드 스크립트는 소스 코드 빌드 명령을 실행한다. 외부 라이브러리를 비롯해 코드 컴파일에 필요한 모든 것을 링크한다.

  • 정적 분석기 실행 : 코드가 제대로 빌드되었다면 정적 분석기가 실행되어서 코드 품질을 측정한다. 코드를 살펴보고 발생할 것 같은 에러나 모범 사례 위반을 찾는 도구라고 생각하면 된다.

  • 단위 테스트 실행 : 컴파일된 코드를 대상으로 단위 테스트가 실행되고 나중을 위해 결과는 기록된다.

  • 결과 보고 : 빌드의 결과가 보고될 시점. 보고서에는 빌드가 성공했는지 실패했는지, 실행하는 데 시간이 얼마나 거렸는지를 비롯해 코드 품질 기준, 단위 테스트 등에 관한 정보가 담긴다. 문서도 아마 빌드에 의해 자동으로 생성될 것이다.

  • 소프트웨어 패키징 : 빌드 소프트웨어는 배포하거나 설치할 수 있는 형태로 패키징된다. 여기에는 컴파일된 코드와 더불어 외부 리소스나 의존성을 어떤 구조로 묶어서 배포 또는 설치가 가능한 형태로 만드는 과정이 포함된다.

  • 지속적 배포 : 테스트 목적으로 코드를 특정 환경에 직접 배포해보는 것이다.

CI 소프트웨어

CI 소프트웨어가 없다면 맞춤 스크립트를 써야 한다.
그 말은 빌드 서버를 직접 프로그래밍해야 한다는 뜻이다.

CI 소프트웨어는 지속적 통합의 일반적인 작업 대부분을 자동화해준다. 대부분의 CI 소프트웨어는 매우 유사한 방식으로 작동하며 앞에서 설명한 작업 흐름을 쉽게 수행할 수 있도록 해준다. 시중에는 상당수의 CI 서버와 소프트웨어가 나와 있다.

  • 젠킨스 : 원래는 자바 환경에서 CI를 할 수 있게 만들어진 자바 프로그램이었다. 하지만 사용하기 쉬운 데다 유명세까지 얻으면서 다른 대부분의 기술에서도 쓸 수 있게 되었다. 자체적으로 웹서버를 갖추고 있어서 실행하기 정말 쉽고 플러그인도 많다.

  • 허드슨 : 젠킨스가 등장하기 전 허드슨이 있었다. 허드슨은 오라클이 관리한다.

  • 트래비스 CI : 호스팅 또는 서비스 형태로 제공된다. 사용하려면 가입 해야 한다. 깃허브에 호스팅된 프로젝트에서 CI를 실행할 수 있도록 만든 소프트웨어로 설계도 훌륭하고 사용하기도 쉽다. 게다가 빌드 서버를 유지 보수할 필요가 없어져서 좋다.

  • TFS : 마이크로소프트 개발 도구를 사요앟는 업체에서 TFS를 사용한다.

  • 팀시티 : 젯브레인스라는 영리회사가 만들었다. 무료 버전이 있긴 하지만 이 또한 라이선스 제품이다.

0개의 댓글