티스토리 뷰

 

훌륭한 프로그래머 되는 법

차례

Part 1. you.wirte(code);
Part 2. 연습을 통해 완벽해진다
Part 3. 개인적인 일로 받아들이기
Part 4. 일 끝내기
Part 5. 사람의 일

 

4장. 코드 줄여 개선하기

외과적 적출

미래에 필요할지도 모르는 기능이라도 코드를 제거하는 것이 안전하다. 버전 관리 시스템에서 되돌릴 수 있다.

코드 정리와 기능 변화는 별도의 커밋으로 이루어져야 한다.

마치며

가장 훌륭한 코드베이스에도 불필요한 코드는 만들어진다. 프로젝트가 클수록 더 많은 불필요한 코드가 존재한다. 이것은 실패의 징조가 아니다. 죽은 코드를 발견했음에도 아무런 조치도 취하지 않는 것이야말로 실패의 징조이다. 사용되거나 실행되지 않는 불필요한 코드를 찾아냈다면 즉시 제거해버리자.

 

10장. 버그 사냥하기

대비책

"디버깅이 코드 작성보다 두 배는 힘들다. 가능한 한도 내에서 최대로 '영리한' 코드를 작성할 경우, 정의에 따르면 디버깅하기 위해서는 2배로 영리해야 한다. 그러니 그 코드를 디버그할 만큼 똑똑할 수는 없다." - 브라이언 커니핸

"미련한 프로그래머는 컴퓨터가 이해할 수 있는 코드를 만들고, 좋은 프로그래머는 사람이 이해할 수 있는 코드를 만든다." - 마틴 파울러

 

11장. 테스트하기

"품질은 그것을 위해 큰 대가를 치를 용의가 있는 자들에게만 공짜이다." - 톰 드마르코, 팀 리스터, <피플웨어>

TDD 단계

1. 다음으로 구현해야 하는 기능을 결정한 뒤, 해당 기능에 대한 테스트를 작성하라. 물론 테스트는 실패할 것이다.
2. 테스트를 작성한 뒤 최대한 간단한 방법으로 기능을 구현하라. 테스트를 통과했다면 기능이 적절히 구현되었음을 알 수 있다. 코드를 작성하면서 여러번 테스트를 수행할게 될 것이다. 각 단계에서 작은 기능을 추가하면서 작은 테스트도 함께 추가한다. 각 테스트가 작은 만큼 실행도 빠르다.
3. 중요하지만 자주 간과되는 부분이 있다. 코드를 정리하라. 이상한 공통분모를 리팩토링하라. 테스트 대상 시스템(SUT)을 재구조화하여 더 나은 내부 구조를 만들라. 그 무엇도 잘못되지 않게 만들 수 있다는 확신을 가지고 작업해도 된다. 일련의 테스트를 통해 모두 검증할 수 있기 때문이다.
4. 첫 단계로 돌아가서, 요구사항에 대한 테스트 케이스 전체를 통과할 때까지 테스트를 반복하라.

좋은 테스트의 특징

- 짧고 명확한 이름을 가지고 있어 실패했을 때 무엇이 문제인지 쉽게 알 수 있다.
- 유지 보수가 가능하다. 작성은 물론 읽고 수정하기도 쉽다.
- 수행에 오랜 시간이 걸리지 않는다.
- 최신 구현 코드에 맞춰져 있다.
- 특별한 머신 설정이 필요 없다
- 다른 테스트에 대한 의존성이 없어서 특정 테스트 전후에 실행할 필요가 없다. 외부 상태나 코드상의 어떤 공유 변수에 대한 의존성이 없다.
- 실제 구현 코드를 테스트한다.

어떤 코드도 혼자가 아니다

글로벌 변수나 싱글턴 객체는 신뢰할 만한 테스트에 대한 저주와 같다. 숨겨진 의존성이 있다면 하나의 유닛을 쉽게 테스트할 수 없다.

 

13장. 두 개의 시스템에 대한 이야기

응집도와 결합도

