|
| 1 | +# 더 나은 프로그래머 되는 법 |
| 2 | +## 9장 ~ 13장 |
| 3 | +--- |
| 4 | +이번 스프린트에서는 11장까지는 테스트 코드에 대해서 집중하였고, 12장 ~ 13장은 코드 설계에 집중한 느낌이었습니다. |
| 5 | + |
| 6 | +특히 테스트 코드를 좋아하고 중요하게 여겼던 기존 생각으로 정말 재밌고 어렵지 않게 읽을 수 있었습니다. |
| 7 | +“이진 탐색”은 보통 알고리즘에만 적용하는 것으로 생각하고 있었는데, 버그를 찾는 전략으로도 사용하고 이를 위한 Git 명령어를 알게된 것도 정말 좋은 기회였던 것 같습니다. |
| 8 | + |
| 9 | +저자의 두가지의 시스템에 대한 경험을 통해 설계가 얼마나 중요한지 다시 한번 생각하는 시간을 가질 수 있었습니다. |
| 10 | + |
| 11 | +## 9장 - 예상하지 못한 것을 예상하기 |
| 12 | +코드 작성 시 벌어질 것으로 예상되는 상황에 대한 대비만으로는 부족하기 때문에 모든 단계에서 조금이라도 발생할 가능성이 있는 특이 사항들은 모두 고려해야 한다. |
| 13 | + |
| 14 | +**오류** |
| 15 | +* 항상 오류를 고려한 코드를 작성하여 그로부터 복구할 수 있도록 해야 한다. |
| 16 | +* 오류를 무시하지 말고 오류 상황에서도 최선을 다하도록 코드를 작성하자. |
| 17 | + |
| 18 | +**스레딩** |
| 19 | +* 멀티 스레딩으로 인해 예상하지 못하는 일이 발생하는 범위가 넓어졌지만 동시 실행의 원리와 스레드 간의 결합도를 낮추는 방법을 반드시 이해해야 한다. |
| 20 | + |
| 21 | +**셧다운** |
| 22 | +* 애플리케이션이 종료되면서 작업자 객체(worker object)를 파기하므로, 그 과정에서 이미 파기한 객체를 사용하면 안 된다. 목표 객체가 이미 다른 스레드에 의해 제거된 콜백을 작업 큐에 넣으면 안된다. |
| 23 | + |
| 24 | +> 코드 작성 시 가능한 모든 코드 경로를 고려해야 한다. 나중에 비정상적인 경우에 대응하려 하지 말라. 그렇게 할 일을 미루다 보면, 이후 그와 같은 경로 자체가 있음을 잊어버리게 되고 코드는 버그로 가득 찰 것이다. |
| 25 | +
|
| 26 | + |
| 27 | +## 10장 - 버그 사냥하기 |
| 28 | +소프트웨어는 처음부터 완벽할 수 없기 때문에, 초보나 전문가나 모두 똑같은 문제로 쉽게 무너질 수 있다. |
| 29 | +책에 쓰여있는 에르허르 데이크스트라는 *“디버깅이 소프트웨어 버그를 없애는 과정이라면, 프로그래밍은 분명 버그를 만드는 과정이다.”* 라고 한다. |
| 30 | +그러니 버그는 어쩔 수 없이 무조건 발생하고 일상이니 피하지 말고 계속 신경써서 부딪히자. |
| 31 | + |
| 32 | +**경제적 우려** |
| 33 | +* 프로그래머들이 소프트웨어를 디버깅하는 데는 연간 약 3,120억 달러에 달하는 엄청난 비용이 소요된다고 한다. |
| 34 | +* 이것으로 볼 때 프로그래머들은 버그를 빠르고 정확하게 고쳐야한다는 사명감이 있다고 볼 수 있다. |
| 35 | + |
| 36 | +**대비책** |
| 37 | +* 버그가 생기고 나서 고치는 것보다는 예방하는 편이 훨씬 낫다. |
| 38 | +* 예방하기 위해서는 코드 검토, 페어 프로그래밍, 테스트 전략 (TDD 등), 커버리지 도구 등이 있다. |
| 39 | +* 버그를 피하기 위해서는 너무 영리한 코드를 만들지 말자. 즉 너무 복잡한 코드를 작성하는 것을 피하자. |
| 40 | +* 마틴 파울러: *“미련한 프로그래머는 컴퓨터가 이해할 수 있는 코드를 만들고, 좋은 프로그래머는 사람이 이해할 수 있는 코드를 만든다.”* |
| 41 | + |
| 42 | +**버그 잡기** |
| 43 | +* 대부분의 버그는 금방 찾아내서 수정할 수 있지만, 어떤 것들은 정말 오랜 시간을 잡아먹기도 한다. |
| 44 | +* 버그를 체계적으로 조사하고 특징을 잡아내야 한다. 버그 재현 과정을 가장 단순하게 하여 하나의 문제에 집중하도록 하고, 특정 환경에 영향이 있는지 등을 조사해야 한다. |
| 45 | + |
| 46 | +**함정 파기** |
| 47 | +* assert나 테스트 코드를 이용해서 많은 가정을 입증하자. |
| 48 | +* 이와 함께 로그를 이용하면 문제를 발견하고 해결한 이후 코드를 남겨놓는 확실한 방법이다. 하지만 너무 쓸데 없는 로그는 오히려 방해가 될 수 있다. |
| 49 | + |
| 50 | +**이진 탐색 배우기** |
| 51 | +* 코드를 한 줄씩 단순히 따라가기보다는 일련의 사건의 시작과 끝을 확인하라. 그런 다음 문제 공간을 두 개로 나누고, 가운데 시점에서 코드가 괜찮은지 확인하라. |
| 52 | + |
| 53 | +**소프트웨어 고고학을 채택하라** |
| 54 | +* 소프트웨어 고고학(Software archaeology)은 과거 이력을 바탕으로 버전 관리 시스템을 뒤져보는 방법에 대해 설명한다. |
| 55 | +* 버그가 생성되기 이전의 가장 최근의 코드베이스 하나 선정하고 재현할 수 있는 테스트 케이스를 갖춰 어떤 코드 변경 모음으로 인해 파손이 발생했는지 찾아보자. 이때 이진 탐색 전략을 사용하는 것이 최선의 방법이다. (git bisect) |
| 56 | +* 이것이 한꺼번에 많은 범위를 커밋하기보다는, 작게 나눠야하는 이유다. |
| 57 | + |
| 58 | +**테스트하고 테스트하고 테스트해라** |
| 59 | +* 단위 테스트를 작성하는 데 시간을 투자해야 한다. 처음에 작성한 코드를 개선하고 검증해나가는 데 도움이 될 뿐만 아니라, 향후 변경에 대비한 훌륭한 조기 경보 장치의 역할을 해줄 것이다. |
| 60 | +* 버그의 원인을 최종 결정했다면, 문제를 명백히 재현해내는 간단한 테스트 작성을 고려하고 실제로 코드를 수정하기 전에 테스트 모음에 추가하라. |
| 61 | + |
| 62 | +**예리한 도구에 투자하라** |
| 63 | +* 디버깅을 위한 도구는 다양하며 이를 사용할 줄 알면 아주 효과적이다. |
| 64 | +* 디버거 사용법을 잘 익힌 다음 필요한 때에 사용하자. |
| 65 | + |
| 66 | +**간접적 전략** |
| 67 | +* *쉬어가기* - 휴식을 통해 새로운 관점을 얻을 수 있고, 더욱 신중하게 생각할 수 있기 때문에 작업을 멈추고 코드에서 떨어져 있어야 할 때를 배우는 것이 중요하다. |
| 68 | +* *다른 사람에게 설명해보라* - 어떤 문제를 다른 사람에게 설명하다보면, 동시에 자신에게도 설명하게 되고 문제를 해결하게 된다. |
| 69 | + |
| 70 | +**재현할 수 없는 버그** |
| 71 | +* 실패를 유발하는 요소들을 기록하고 더 많은 정보를 수집하자. |
| 72 | +* 매우 힘든 문제일 경우, 테스트 팜을 세팅하여 오랫동안 가동하는 스트레스 테스트를 실행하는 방법도 있다. |
| 73 | +* *스레드를 이용하는 코드* - 스레드는 비결정적이고 재현하기 어려운 방법으로 이리저리 휘감기며 상호 작용을 하는 만큼, 간헐적인 실패를 종종 유발한다. |
| 74 | +* *네트워크 상호 작용* - 대부분의 코드는 로컬 저장소에 항상 접속할 수 있다고 가정되나 이는 부주의한 가정이다. 접속 실패, 간헐적인 긴 로딩 타임이 네트워크에서는 일어날 수 있다. |
| 75 | +* *저장 장치의 다양한 속도* |
| 76 | +* *메모리 손상* |
| 77 | +* *전역 변수 / 싱글톤* |
| 78 | + |
| 79 | +## 11장 - 테스트하기 |
| 80 | +TDD는 누군가에게는 종교와 다름 없거나, 적절한 개발 방법이나, 괜찮은 방법이나 작업에는 적용하기 어려운 것일 수 있다. |
| 81 | +또한 그저 낭비라고 생각할 수도 있다. |
| 82 | + |
| 83 | +**왜 테스트하는가** |
| 84 | +당연히 자신의 코드는 자신이 테스트해야 하지만, 그렇지 않고 출시는 경우가 있다. 문제는 QA 단계에서 발견되거나 사용자가 발견할 때까지 방치된다. |
| 85 | + |
| 86 | +**피드백 과정 줄이기** |
| 87 | +소프트웨어를 제대로 만들기 위해서는 피드백은 필수며 좋은 테스트 전략은 피드백 절차를 간소화하는 것이다. |
| 88 | +QA에게 후보 버전을 테스트하는 방법이 있으나, 새로운 하부 시스템을 만드는 방법도 있고, 이보다 더 나은 방법으로는 단위 부분(클래스, 함수 수준)으로 나누어 테스트하는 것이다. |
| 89 | +이때 자동화된 테스트로 효율적으로 테스트를 수행할 수 있다. |
| 90 | + |
| 91 | +**테스트 코드 짜기** |
| 92 | +테스트 코드를 작성하는 것에는 시간이 소모된다. |
| 93 | +그러나 오히려 테스트 코드를 통해 코드 작성 시간을 줄일 수 있다. |
| 94 | +실제로 여러 연구에 따르면 적절한 테스트 전략이야말로 불량 발생률을 줄일 수 있는 것으로 증명되었다. |
| 95 | +물론 속도가 느려질 수 있으나 이는 테스트를 제대로 이해하지 못해 오류가 많이 발생하거나, 코드 구조가 경직되어 하나의 함수 일부를 고치는 데 수백만 개의 테스트를 재작성해야 하는 경우다. |
| 96 | +그러나 이는 잘못된 테스트 코드를 작성하면 안되는 이유이며, 테스트를 하지 말아야 하는 이유가 아니다. |
| 97 | + |
| 98 | +**테스트 유형** |
| 99 | +* *단위 테스트* - 가장 작은 단위의 기능에 대한 테스트를 단독으로 수행하는 것으로, 각각의 함수가 정확히 작동하는지 확인하는 것. 단독이란 그 어떤 외부에 대한 접속도 하지 않는다는 것이다. |
| 100 | +* *통합 테스트* - 각각의 단위 모듈을 더 큰 결합체로 통합하여 작동시키는 복합 기능을 검증할 수 있다. |
| 101 | +* *시스템 테스트* - “종단 간 테스트(end-to-end tests)”라고도 알려진 이 테스트를 통해 전체 시스템에 대한 요구 사항 명세를 확인할 수 있다. |
| 102 | + |
| 103 | +TDD에 대해 간략하게 레드-그린-리팩토링 주기에 대해서도 설명을 한다. |
| 104 | +TDD가 중요 포인트는 아니고, 결국 테스트는 피드백 절차를 줄이고, 이는 좋은 품질의 소프트웨어로 이끌어 준다. |
| 105 | + |
| 106 | +> 코드를 작성하면서 테스트를 같이 작성하라. 테스트 작성을 미루면 그만큼 테스트 효과가 줄어들 것이다. |
| 107 | +
|
| 108 | +> 아무리 좋은 개발자 테스트라 해도 QA 테스트를 대치할 수는 없다. |
| 109 | +
|
| 110 | + |
| 111 | +## 12장 - 복잡도 다루기 |
| 112 | +자신이 짠 코드가 위대하고, 다른 사람의 코드가 복잡하다는 것은 늘 그렇지 않다는 사실을 인정해야 한다. |
| 113 | +실제로 복잡한 어떤 일을 너무 쉽게보고 충분한 계획을 세우지 않으면 프로그래머의 발목을 잡게된다. |
| 114 | + |
| 115 | +책의 필자는 소프트웨어의 복잡도는 크게 세 가지 원인에서 기인하며 그중 두 가지는 블롭(blob)과 라인(line)이라고 본다. |
| 116 | + |
| 117 | +크기 자체는 문제가 되지 않는다. 코드를 얻허게 구성하고 배분할지가 문제다. |
| 118 | +겉보기에만 단순해 보이는 것이 아니라, 실제로 간결해지도록 설계하려면, 각 블롭이 정확한 역할과 책임을 확실히 갖도록 해야 한다. |
| 119 | + |
| 120 | +복잡도는 각 블롭 안에서 커간 것이 아닌, 우리가 블롭을 서로 연결하는 과정에서 커진 것이다. |
| 121 | +일반적으로 라인이 적을수록 소프트웨어 디자인은 간결해지고 많아질 수록 설계는 더 경직된다. |
| 122 | + |
| 123 | +## 13장 - 두 개의 시스템에 대한 이야기 |
| 124 | +소프트웨어의 시스템은 마치 도시와 같다. 소프트웨어 도시의 품질은 도시 계획을 얼마나 잘 짰는지에 달려 있다. |
| 125 | + |
| 126 | +**지저분한 대도시** |
| 127 | +책은 두 개의 시스템을 보면서 어떤 경험을 했는지 설명주며, 첫 번째는 “지저분한 대도시”이다. |
| 128 | +해당 프로젝트는 이미 성숙기에 접어들었지만, 복잡한 코드베이스로 아무런 설계가 있지 않았다. |
| 129 | + |
| 130 | +> 소프트웨어는 건강하지 못한 회사 구조와 개발 절차로 인해 잘못 설계될 수 있다. |
| 131 | +
|
| 132 | +나쁜 설계로 인해 추가 부분은 더 나쁜 설계로 이루어질 수 밖에 없었고, 신규 인력들은 복잡함으로 인해 파악하기 힘들었다. |
| 133 | +응집도 또한 부족하였고 각 기능 단위와 데이터 처리 부분의 위치가 잘못되었다. |
| 134 | +모듈 간 의존성 측면에서 대부분의 결함은 단방향이 아닌 양방향으로 이루어져 있었다. |
| 135 | +이로 인해 하나의 컴포넌트를 조금이라도 변경하면 다른 수많은 연관 컴포넌트도 바꿔야 했다. 즉 모든 컴포넌트는 단독으로 작동하지 않았다. |
| 136 | +그 결과 low-level 테스트, 코드 수준에서의 단위 테스트, 컴포넌트 수준에서의 통합 테스트가 불가능했다. |
| 137 | + |
| 138 | +> 나쁜 구조로 인한 문제는 코드 대부에 한정되지 않는다. 외부와도 연계되어 개개인과 팀, 업무 처리 과정, 일정 산정 모두에 악영향을 미친다. |
| 139 | +
|
| 140 | +**디자인 타운** |
| 141 | +지저분한 대도시와 동일한 기술을 기반으로 한 제품이며, 다른 방식으로 만들어졌고 내부 구조도 완전히 달랐다. |
| 142 | +가장 중요한 기능 영역에 대해서만 초기 설계 단계부터 집중하였다. |
| 143 | +기본적인 유지 보수를 위한 부분에 있어서도 코드를 작성하기 쉽고 응집되도록 하는 방향으로 빠르게 결정했다. |
| 144 | +설계와 코드 작성은 모두 페어 프로그래밍 방식으로 이루어졌고, 작업이 정확히 이루어졌는지 확정하기 위해 세밀한 코드리뷰가 진행되었다. |
| 145 | + |
| 146 | +> 명확한 구조 설계를 통해 일관된 시스템을 구성할 수 있다. 모든 설계 결정은 전체 구조 설계의 관점에서 수행해야 한다. |
| 147 | +
|
| 148 | +> 소프트웨어 구조는 불변의 것이 아니다. 필요하다면 변경하라. 변경 가능하게 만들려면 구조를 간결하게 유지해야 한다. 간결성을 빼앗는 변화에 저항하라. |
| 149 | +
|
| 150 | +디자인 타운의 품질을 향상시킬 수 있게 도와준 XP 원칙은 바로 YAGNI였다. |
| 151 | +“Don’t do anything if You Aren’t Going to Need It”. |
| 152 | +결정을 적절히 미루는 것은 설계에 대한 대단히 강력한 접근이었으며 상당히 자유로운 것이었다. |
| 153 | + |
| 154 | +> 필요해질 때까지 설계상의 결정을 미루라. 요구 사항을 파악하기 전까지 구조 설계를 하지 말라. 추측하지 말라. |
| 155 | +
|
| 156 | +## [논의 내용] |
| 157 | +* 이진 탐색 전략을 이용할 수 있는 Git 명령어(git bisect)를 이번에 처음 알게 되었습니다. 기능이 매우 흥미롭게 보이는데, 실제로 사용해본 경험이 있으신 분이 있는지 궁금합니다! |
| 158 | +* 책에서는 테스트 이름을 어떻게 작성할지 어느정도 설명해주는데, 실제로 다른 분들께서는 어떤 식으로 이름을 작성하시는지 경험을 공유해보고 싶습니다. |
0 commit comments