Summary
이 글은 ‘개발자의 Memoization’ 블로그의 “시니어 개발자로 성장하기 위한 핵심 역량 - ‘육각형 개발자’ 책 리뷰” 아티클에 대한 리뷰다. 원문은 시니어 개발자가 갖춰야 할 핵심 역량 중 특히 설계 역량에 관한 내용을 다루고 있다. 책에서 강조하는 “높은 응집도와 낮은 결합도를 추구해야 한다”는 설계 원칙과 다양한 결합도 유형들, 그리고 “상속보다는 조립(Composition over inheritance)” 원칙이 특히 인상적이었다. 또한 리팩토링의 중요성과 테스트 코드 작성을 통해 코드의 품질을 높이는 방법도 실무적인 관점에서 매우 유용했다. 이 리뷰에서는 원문의 핵심 내용과 함께 내가 프로젝트를 진행하면서 경험한 코드 설계의 어려움, 응집도와 결합도 개선을 위한 노력, 그리고 테스트 코드 작성의 중요성에 대한 나의 생각을 담았다.
Info
이 글은 항해99 1일 1아티클 챌린지 참여 글입니다.
아티클 내용
들어가며
이 아티클은 ‘육각형 개발자’라는 책의 리뷰로, 시니어 개발자가 되기 위해 필요한 핵심 역량들을 다루고 있다. 특히 이번 파트에서는 설계 역량에 초점을 맞추어, 변화에 유연하게 대응할 수 있는 코드를 작성하는 방법에 대해 설명하고 있다.
설계 역량은 단순히 기능을 구현하는 것을 넘어, 미래의 변화를 고려한 구조를 만드는 능력이다. 저자는 코드의 응집도와 결합도, 리팩토링, 그리고 테스트라는 세 가지 핵심 주제를 통해 시니어 개발자가 갖춰야 할 설계 역량을 설명한다.
인상 깊었던 한 줄
“상속보다는 조립(Composition over inheritance) 원칙 - 상속은 상위 클래스와 하위 클래스간 강한 결합을 발생시키지만 컴포지션을 사용하면 상속에 비해 결합도를 낮출 수 있다.”
이 문장이 가장 인상 깊었던 이유는, 객체지향 프로그래밍에서 자주 마주치는 설계 딜레마에 대한 명확한 지침을 제시하기 때문이다. 학교나 기초 교육에서는 상속의 개념과 장점을 많이 강조하지만, 실제 프로덕션 코드에서는 상속보다 컴포지션이 더 유연하고 확장성 있는 설계를 가능하게 한다는 점을 경험적으로 느끼고 있었다.
실제로 내가 진행했던 프로젝트에서도 처음에는 상속을 통해 코드를 구조화했다가, 요구사항이 변경되면서 상속 구조가 오히려 발목을 잡는 경험을 했다. 기능을 확장하거나 변경할 때마다 상위 클래스와의 의존성 때문에 수정 범위가 넓어졌고, 결국 컴포지션 방식으로 리팩토링했을 때 훨씬 더 유연하게 변화에 대응할 수 있었다.
이 원칙은 단순한 코드 스타일의 문제가 아니라, 시스템의 유지보수성과 확장성에 직접적인 영향을 미치는 중요한 설계 결정이라는 점에서 깊은 공감을 했다. 코드베이스가 커질수록, 또 서비스가 오래 유지될수록 이런 설계 원칙의 중요성이 더욱 크게 다가온다.
“수정 비용을 줄이기 위해. 응집도가 높을수록 구성 요소가 각 역할에 따라 분리될 가능성이 크고, 그럴수록 수정해야할때 변경 범위가 좁아진다.”
이 문장은 높은 응집도를 추구해야 하는 이유를 설명하는 부분으로, 매우 실용적인 관점에서 설계 원칙의 중요성을 강조한다. 개발자로서 가장 많은 시간을 보내는 일이 기존 코드의 유지보수와 기능 확장인데, 이때 응집도가 높은 코드는 변경해야 할 범위를 최소화해 작업 효율을 크게 높여준다.
내 경험에서도 초기에 빠르게 개발하기 위해 응집도를 고려하지 않고 코드를 작성했다가, 나중에 기능을 확장하거나 버그를 수정할 때 예상보다 훨씬 많은 시간을 소비한 적이 있다. 한 가지 기능을 변경하려고 했는데, 관련 코드가 여러 파일에 분산되어 있어 전체 시스템을 이해하는 데 많은 시간이 필요했고, 수정 후에도 예상치 못한 부작용이 발생했던 것이다.
이 문장은 단순히 “좋은 코드”를 위한 추상적인 원칙이 아니라, 실제 개발 과정에서 시간과 비용을 절약하는 매우 실용적인 지침이라는 점에서 가치가 있다.
응집도와 결합도
아티클에서는 높은 응집도와 낮은 결합도를 추구해야 한다는 원칙의 구체적인 의미를 설명한다.
응집도란 한 기능과 관련된 코드가 한곳에 모여 있어야 한다는 개념이다. 응집도가 높아야 하는 이유는 수정 비용을 줄이기 위해서이며, 이를 위한 방법으로 캡슐화를 강조한다. 캡슐화를 통해 데이터에 대한 직접 접근을 최소화하고 구현을 감추어 외부에 기능만을 제공하는 것이 중요하다.
결합도는 소프트웨어 모듈이 서로 의존하는 정도를 말한다. 아티클에서는 다양한 결합도의 유형들을 소개한다:
- 공통 결합: 여러 모듈이 동일한 글로벌 데이터에 접근하는 경우
- 제어 결합: 한 모듈이 다른 모듈의 논리적 흐름을 제어하는 경우
- 하위 클래스 결합: 상속 관계에서 발생하는 결합
- 시간적 결합: 함께 실행해야 하는 코드들이 한 모듈에 묶여있는 경우
- 논리 결합: 한 모듈의 변경이 다른 모듈의 변경을 필요로 하는 경우
내가 특히 공감했던 부분은 상속 관계에서 발생하는 하위 클래스 결합에 대한 설명이었다. 앞서 인상 깊었던 “상속보다는 조립” 원칙과 연결되는 내용으로, 상속은 상위 클래스와 하위 클래스 간의 강한 결합을 만들어내고, 상위 클래스의 작은 변경이 여러 하위 클래스에 큰 영향을 미칠 수 있다는 점을 강조했다.
이러한 문제를 해결하기 위해 컴포지션을 사용하면 상속에 비해 결합도를 낮출 수 있다는 점이 실제 개발 경험과도 일치했다. 객체의 참조를 통해 기능을 조합하는 방식이 클래스 계층 구조를 통한 상속보다 유연하고 변화에 강하다는 것을 여러 프로젝트에서 경험했다.
리팩토링
아티클에서는 리팩토링을 “외부로 드러나는 동작이나 기능을 변경하지 않고 내부 구조를 변경해서 재구성하는 기법”으로 정의하고 있다. 레거시 코드를 다룰 때 특히 중요한 스킬이라고 설명한다.
다양한 리팩토링 기법들도 소개되었다:
- 미사용 코드 삭제
- 매직 넘버를 상수나 enum으로 대체
- 이름 변경: 구체적이고 의미가 명확한 이름 사용
- 메서드 추출: 논리적으로 하나의 작업을 수행하는 코드를 메서드로 묶기
- 클래스 추출/분리: 특정 기능을 전담하는 역할의 클래스로 분리
- 파라미터 정리: 사용하지 않는 파라미터 제거
- for 문에서 하는 일 분리: 한 반복문에서 여러 작업을 하지 않도록 하기
이 부분에서 나는 특히 “매직 넘버” 관련 내용과 “이름 변경”에 대한 부분에 공감했다. 코드 리뷰를 할 때마다 가장 자주 지적하는 부분이 바로 이런 부분들이다. 예를 들어 type=42
와 같은 매직 넘버는 그 의미를 유추하기 어렵고, selectInput()
이라는 모호한 함수명보다는 filterAndSaveSuccessInput()
처럼 구체적인 이름이 코드의 가독성과 유지보수성을 크게 높인다.
실제로 내가 최근에 작업한 프로젝트에서도 legacy 코드의 매직 넘버들을 enum으로 리팩토링하는 작업을 진행했는데, 이후 코드를 이해하고 수정하는 데 걸리는 시간이 크게 줄어든 경험이 있다.
테스트
아티클에서는 테스트의 중요성, 특히 자동화된 테스트 코드의 가치에 대해 설명한다. 시스템이 복잡할수록 테스트의 중요성이 증가하며, 자동화된 테스트가 있으면 수정한 코드가 발생시키는 문제를 빨리 찾을 수 있다고 강조한다.
테스트 주도 개발(TDD)에 대한 설명도 있는데, 테스트 코드를 먼저 작성하고 이를 통과할 만큼만 구현을 진행하는 방식이다. 이는 기능 설계에도 도움을 주는데, 테스트할 대상의 이름, 타입, 의존 대상 등을 테스트 코드 작성 단계에서 이미 설계하게 된다.
수동 테스트와 비교했을 때 테스트 코드가 갖는 장점으로는:
- 모든 코드를 개발하지 않아도 일부만으로 테스트 가능
- 실제 DB, 외부 API 등에 의존하지 않아도 됨
- 개발 즉시 테스트 실행 가능
- 테스트 가능한 방식으로 코드를 설계하게 되어 더 좋은 설계로 이어짐
이 부분에서 나는 특히 “테스트 가능성을 높이는 방향으로 구현하는 것이 개발 생산성과 코드 품질 향상에 도움이 된다”는 내용에 공감했다. 처음에는 테스트 코드 작성이 추가적인 작업으로 느껴졌지만, 경험이 쌓이면서 테스트를 염두에 둔 설계가 전체적인 코드 품질을 높이고 장기적으로는 개발 속도를 향상시킨다는 것을 알게 되었다.
개인적인 공감 포인트
-
응집도와 결합도의 균형에 대한 고민: 아티클에서 설명하는 “높은 응집도와 낮은 결합도”는 이상적인 설계 원칙이지만, 실제 개발 과정에서는 이 둘 사이의 균형을 찾는 것이 쉽지 않다. 특히 시간 압박이 있는 프로젝트에서는 응집도를 높이기 위한 리팩토링을 미루게 되는 경우가 많았다. 그러나 장기적으로는 이러한 기술 부채가 쌓여 더 큰 비용을 지불하게 된다는 것을 경험했다.
-
상속보다 컴포지션의 유용성: 초기 개발자 시절에는 상속을 통한 코드 재사용에 집중했지만, 실제 복잡한 프로젝트에서는 상속이 오히려 유연성을 떨어뜨리는 경우가 많았다. 특히 Java와 같은 단일 상속만 지원하는 언어에서는 컴포지션을 통한 설계가 훨씬 더 유연하고 확장 가능한 구조를 만들어준다는 점에 크게 공감했다.
-
테스트 코드의 가치: 처음에는 테스트 코드 작성이 추가적인 부담으로 느껴졌지만, 프로젝트가 커질수록 자동화된 테스트의 가치를 실감했다. 특히 코드 리팩토링을 할 때, 테스트 코드가 있으면 변경 후에도 기존 기능이 정상적으로 동작하는지 빠르게 확인할 수 있어 큰 도움이 되었다.
-
리팩토링의 실질적 효과: 아티클에서 언급한 여러 리팩토링 기법들을 실제 프로젝트에 적용해보았을 때, 코드의 가독성과 유지보수성이 크게 향상되는 것을 경험했다. 특히 매직 넘버 제거, 명확한 이름 사용, 메서드 추출 등은 비교적 쉽게 적용할 수 있으면서도 효과가 큰 리팩토링 기법이었다.
핵심 요약
-
응집도와 결합도는 코드 품질의 핵심 지표: 높은 응집도(관련 기능을 한 곳에 모음)와 낮은 결합도(모듈 간 의존성 최소화)는 변화에 유연하게 대응할 수 있는 코드 설계의 기본 원칙이다. 특히 캡슐화를 통한 응집도 향상과 추상화를 통한 결합도 감소가 중요하다.
-
상속보다는 컴포지션 활용: 상속은 강한 결합을 발생시키므로, 가능하면 컴포지션(객체 조합)을 통한 설계가 더 유연하고 확장성 있는 코드를 만든다. 이는 실제 프로덕션 환경에서 코드 유지보수 시 큰 차이를 만든다.
-
리팩토링은 지속적인 코드 품질 관리의 핵심: 외부 동작은 변경하지 않고 내부 구조를 개선하는 리팩토링은 코드 품질을 유지하는 필수적인 과정이다. 미사용 코드 제거, 명확한 이름 사용, 단일 책임을 가진 메서드/클래스 분리 등의 기법을 통해 코드 품질을 점진적으로 향상시킬 수 있다.
-
테스트 코드는 안정적인 변화를 가능하게 하는 안전망: 자동화된 테스트 코드는 리팩토링과 기능 확장 시 발생할 수 있는 문제를 빠르게 발견할 수 있게 해준다. 또한 테스트 가능한 코드를 작성하기 위한 노력이 전반적인 코드 품질 향상으로 이어진다.
결론
이 아티클은 시니어 개발자로 성장하기 위해 필요한 설계 역량을 매우 실용적인 관점에서 설명하고 있다. 특히 응집도와 결합도, 리팩토링, 테스트라는 세 가지 핵심 주제를 통해 변화에 유연하게 대응할 수 있는 코드를 작성하는 방법을 알려준다.
개발자로서 성장하면서 단순히 “동작하는 코드”를 넘어 “잘 설계된 코드”를 작성하는 것의 중요성을 점점 더 체감하고 있다. 특히 프로젝트의 규모가 커지고 팀원이 늘어날수록, 그리고 제품의 수명이 길어질수록 좋은 설계의 가치는 더욱 분명하게 드러난다.
이 아티클을 통해 배운 설계 원칙들을 실제 프로젝트에 적용하면서, 단기적인 생산성과 장기적인 유지보수성 사이의 균형을 찾는 지혜를 키워나가야겠다는 생각이 든다. 시니어 개발자로 성장한다는 것은 단순히 기술적 지식을 쌓는 것이 아니라, 이런 설계 원칙들을 자연스럽게 적용할 수 있는 판단력과 경험을 쌓아가는 과정이라고 생각한다.