응집도와 결합도는 다음과 같은 특징을 지닌 컴포넌트로 시스템을 설계하는 것을 목표로 한다.

- 강력한 응집도
응집도는 기능적으로 연관된 것끼리 얼마나 모여 있고, 하나의 모듈 내에서 내부 부분들이 얼마나 유기적으로 작동하는지에 대한 척도이다. 각 모듈은 명확하게 정의된 역할을 가져야 하며, 관련없는 기능을 마구잡이로 모아놓은 덩어리여서는 안 된다.

- 느슨한 결합도
결합도는 모듈 상호 간의 의존성에 대한 지표이다. 간결한 설계에서는 모듈 간 결합도가 낮아 상호 독립적이다. 상호 작동하는 모듈들은 직접적 혹은 간접적으로 서로에 대해 의존성을 가진다.

좋은 설계는 상호 연결 구조나 컴포넌트 간 연결의 분량을 검토한다. 시스템의 개별 부분은 단독으로 작동할 수 있어야 한다. 밀착 결합은 테스트를 하기 어렵게 만든다.

일관성

명확한 구조 설계를 통해 일관된 시스템을 구성할 수 있다. 모든 설계 결정은 전체 구조 설계의 관점에서 수행해야 한다.
명확한 구조를 통해 복제를 줄일 수 있다.

구조 확장

소프트웨어 구조는 불변의 것이 아니다. 필요하다면 변경하라. 변경 가능하게 만들려면 구조를 간결하게 유지해야 한다. 간결성을 빼앗는 변화에 저항하라.

설계 관련 결정 연기하기

필요해질 때까지 설계상의 결정을 미루라. 요구사항을 파악하기 전까지 구조 설계를 하지 말라. 추측하지 말라.

기술 부채 관리

기술 부채란 코드로 되돌아가 업데이트하라는 뜻이다. 수정하지 않은 채 코드를 내버려둔다면, 늘어나는 빚 속에 갇혀 점점 올짝달싹 못하게 되고 개발 절차도 느려지게 된다. 이는 중요하다. 낮은 코드 품질은 더 긴 개발 기간을 뜻하지만, 길게 보았을 때는 짧은 기간 안에 반드시 갚을 빚을 통해 빠르게 일을 처리할 수 있다는 말이기도 하다.

설계 방향을 잡는 테스트

훌륭한 자동화 테스트를 시스템에 적용하면 최소한의 위험만으로 근본적인 구조 변경을 수행할 수 있다. 이는 작업에 여유를 보장해준다.

유닛 테스트의 또 다른 커다란 장점은 코드 설계에 있어 훌륭한 윤곽을 그려준다는 데 있다. 각각의 작은 코드 요소는 잘 정의된 독립체로 고안하였는데, 시스템의 나머지 요소 없이도 유닛 테스트를 통과할 수 있어야 했기 때문이다. 유닛 테스트를 작성함으로써, 각 모듈이 내부적으로는 응집도가 높으면서도 시스템의 나머지 부분과는 결합도가 낮도록 만들 수 있다. 유닛 테스트에 따라 각 단위의 인터페이스를 신중하게 설계해야 하고, API는 유의미하면서도 내부적으로 일관되도록 설계해야 한다.

설계를 가지고 작업하기

팀 구성은 구성하는 코드에 피할 수 없는 영향을 미친다. 시간이 지나면, 반대로 소프트웨어의 구조가 팀이 일을 얼마나 잘 하는지에 영향을 준다. 팀이 분열되어 있다면 코드도 어색하게 엮인다. 반면 팀이 서로 긴밀하게 작업한다면 구조 역시 적절히 통합된다.

 

참고

마이클 C. 페더스, <레거시 코드 활용 전략>, 에이콘, 2008년
앤드류 헌트 외, <실용주의 프로그래머>, 인사이트, 2014년
톰 드마르코 외, <피플웨어>, 인사이트, 2014년
CppUnit Cookbook, http://cppunit.sourceforge.net/doc/lastest/cppunit_cookbook.html

 

- James Song

댓글