Chapter 7

설계 원칙 종합

  • 7.1 객체지향과 디자인 패턴
  • 7.2 성능과 설계
  • 7.3 결론

어떤 트렌드든 맹목적으로 따르지 말고, "이것이 복잡성을 줄이는가?"를 물어봐야 해. 그리고 결국 중요한 건 뭐가 중요한지 결정하는 거야.

저자는 현대 소프트웨어 개발의 주요 트렌드들을 비판적으로 평가해. 다 나쁘다는 게 아니라, 맹목적으로 따르면 오히려 복잡성이 늘어날 수 있다는 경고야. 먼저 상속. OOP의 핵심이지만, 잘못 쓰면 부모와 자식 클래스 사이에 밀접한 의존성을 만들어. 부모를 수정하면 자식에 예상치 못한 영향이 가는 건 정보 누출의 한 형태거든. 합성이 상속보다 나은 경우가 많아. GoF 책에서도 그렇게 말하는데, 현실에서는 여전히 상속이 남용되고 있지.

디자인 패턴도 마찬가지야. 패턴의 가치는 인정하지만, 패턴을 적용하는 것 자체가 목적이 되면 안 돼. "여기에 패턴을 적용할 수 있겠다!" 하면서 필요하지 않은 곳에 억지로 쓰면 복잡성만 늘어나지. Factory, Abstract Factory, Builder, Strategy... 클래스 하나면 될 일을 패턴 적용한답시고 5개로 늘리는 건 classitis의 전형이야.

애자일의 증분적 개발에는 기본적으로 긍정적이야. 소프트웨어는 만들어봐야 문제를 알 수 있으니까. 근데 애자일이 "설계를 안 해도 된다"는 핑계로 쓰이면 문제지. "다음 스프린트에서 리팩터링하면 되지" 하면서 설계를 미루는 건 전술적 프로그래밍이야. 반복은 설계를 개선하기 위한 것이지, 미루기 위한 게 아니거든.

TDD에 대해서도 회의적이야. 테스트의 가치는 인정하지만, 테스트가 통과한다고 설계가 좋은 건 아니지. 복잡하고 나쁜 설계도 테스트를 통과할 수 있어. 테스트와 설계는 별도의 관심사로 취급해야 해. 유닛 테스트 자체는 좋지만, 내부 구현에 너무 결합되면 리팩터링할 때마다 테스트도 다 고쳐야 해서 오히려 변경을 어렵게 만들어.

결국 모든 트렌드에 이 질문을 던져봐야 해: 이 접근이 복잡성을 줄이는가, 늘리는가? 모듈이 더 깊어지는가, 얕아지는가? 정보 은닉이 더 잘 되는가? 소프트웨어 업계는 은탄환에 대한 환상을 자주 갖는데, 복잡성 관리라는 근본 과제는 어떤 트렌드로도 해결되지 않아. 개발자 개인의 설계 역량을 기르는 것만이 답이야.

이제 성능과 설계의 관계를 보자. 보통 "섣부른 최적화는 악의 근원"이라고 하지만, 저자는 설계 단계에서 성능을 아예 무시하면 나중에 큰 대가를 치를 수 있다고 경고해. 성능 문제가 아키텍처 수준에서 발생하면 — 데이터 구조의 선택, 모듈 간 통신 방식, 캐싱 전략 같은 거 — 나중에 고치기가 엄청나게 어렵거든. 반대로 처음부터 모든 걸 최적화하면 코드가 복잡해지고 유지보수가 어려워지고. 저자의 제안은 중간 지점이야. 아키텍처 결정에서는 성능을 고려하되, 세부 구현에서는 깔끔한 설계를 우선하고 프로파일링 결과에 따라 최적화하라는 거지.

최적화가 필요할 때는 핵심 경로에 집중해야 해. 전체 코드의 소수가 전체 실행 시간의 대부분을 차지하니까. 가장 흔한 케이스를 먼저 체크해서 빠른 경로로 보내고, 불필요한 연산을 제거하고, 캐싱을 활용하는 거야. 감으로 최적화하지 말고 항상 프로파일링으로 실제 병목을 찾아야 해.

좋은 소식은 성능과 깔끔한 설계가 반드시 충돌하지는 않는다는 거야. 깊은 모듈은 함수 호출 오버헤드를 줄이고, 정보 은닉은 외부 코드 변경 없이 내부 알고리즘만 바꿀 수 있게 해주지. 성능 문제의 대부분은 설계를 망가뜨리지 않고도 해결 가능해. 인터페이스를 바꿔야 하는 수준의 성능 문제는 드물고, 그런 경우는 보통 아키텍처 설계 단계의 실수야.

마지막으로 이 책 전체를 관통하는 메타 원칙이야 — 무엇이 중요한지 결정하고, 그것에 집중하라. 소프트웨어 설계에서 모든 것을 동시에 잘할 수는 없어. 성능, 단순성, 범용성, 구현 속도, 유지보수성은 서로 상충할 때가 많거든. 좋은 설계자의 핵심 역량은 현재 상황에서 가장 중요한 것이 무엇인지 판단하는 능력이야. 시스템 수준에서는 게임 엔진이면 성능, 의료 소프트웨어면 안정성, 스타트업 MVP면 개발 속도가 최우선이지. 모듈 수준, 코드 수준에서도 마찬가지야.

뭐가 중요한지 결정했으면 중요한 것을 강조하고, 덜 중요한 것을 최소화해야 해. 핵심 로직이 에러 처리, 로깅, 설정 같은 부가적 코드에 묻히면 안 돼. 핵심이 아닌 부분은 "충분히 좋은" 수준이면 되고, 깊은 모듈로 비핵심을 숨기면 사용자는 핵심에만 집중할 수 있지.

이 책의 모든 원칙 — 깊은 모듈, 정보 은닉, 주석, 일관성, 명확성 — 은 결국 하나의 목표를 향해. 개발자가 한 번에 이해해야 하는 것의 양을 줄이는 것. 전체 시스템이 복잡하더라도, 지금 작업하는 부분만 이해하면 되게 만드는 것. 그게 좋은 설계야. 저자는 소프트웨어 설계가 예술에 가깝다고 말해. 기계적 규칙으로 해결되지 않고, 경험과 직관과 판단이 필요하지. 이 책의 원칙들은 절대적 규칙이 아니라 가이드라인이야. 상황에 맞게 적용하고, 때로는 의도적으로 원칙을 깨는 판단도 필요해.


정리

7장 읽고 기억할 거 세 가지:

  1. 어떤 트렌드든 **"이것이 복잡성을 줄이는가?"**로 평가해야 해. 상속보다 합성, 패턴은 필요할 때만, 애자일은 설계를 미루는 핑계가 아니야
  2. 설계할 때 성능을 의식하되 섣부른 최적화는 피해. 좋은 설계와 좋은 성능은 양립 가능하고, 깊은 모듈과 정보 은닉이 성능 최적화를 내부에서 투명하게 해줘
  3. 소프트웨어 설계의 본질은 개발자가 한 번에 이해해야 하는 것의 양을 줄이는 것이야. 무엇이 중요한지 결정하고, 그것에 집중하자