From 11e8ed842f73d8a0b887dd797969db8f94d9e03f Mon Sep 17 00:00:00 2001 From: ymkim97 Date: Thu, 1 May 2025 19:59:57 +0900 Subject: [PATCH 1/2] Chapter 9 ~ 13 --- .../ymkim97/chapter9~13.md | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 2025/Becoming a Better Programmer/ymkim97/chapter9~13.md diff --git a/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md b/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md new file mode 100644 index 00000000..9e8eb9b3 --- /dev/null +++ b/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md @@ -0,0 +1,158 @@ +# 더 나은 프로그래머 되는 법 +## 9장 ~ 13장 +--- +이번 스프린트에서는 11장까지는 테스트 코드에 대해서 집중하였고, 12장 ~ 13장은 코드 설계에 집중한 느낌이었습니다. + +특히 테스트 코드를 좋아하고 중요하게 여겼던 기존 생각으로 정말 재밌고 어렵지 않게 읽을 수 있었습니다. +“이진 탐색”은 보통 알고리즘에만 적용하는 것으로 생각하고 있었는데, 버그를 찾는 전략으로도 사용하고 이를 위한 Git 명령어를 알게된 것도 정말 좋은 기회였던 것 같습니다. + +저자의 두가지의 시스템에 대한 경험을 통해 설계가 얼마나 중요한지 다시 한번 생각하는 시간을 가질 수 있었습니다. + +## 9장 - 예상하지 못한 것을 예상하기 +코드 작성 시 벌어질 것으로 예상되는 상황에 대한 대비만으로는 부족하기 때문에 모든 단계에서 조금이라도 발생할 가능성이 있는 특이 사항들은 모두 고려해야 한다. + +**오류** +* 항상 오류를 고려한 코드를 작성하여 그로부터 복구할 수 있도록 해야 한다. +* 오류를 무시하지 말고 오류 상황에서도 최선을 다하도록 코드를 작성하자. + +**스레딩** +* 멀티 스레딩으로 인해 예상하지 못하는 일이 발생하는 범위가 넓어졌지만 동시 실행의 원리와 스레드 간의 결합도를 낮추는 방법을 반드시 이해해야 한다. + +**셧다운** +* 애플리케이션이 종료되면서 작업자 객체(worker object)를 파기하므로, 그 과정에서 이미 파기한 객체를 사용하면 안 된다. 목표 객체가 이미 다른 스레드에 의해 제거된 콜백을 작업 큐에 넣으면 안된다. + +> 코드 작성 시 가능한 모든 코드 경로를 고려해야 한다. 나중에 비정상적인 경우에 대응하려 하지 말라. 그렇게 할 일을 미루다 보면, 이후 그와 같은 경로 자체가 있음을 잊어버리게 되고 코드는 버그로 가득 찰 것이다. + + +## 10장 - 버그 사냥하기 +소프트웨어는 처음부터 완벽할 수 없기 때문에, 초보나 전문가나 모두 똑같은 문제로 쉽게 무너질 수 있다. +책에 쓰여있는 에르허르 데이크스트라는 *“디버깅이 소프트웨어 버그를 없애는 과정이라면, 프로그래밍은 분명 버그를 만드는 과정이다.”* 라고 한다. +그러니 버그는 어쩔 수 없이 무조건 발생하고 일상이니 피하지 말고 계속 신경써서 부딪히자. + +**경제적 우려** +* 프로그래머들이 소프트웨어를 디버깅하는 데는 연간 약 3,120억 달러에 달하는 엄청난 비용이 소요된다고 한다. +* 이것으로 볼 때 프로그래머들은 버그를 빠르고 정확하게 고쳐야한다는 사명감이 있다고 볼 수 있다. + +**대비책** +* 버그가 생기고 나서 고치는 것보다는 예방하는 편이 훨씬 낫다. +* 예방하기 위해서는 코드 검토, 페어 프로그래밍, 테스트 전략 (TDD 등), 커버리지 도구 등이 있다. +* 버그를 피하기 위해서는 너무 영리한 코드를 만들지 말자. 즉 너무 복잡한 코드를 작성하는 것을 피하자. +* 마틴 파울러: *“미련한 프로그래머는 컴퓨터가 이해할 수 있는 코드를 만들고, 좋은 프로그래머는 사람이 이해할 수 있는 코드를 만든다.”* + +**버그 잡기** +* 대부분의 버그는 금방 찾아내서 수정할 수 있지만, 어떤 것들은 정말 오랜 시간을 잡아먹기도 한다. +* 버그를 체계적으로 조사하고 특징을 잡아내야 한다. 버그 재현 과정을 가장 단순하게 하여 하나의 문제에 집중하도록 하고, 특정 환경에 영향이 있는지 등을 조사해야 한다. + +**함정 파기** +* assert나 테스트 코드를 이용해서 많은 가정을 입증하자. +* 이와 함께 로그를 이용하면 문제를 발견하고 해결한 이후 코드를 남겨놓는 확실한 방법이다. 하지만 너무 쓸데 없는 로그는 오히려 방해가 될 수 있다. + +**이진 탐색 배우기** +* 코드를 한 줄씩 단순히 따라가기보다는 일련의 사건의 시작과 끝을 확인하라. 그런 다음 문제 공간을 두 개로 나누고, 가운데 시점에서 코드가 괜찮은지 확인하라. + +**소프트웨어 고고학을 채택하라** +* 소프트웨어 고고학(Software archaeology)은 과거 이력을 바탕으로 버전 관리 시스템을 뒤져보는 방법에 대해 설명한다. +* 버그가 생성되기 이전의 가장 최근의 코드베이스 하나 선정하고 재현할 수 있는 테스트 케이스를 갖춰 어떤 코드 변경 모음으로 인해 파손이 발생했는지 찾아보자. 이때 이진 탐색 전략을 사용하는 것이 최선의 방법이다. (git bisect) +* 이것이 한꺼번에 많은 범위를 커밋하기보다는, 작게 나눠야하는 이유다. + +**테스트하고 테스트하고 테스트해라** +* 단위 테스트를 작성하는 데 시간을 투자해야 한다. 처음에 작성한 코드를 개선하고 검증해나가는 데 도움이 될 뿐만 아니라, 향후 변경에 대비한 훌륭한 조기 경보 장치의 역할을 해줄 것이다. +* 버그의 원인을 최종 결정했다면, 문제를 명백히 재현해내는 간단한 테스트 작성을 고려하고 실제로 코드를 수정하기 전에 테스트 모음에 추가하라. + +**예리한 도구에 투자하라** +* 디버깅을 위한 도구는 다양하며 이를 사용할 줄 알면 아주 효과적이다. +* 디버거 사용법을 잘 익힌 다음 필요한 때에 사용하자. + +**간접적 전략** +* *쉬어가기* - 휴식을 통해 새로운 관점을 얻을 수 있고, 더욱 신중하게 생각할 수 있기 때문에 작업을 멈추고 코드에서 떨어져 있어야 할 때를 배우는 것이 중요하다. +* *다른 사람에게 설명해보라* - 어떤 문제를 다른 사람에게 설명하다보면, 동시에 자신에게도 설명하게 되고 문제를 해결하게 된다. + +**재현할 수 없는 버그** +* 실패를 유발하는 요소들을 기록하고 더 많은 정보를 수집하자. +* 매우 힘든 문제일 경우, 테스트 팜을 세팅하여 오랫동안 가동하는 스트레스 테스트를 실행하는 방법도 있다. +* *스레드를 이용하는 코드* - 스레드는 비결정적이고 재현하기 어려운 방법으로 이리저리 휘감기며 상호 작용을 하는 만큼, 간헐적인 실패를 종종 유발한다. +* *네트워크 상호 작용* - 대부분의 코드는 로컬 저장소에 항상 접속할 수 있다고 가정되나 이는 부주의한 가정이다. 접속 실패, 간헐적인 긴 로딩 타임이 네트워크에서는 일어날 수 있다. +* *저장 장치의 다양한 속도* +* *메모리 손상* +* *전역 변수 / 싱글톤* + +## 11장 - 테스트하기 +TDD는 누군가에게는 종교와 다름 없거나, 적절한 개발 방법이나, 괜찮은 방법이나 작업에는 적용하기 어려운 것일 수 있다. +또한 그저 낭비라고 생각할 수도 있다. + +**왜 테스트하는가** +당연히 자신의 코드는 자신이 테스트해야 하지만, 그렇지 않고 출시는 경우가 있다. 문제는 QA 단계에서 발견되거나 사용자가 발견할 때까지 방치된다. + +**피드백 과정 줄이기** +소프트웨어를 제대로 만들기 위해서는 피드백은 필수며 좋은 테스트 전략은 피드백 절차를 간소화하는 것이다. +QA에게 후보 버전을 테스트하는 방법이 있으나, 새로운 하부 시스템을 만드는 방법도 있고, 이보다 더 나은 방법으로는 단위 부분(클래스, 함수 수준)으로 나누어 테스트하는 것이다. +이때 자동화된 테스트로 효율적으로 테스트를 수행할 수 있다. + +**테스트 코드 짜기** +테스트 코드를 작성하는 것에는 시간이 소모된다. +그러나 오히려 테스트 코드를 통해 코드 작성 시간을 줄일 수 있다. +실제로 여러 연구에 따르면 적절한 테스트 전략이야말로 불량 발생률을 줄일 수 있는 것으로 증명되었다. +물론 속도가 느려질 수 있으나 이는 테스트를 제대로 이해하지 못해 오류가 많이 발생하거나, 코드 구조가 경직되어 하나의 함수 일부를 고치는 데 수백만 개의 테스트를 재작성해야 하는 경우다. +그러나 이는 잘못된 테스트 코드를 작성하면 안되는 이유이며, 테스트를 하지 말아야 하는 이유가 아니다. + +**테스트 유형** +* *단위 테스트* - 가장 작은 단위의 기능에 대한 테스트를 단독으로 수행하는 것으로, 각각의 함수가 정확히 작동하는지 확인하는 것. 단독이란 그 어떤 외부에 대한 접속도 하지 않는다는 것이다. +* *통합 테스트* - 각각의 단위 모듈을 더 큰 결합체로 통합하여 작동시키는 복합 기능을 검증할 수 있다. +* *시스템 테스트* - “종단 간 테스트(end-to-end tests)”라고도 알려진 이 테스트를 통해 전체 시스템에 대한 요구 사항 명세를 확인할 수 있다. + +TDD에 대해 간략하게 레드-그린-리팩토링 주기에 대해서도 설명을 한다. +TDD가 중요 포인트는 아니고, 결국 테스트는 피드백 절차를 줄이고, 이는 좋은 품질의 소프트웨어로 이끌어 준다. + +> 코드를 작성하면서 테스트를 같이 작성하라. 테스트 작성을 미루면 그만큼 테스트 효과가 줄어들 것이다. + +> 아무리 좋은 개발자 테스트라 해도 QA 테스트를 대치할 수는 없다. + + +## 12장 - 복잡도 다루기 +자신이 짠 코드가 위대하고, 다른 사람의 코드가 복잡하다는 것은 늘 그렇지 않다는 사실을 인정해야 한다. +실제로 복잡한 어떤 일을 너무 쉽게보고 충분한 계획을 세우지 않으면 프로그래머의 발목을 잡게된다. + +책의 필자는 소프트웨어의 복잡도는 크게 세 가지 원인에서 기인하며 그중 두 가지는 블롭(blob)과 라인(line)이라고 본다. + +크기 자체는 문제가 되지 않는다. 코드를 얻허게 구성하고 배분할지가 문제다. +겉보기에만 단순해 보이는 것이 아니라, 실제로 간결해지도록 설계하려면, 각 블롭이 정확한 역할과 책임을 확실히 갖도록 해야 한다. + +복잡도는 각 블롭 안에서 커간 것이 아닌, 우리가 블롭을 서로 연결하는 과정에서 커진 것이다. +일반적으로 라인이 적을수록 소프트웨어 디자인은 간결해지고 많아질 수록 설계는 더 경직된다. + +## 13장 - 두 개의 시스템에 대한 이야기 +소프트웨어의 시스템은 마치 도시와 같다. 소프트웨어 도시의 품질은 도시 계획을 얼마나 잘 짰는지에 달려 있다. + +**지저분한 대도시** +책은 두 개의 시스템을 보면서 어떤 경험을 했는지 설명주며, 첫 번째는 “지저분한 대도시”이다. +해당 프로젝트는 이미 성숙기에 접어들었지만, 복잡한 코드베이스로 아무런 설계가 있지 않았다. + +> 소프트웨어는 건강하지 못한 회사 구조와 개발 절차로 인해 잘못 설계될 수 있다. + +나쁜 설계로 인해 추가 부분은 더 나쁜 설계로 이루어질 수 밖에 없었고, 신규 인력들은 복잡함으로 인해 파악하기 힘들었다. +응집도 또한 부족하였고 각 기능 단위와 데이터 처리 부분의 위치가 잘못되었다. +모듈 간 의존성 측면에서 대부분의 결함은 단방향이 아닌 양방향으로 이루어져 있었다. +이로 인해 하나의 컴포넌트를 조금이라도 변경하면 다른 수많은 연관 컴포넌트도 바꿔야 했다. 즉 모든 컴포넌트는 단독으로 작동하지 않았다. +그 결과 low-level 테스트, 코드 수준에서의 단위 테스트, 컴포넌트 수준에서의 통합 테스트가 불가능했다. + +> 나쁜 구조로 인한 문제는 코드 대부에 한정되지 않는다. 외부와도 연계되어 개개인과 팀, 업무 처리 과정, 일정 산정 모두에 악영향을 미친다. + +**디자인 타운** +지저분한 대도시와 동일한 기술을 기반으로 한 제품이며, 다른 방식으로 만들어졌고 내부 구조도 완전히 달랐다. +가장 중요한 기능 영역에 대해서만 초기 설계 단계부터 집중하였다. +기본적인 유지 보수를 위한 부분에 있어서도 코드를 작성하기 쉽고 응집되도록 하는 방향으로 빠르게 결정했다. +설계와 코드 작성은 모두 페어 프로그래밍 방식으로 이루어졌고, 작업이 정확히 이루어졌는지 확정하기 위해 세밀한 코드리뷰가 진행되었다. + +> 명확한 구조 설계를 통해 일관된 시스템을 구성할 수 있다. 모든 설계 결정은 전체 구조 설계의 관점에서 수행해야 한다. + +> 소프트웨어 구조는 불변의 것이 아니다. 필요하다면 변경하라. 변경 가능하게 만들려면 구조를 간결하게 유지해야 한다. 간결성을 빼앗는 변화에 저항하라. + +디자인 타운의 품질을 향상시킬 수 있게 도와준 XP 원칙은 바로 YAGNI였다. +‘Don’t do anything if You Aren’t Going to Need It”. +결정을 적절히 미루는 것은 설계에 대한 대단히 강력한 접근이었으며 상당히 자유로운 것이었다. + +> 필요해질 때까지 설계상의 결정을 미루라. 요구 사항을 파악하기 전까지 구조 설계를 하지 말라. 추측하지 말라. + +## [논의 내용] +* 이진 탐색 전략을 이용할 수 있는 Git 명령어(git bisect)를 이번에 처음 알게 되었습니다. 기능이 매우 흥미롭게 보이는데, 실제로 사용해본 경험이 있으신 분이 있는지 궁금합니다! +* 책에서는 테스트 이름을 어떻게 작성할지 어느정도 설명해주는데, 실제로 다른 분들께서는 어떤 식으로 이름을 작성하시는지 경험을 공유해보고 싶습니다. \ No newline at end of file From db9decd11636fa0bb0d995d05dd108b802da590b Mon Sep 17 00:00:00 2001 From: Youngmyung Kim <83266154+ymkim97@users.noreply.github.com> Date: Thu, 1 May 2025 20:06:02 +0900 Subject: [PATCH 2/2] Update 2025/Becoming a Better Programmer/ymkim97/chapter9~13.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- 2025/Becoming a Better Programmer/ymkim97/chapter9~13.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md b/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md index 9e8eb9b3..d5fd4cba 100644 --- a/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md +++ b/2025/Becoming a Better Programmer/ymkim97/chapter9~13.md @@ -148,7 +148,7 @@ TDD가 중요 포인트는 아니고, 결국 테스트는 피드백 절차를 > 소프트웨어 구조는 불변의 것이 아니다. 필요하다면 변경하라. 변경 가능하게 만들려면 구조를 간결하게 유지해야 한다. 간결성을 빼앗는 변화에 저항하라. 디자인 타운의 품질을 향상시킬 수 있게 도와준 XP 원칙은 바로 YAGNI였다. -‘Don’t do anything if You Aren’t Going to Need It”. +“Don’t do anything if You Aren’t Going to Need It”. 결정을 적절히 미루는 것은 설계에 대한 대단히 강력한 접근이었으며 상당히 자유로운 것이었다. > 필요해질 때까지 설계상의 결정을 미루라. 요구 사항을 파악하기 전까지 구조 설계를 하지 말라. 추측하지 말라.