diff --git a/2025/Becoming a Better Programmer/aquamagic9/Chapter9~13.md b/2025/Becoming a Better Programmer/aquamagic9/Chapter9~13.md new file mode 100644 index 00000000..abca56ef --- /dev/null +++ b/2025/Becoming a Better Programmer/aquamagic9/Chapter9~13.md @@ -0,0 +1,206 @@ +### 논의사항 +재현 가능성이 적었던 버그를 마주했던 경험이 존재하나요? 어떤 경험이였고 어떻게 해결할 수 있었는지 방법을 이야기해보면 좋을 것 같습니다. + +저 같은 경우는 완벽한 원인을 찾지 못하여 버그를 보완하는 방향으로 해결을 했는데 플레이어가 오른쪽으로 눕혀진 스프링과 상호작용을 할 때 간헐적으로 위치에 따라 바닥과의 마찰(?)에 의해 튕김이 먹히는 경우가 있었고 스프링 튕기는 힘이 오른쪽 위로 향하도록 조정을 하여 해결 했던 기억이 있습니다. 플레이 테스트를 많이 해보면서 분석하느라 시간을 많이 소비하였습니다. +# 더 나은 프로그래머 되는 법 9~13장 +## Ch09 예상하지 못한 것을 예상하기 +**예상하기** +코드 작성시 벌어질 것으로 예상되는 상황에 대한 대비만으로는 부족하다. 모든 단계에서 조금이라도 발생 가능성 요소를 모두 찾아보아야 한다. + +항상 오류를 고려한 코드를 작성하여 그로부터 복구할 수 있도록 하라. 복구하지 못할 오류 역시 고려하라. 오류 상황에서도 최선을 다하도록 코드를 작성하라. + +예측 불가능한 수준을 다루기 위해서는 기본적인 동시 실행의 원리와 스레드 간의 결합도를 완화하여 위험하게 상호작용하지 않도록 해야 한다. + + +**생각해보기** +1. '예상치 못한' 상황을 적절히 다루지 못한 코드에서 어떤 문제들을 발견하였는가? + 나중에는 엄청나게 오류가 커졌고 제거하기 어려운 상태까지 놓였던 순간이 있었다. +2. 모든 코드는 자신 속에 항상 견고한 오류 처리를 포함하고 있는가? + 항상 그렇지 않았다. 오류가 발생하는 것을 실행 중에 알 수 있게 해놓은 경우도 있다. 하지만 이번 파트를 읽으면서 모든 코드에 오류를 예상할 수 있도록 노력하고 오류처리를 해놓아야 한다는 점을 명확히 인지하였다. +3. 어떤 상황에서 엄격한 오류 처리를 포기할 수 있는가? + 책의 예시처럼 얼랭과 같은 언어에서는 실패하도록 두라 라는 철학이 있다. 눈에 띄는 실패상황을 만들어 프로세스 수준에서 오류가 처리되도록 하는 것을 권장하는 경우 엄격한 오류 처리를 포기한다고 생각한다. +4. 코드의 품질과 견고함에 영향을 줄 수 있는 다른 놀라운 시나리오로는 어떤 것이 있다고 생각하는가? + +## Ch10 버그 사냥하기 +**버그 대처하기** +버그를 피하지 말라. 더 많은 디버깅을 하게 될 것이고 점차 익숙해질 것이며 잘해낼 것이다. + +버그가 생긴 뒤에 처리하는 것이 아닌 버그가 생기지 않도록 예방책을 단단히 해놓아야 한다. 코드 검토, 페어 프로그래밍, 테스트 전략은 중요한 방법론들이다. + +마틴 파울러는 "미련한 프로그래머는 컴퓨터가 이해할 수 있는 코드를 만들고, 좋은 프로그래머는 사람이 이해할 수 있는 코드를 만든다." 라고 이야기 한다. + + +**버그 잡기** +재현 가능성이 얼마나 되는가, 버그를 발견한 시점과 소프트웨어 구현 간 시간 간격이 얼마나 되는가 이 두 가지가 해당 버그를 해결하기 얼마나 어려운지를 나타낼 수 있는 척도이다. + +버그를 체계적으로 조사하고 특징을 잡아야 한다. 버그 재현 과정을 가능한 한 가장 단순하게 줄여야 한다. 단 하나의 문제에 집중할 수 있도록 한다. 해당 문제를 얼마나 반복할 수 있는지 알아봐야 한다. + +일련의 사건의 시작과 끝을 확인하여 이진 탐색과 같은 방법으로 버그를 탐색하면 효율적이다. + +소프트웨어를 개발했다면 단위 테스트를 작성하는데 시간을 투자하라. 이는 향후 조기 경보 장치의 역할을 해준다. + +일렉트릭 펜스와 같은 메모리 확인 도구나 발그라인드와 같은 도구들에 익숙해지면 버그를 잡기 훨씬 효과적이다. + + +**간접적 전략** +가끔은 문제와 몇시간 동안 씨름을 하다가도 아무것도 얻지 못하는 경우도 있다. 이럴 경우에는 잠시 작업을 멈추고 코드와 떨어져 있는 시간을 보내야 한다. 혹은 문제를 다른 사람에게 설명해보는 과정을 통해 문제가 어디에 있는지 알아차릴 수도 있다. + + +**재현할 수 없는 버그** +때로는 재현할 수 없는 버그를 발견하기도 한다. 이런 버그를 해결하기 위해서는 실패를 유발하는 요소들을 기록해두자. 더 많은 로그와 assert를 추가해 사용자의 정보를 획득하자. 매우 힘든 문제인 경우 테스트 팜을 세팅하자. + + +**생각해보기** +1. 자신이 얼마나 많은 시간을 디버깅에 할애하는지 평가해보라. 시스템에 새로운 코드를 작성하지 않는 모든 활동도 고려하라. + 버그가 발생한다면 버그 발생한 지점에 대해서 디버깅을 실시한다. 생각보다 디버깅을 하는데 시간을 많이 사용하지는 않았던 것 같다. +2. 자신이 작성한 새로운 코드에 디버깅 시간을 더 많이 할애하는가, 아니면 기존 코드를 조정하는 데 더 많은 시간을 할애하는가? + 기존 코드를 조정하는데 더 많은 시간을 할애하는 것 같다. 디버깅의 경우 버그가 발생한 경우 사용하며 시간이 많이 걸리는 경우도 있고 짧게 걸리는 경우도 있다. +3. 기존 코드를 위한 단위 테스트들은 디버깅 시간에 변화를 주는가, 아니면 디버깅 방법에 변화를 주는가? + 디버깅 방법에 변화를 주는 것이라고 생각한다. +4. 버그 없는 소프트웨어를 목표로 삼는 것은 현실적인가? 이것은 실현 가능한가? 버그 없는 소프트웨어를 진짜 목표로 삼는 때는 언제가 적절한가? 소프트웨어에서 버그의 양을 결정하는 요소는 무엇인가? + 목표로 삼아야 한다고 생각한다. 목표와 최대한 가깝게 추구할 수록 버그를 최대한 많이 퇴치 할 수 있다. 실현 불가능한 경우도 존재한다고 생각하지만 결국 버그 없는 소프트웨어를 지향함으로써 얻을 수 있는 것들이 많다. 소프트웨어에서 버그의 양을 결정하는 요소는 버그의 예방책이 없을 경우라고 생각된다. + +## Ch11 테스트하기 +**왜 테스트 하는가** +자신의 코드는 자신이 테스트해야 한다. 좋은 테스트 전략이란 피드백 절차를 간소화하는 것이다. 이를 통해 더 효율적으로 일할 수 있다. + +소프트웨어 개발 과정을 개선하려면 빠른 피드백을 통해 문제 파악에 소모되는 시간을 줄여야 한다. 자동화된 테스트를 통해 피드백 과정을 줄이면 코드 개발 과정에도 도움이 되고 재활용도 가능하다. + + +**테스트 유형** +단위 테스트란 가장 작은 단위의 기능에 대한 테스트를 단독으로 수행하는 것으로, 각각의 함수가 정확히 작동하는지 확인하기 위한 것이다. + +통합 테스트를 통해 각각의 단위 모듈을 더 큰 결합체로 통합하여 작동시키는 복합 기능을 검증할 수 있다. 통합 테스트는 보통 단위 테스트 코드 작성 시 사용하는 것과 같은 프레임워크를 사용하여 작성한다. + +시스템 테스트는 외부 노출 API나 시스템 시작점에 대해 실행되는 코드로 작성될 수 있다. 혹은 셀레니움 같은 웹 브라우저 자동화 도구를 통해 시스템 외부에서 수행될 수도 있다. 시스템 테스트는 범위가 넓은 탓에 전체 테스트를 수행하는 데 많은 시간이 걸릴 수 있다. + + +**TDD의 단계** +1. 다음으로 구현해야 하는 기능을 결정한 뒤, 해당 기능에 대한 테스트를 작성하라. 물론 테스트는 실패할 것이다. +2. 테스트를 작성한 뒤 최대한 간단한 방법으로 기능을 구현하라. 테스트를 통과했다면 기능이 적절히 구현되었음을 알 수 있다. 코드를 작성하면서 여러 번 테스트를 수행하게 될 것이다. 각 단계에서 작은 기능을 추가하면서 작은 테스트도 함께 추가한다. 각 테스트가 작은 만큼 실행도 빠르다. +3. 중요하지만 자주 간과되는 부분이 있다. 코드를 정리히라. 이상한 공통분모를 리팩터링하라. 테스트 대상 시스템을 재구조화하여 더 나은 내부 구조로 만들라. 그 무엇도 잘못되지 않게 만들 수 있다는 확신을 가지고 작업해도 된다. 일련의 테스트를 통해 모두 검증할 수 있기 때문이다. +4. 첫 단계로 돌아가서, 요구 사항에 대한 테스트 케이스 전체를 통과할 때까지 테스트를 반복하라. + + +**좋은 테스트** ++ 짧고 명확한 이름, 실패했을 때 무엇이 문제인지 알 수 있다. ++ 유지보수가 가능. 작성은 물론 읽고 수정하기도 쉽다. ++ 수행에 오랜 시간이 걸리지 않는다. ++ 최신 구현 코드에 맞춰져 있다. ++ 특별한 머신 설정이 필요 없다. ++ 다른 테스트에 의존성이 없어서 특정 테스트 전후에 실행할 필요가 없다. ++ 실제 구현 코드를 테스트 한다. + + +**나쁜 테스트** ++ 때로는 성공하고 때로는 실패하는 테스트 ++ 이상해 보이고, 읽거나 수정하기 힘든 테스트 ++ 지나치게 큰 테스트 ++ 하나의 테스트 케이스에서 둘 이상을 수행하는 테스트 ++ 클래스 API에 대해 개별 행태를 확인하는 것이 아닌 함수마다 공격을 하는 테스트 ++ 직접 작성하지 않은 서드파티 코드에 대한 테스트 ++ 지나치게 상세하여 중구난방인 테스트 ++ SUT의 내부 구현에 대한 지식을 바탕으로 한 화이트박스식 테스트 ++ 단 하나의 머신에서만 수행 가능한 테스트 + + +**테스트는 어떠해야 하는가** +1. 배치(Arrange): 입력을 준비. +2. 실행(Act): 실제로 실행. +3. 확인(Assert): 실행 결과를 확인. + +테스트 코드를 유지 보수하고, 테스트 코드 결과에 주의를 기울여야 한다. + +흔히 저지르는 실수는 5개의 메서드를 가진 클래스를 확인하기 위해 5개의 테스트를 해야 한다는 것이다. 하나의 함수만 단독으로 테스트하기가 어렵고 하나의 메서드를 호출한 후에 다른 메서드를 호출해봐야 객체의 상태를 확인할 수 있다. + + +**테스트 프레임 워크** +단위 테스트 혹은 통합 테스트에 사용되는 프레임워크에 따라 테스트 코드의 외관과 체크 형태, 그리고 구조가 결정된다. + +대부분의 테스트 프레임워크는 켄트 백이 만든 오리지널 SUnit 프레임워크로부터 유래한 'xUnit' 모델을 따른다. 이러한 프레임워크가 언제나 이상적이라고 할 수는 없다. + + +**생각해보기** +1. 얼마나 많은 종류의 테스트를 보거나 사용해보았는가? + TDD에 대해서 배우고 그에 따라서 테스트를 해본 경험은 없지만 프린트된 결과값에 대해서 일치 여부를 확인하는 테스트를 경험해본 적은 있다. +2. 테스트 우선 방식과 코드 작성 직후의 테스트 방식 중에 가장 좋은 개발자 테스트 기법은 무엇인가? 그 이유는 무엇이며 어떤 경험을 통해 그 결론을 내렸는가? + 테스트 우선 방식이 더 좋다고 생각한다. 이유는 테스트 우선 방식이 미리 코드의 결과를 예측하여 요구사항이 무엇인지 예측할 수 있다고 생각하며 코드 작성 직후 테스트는 결과들이 코드 작성에 갇혀서 생각될 수도 있지 않을까 싶다. +3. 고품질의 테스트를 작성하기 위해 단위 테스트를 작성하는 전문 개발자를 고용하는 것은 좋은 생각인가? + 대규모 프로젝트에서는 필요하다고 생각된다. 하지만 소규모에서는 전문 개발자 보다는 개발자가 같이 테스트 업무도 진행하는게 좀 더 효율적이라고 느낀다. +4. 왜 QA 부서에서 많은 테스트 코드를 작성하지 않고, 테스트 스크립트와 탐험적 테스트를 수행하는데 집중하는가? + QA의 업무는 사용자 입장에서 테스트를 진행하기 때문이라고 생각한다. 그렇기에 코드에 대한 테스트가 아닌 어떤 프로그램의 실행에 대한 테스트가 이루어져야 한다고 본다. +5. 한 번도 자동화 테스트를 하지 않은 코드베이스에 어떻게 하면 TDD를 가장 잘 적용할 수 있을까? 이때 어떤 문제에 직면하게 될까? + 가장 중요한 로직부터 TDD를 적용해야 한다고 생각하고 TDD에 익숙하지 않은 팀문화가 녹아드는데 시간이 필요하지 않을까 생각한다. +6. 행동 주도 개발은 전통적인 TDD와 어떻게 다른가? 어떤 문제를 해결해주는가? TDD를 보충하는가? 아니면 대체하는가? 행동 주도 개발이 테스트가 나아갈 방향인가? + 행동 주도 개발(BDD)는 TDD를 보충해주며 시스템이 어떻게 행동을 해야하는가에 중점을 둔 방식이다. 이는 기능 중심의 시나리오나 사용자 행동 기반 테스트를 중점에 둔다. + +## Ch12 복잡도 다루기 +소프트웨어 복잡도는 세 가지 원인에 기인한다. 블롭, 라인, 사람이다. + +**블롭** +블롭의 크기와 수가 복잡도를 결정한다. 더 큰 프로젝트는 더 많은 블롭을 필요로 한다. + +크기 자체는 문제가 아니다. 우리는 요구 사항을 충족시킬 만큼 큰 코드가 필요하다. 문제는 이 코드를 어떻게 구성하는가 하는 점이다. + +간결해지도록 설계를 하려면 각 블롭이 정확한 역할과 책임을 확실히 갖도록 해야한다. + + +**라인** +라인이 적을수록 소프트웨어 디자인은 간결해진다. 블롭 사이의 연결이 많아질수록 설계는 더 경직되며, 시스템 상에서 작업시에 이해해야 할 연동이 더 많아진다. + +객체들이 상호 종속적 관계일 경우, 그들의 구조는 융통성이 없고 쉽게 변하지 않으며 작업하기도 어렵다. 하나의 객체를 바꾸려면 다른 것도 변경해야 한다. 이 경우 객체들은 실질적으로 하나의 덩어리인 것이다. + +하나의 연결을 깨뜨림으로써 예를들면 추상적인 인터페이스를 만들어 객체들 간의 결합을 줄이는 방법도 있다. 이런 종류의 구조는 구성력을 강화하고 유연성을 가져오며 테스트 가능도를 향상시킨다. + + +**사람** +블롭과 라인을 만들어내는 책임은 사람에게 있다. 프로그래머는 복잡성을 불러일으킬수도, 반대로 간결한 형태로 바꿀수도 있는 힘이 있다. + +복잡도를 줄일 수 있는 방법은 소프트웨어에 책임감을 가지고, 업무 압박으로 인해 적절하지 않은 구조로 코드를 밀어넣는 상황을 피하고자 노력하는 것이다. + + +**생각해보기** +1. 간결한 코드 설계가 더 좋은 이유는 무엇인가? 설계의 간결함과 코드의 단순함은 어떤 차이가 있는가? + 간결한 코드 설계를 통해 역할과 책임을 분명하게 하여 수정하기에도 용이하게 한다. 설계의 간결함은 복잡도에 관한 부분이고 코드의 단순함은 코드를 직관적으로 이해하기 편함에 있다고 생각한다. +2. 코드를 단순하게 만들기 위한 노력으로는 어떤 것이 있는가? 이를 달성했다는 것을 어떻게 알 수 있는가? + 하나의 함수는 하나의 책임만 갖도록 하고 의존성을 줄이는 방식이 단순하게 만드는 방식이라고 생각한다. 이를 달성했다는 것은 동료와의 코드 리뷰를 통해 알 수 있지 않을까 싶다. +3. 연결의 성격도 연결의 수만큼 중요한가? 어떤 성격의 연결 방식이 더 좋은가? + 중요하다고 생각한다. 서로 연결된 것이 아닌 결합도를 줄이는 방법으로의 연결 방식이 좋다고 생각한다. +4. 만약 소프트웨어 복잡도가 사람 사이의 문제에서 기인한다면 이를 어떻게 해결할 수 있는가? + 업무 압박을 받고 있는 상황인지 시스템적인 상황을 체크하며 사람들이 소프트웨어에 대한 책임감을 다시 가질 수 있도록 소통이 필요하다. +5. 필요한 복잡도와 불필요한 복잡도 간의 차이를 어떻게 설명할 수 있는가? + 필요한 복잡도는 핵심 로직을 다룰 때 필요한 복잡도라고 생각하고 불필요한 복잡도는 필요없는 디자인 패턴 사용과 같은 것이라고 생각한다. +6. 만약 많은 프로그래머가 자신의 소프트웨어 설계가 더 간결해야 함을 알고 있다면, 그들이 더 간결한 코드를 작성할 수 있도록 독려하는 방법은 무엇인가? + 간결한 코드를 작성했을 때와 그렇지 않았을 때의 일의 퍼포먼스 차이를 앞선 개발 사례를 통해 설명을 해준다면 현재 프로젝트가 어느 상황에 놓여있는지 비교를 해보며 알아차릴 수 있지 않을까 생각한다. + +## Ch13 두 개의 시스템에 대한 이야기 +**강력한 응집도와 느슨한 결합도** +응집도는 기능적으로 연관된 것끼리 얼마나 모여 있고, 하나의 모듈 내에서 내부 부분들이 얼마나 유기적으로 작동하는지에 대한 척도이다. 응집도는 모듈을 단단하게 뭉쳐놓는 접착제에 비유할 수 있다. 한편 빈약한 응집도를 가진 모듈은 잘못된 기능 분해의 징후이다. 각 모듈은 명확하게 정의된 역할을 가져야 하며, 관련없는 기능을 마구잡이로 모아놓은 덩어리여서는 안 된다. + +결합도는 모듈 상호 간의 의존성에 대한 지표이다. 서로 얼마나 얼기설기 엮여 있는가에 대한 것이다. 간결한 설계에서는 모듈 간 결합도가 낮아 상호 독립적이다. 물론 모듈들이 완전히 독립적일 수는 없으며, 완전히 독자적으로 작동한다는 건 말이 되지 않는다. 상호 작용하는 모듈들은 직접적 혹은 간접적으로 서로에 대해 의존성을 가진다. 하나의 모듈은 다른 모듈의 함수를 호출할 수 있고, 다른 모듈에 의해 하나의 모듈 내 함수가 호출될 수도 있다. 웹 서비스를 사용하거나 다른 모듈에 의해 공개된 기능을 이용할 수도 있다. 아니면 다른 모듈의 자료형을 사용하거나 어떤 데이터를 공유할 수도 있다. 여기서 데이터란 변수나 파일 같은 것을 말한다. + + +**지저분한 대도시 설계** +나쁜 구조로 인한 문제는 코드 내부에 한정되지 않고 외부와도 연계되어 개개인과 팀, 업무 처리 과정, 일정 산정 모두에 악영향을 미친다. + +지저분한 대도시의 '설계'로 인한 끔찍한 문제들 ++ 낮은 품질의 비주기적인 제품 업데이트 ++ 유연하지 못한 시스템으로 어려워진 신규 기능 추가 및 변경 ++ 시스템 전체에 만연한 코드 수준의 문제들 ++ 스트레스, 사기, 이직 등 사람의 문제 ++ 회사 내부에 만연한 지저분하고 정치적인 문제들 ++ 회사에 필요한 성공의 부재 ++ 코드 작업에 필요한 엄청난 고뇌와 야근 + + +**기술 부채** +금융업계에서 유래한 은유적 표현으로 소프트웨어를 빠르게 출시하기 위한 결정을 내리는 것은 부채를 지는 것과 다름없다는 의미를 갖고 있다. 부채를 무시해서는 안 되며 반드시 되갚어야 한다. 되갚는 시기가 늦어질수록 더 많은 비용이 든다. 적절한 시간에 갚지 않는다면, 부채에 대한 이자가 늘어나고 효용은 줄어든다. + + +**생각해보기** +1. 지금까지 본 것 중 최고의 시스템 구조는 무엇이였는가? + 아직 스스로 답변할 수 없다고 생각하여 AI에게 물어보았을때 Hexagonal Architecture의 경우 개발 생산성, 유지보수성, 확장성, 안정성을 보장하기에 최고의 시스템 구조라고 답변을 하였다. 추가적으로 의존성 방향이 명확하고 변경이 용이하며 팀 분업에 적합한 아키텍쳐이다. +2. 지금까지 본 것 중 최악의 시스템 구조는 무엇이였는가? + 이전에 스스로 짰었던 코드들이다. 스파게티 코드처럼 연결되어 있기도 하고 서로를 참조하는 형태이거나 전역변수를 남용하는 등의 문제점들을 갖고 있었다. +3. 현재 당신의 프로젝트는 두 개의 도시 가운데 어디쯤 속하는가? 이전의 어떤 경험을 바탕으로 코드 혹은 코드를 빌드하는 절차를 개선할 수 있는가? + 아직은 지저분한 대도시에 가깝다고 생각한다. 잘짜인 코드들을 보면서 배울 때가 성장하는 느낌이 있었고 내가 작성한 코드와 연결지어 생각해보면 잘못된 부분을 알 수 있었다. \ No newline at end of file