‘Dependency Injection Principles, Practices, and Patterns’에서 배운 교훈
Source: Dev.to
Introduction
소프트웨어 개발자로서 첫 번째 직장은 Microsoft와 매우 긴밀히 협업하던 소프트웨어 개발 에이전시에서 C#을 작성하는 것이었습니다. 주니어 엔지니어였을 때 객체‑지향 프로그래밍과 SOLID 원칙에 대한 기본적인 개념은 있었지만, .NET 의존성 주입 컨테이너를 사용하는 프로젝트를 열 때마다 혼란스러웠습니다. 왜 모든 것에 인터페이스를 작성해야 할까요? 왜 클래스 내부에서 인스턴스를 직접 생성하지 않을까요? 왜 모든 인스턴스를 생성자를 통해 전달해야 할까요? 너무 관료적으로만 느껴졌습니다. 코드가 간결하고 단순하게 유지될 수 있는데도 불구하고, 불필요한 인터페이스와 인스턴스 생성을 없애면 되는 상황에서 왜 이렇게 많은 코드를 추가해야 하는지 이해하지 못했습니다.
몇 년이 흐른 뒤, 다른 회사에서 일하면서 Dependency Injection Principles, Practices, and Patterns 를 집어 들었습니다. 그 책이 한 유튜버의 영상 배경에 떠 있는 것을 기억합니다(YouTuber’s video). 당시에는 이 책을 읽으면 우리 프로젝트에 더 많은 구조와 질서를 도입할 수 있을 것이라고 생각했습니다. 책을 읽고 나니 제 질문들뿐만 아니라 더 많은 답을 얻을 수 있었습니다. 객체‑지향 프로그래밍과 소프트웨어 설계 전반에 대한 이해도가 크게 향상되었습니다.
저자 Steven van Deursen과 Mark Seemann은 디커플링, 응집도, 그리고 이들이 SOLID 원칙과 어떻게 연결되는지와 같은 의존성 주입의 핵심 개념을 훌륭하게 설명합니다. 그들은 의존성 주입 패턴, 흔히 나타나는 안티‑패턴, 코드 냄새에 대한 예시를 제공하며, 이 모든 것이 의존성 주입의 주요 장점을 잘 보여줍니다. 책은 매우 잘 쓰여졌으며, 객체‑지향 프로그래밍을 다루는 모든 사람에게 강력히 추천합니다.
아래는 제가 소프트웨어 개발에 접근하는 방식을 크게 바꾼 책의 세 가지 아이디어입니다. 완전한 요약은 아니며, 의존성 주입을 완전히 이해하려면 책을 직접 읽어보시길 권합니다.
Low Coupling
책은 호텔 헤어드라이어에 대한 비유를 통해 결합도(coupling) 개념을 소개합니다. 많은 호텔 헤어드라이어는 소켓에 직접 연결되어 있습니다. 이는 고결합 코드와 마찬가지로 수정하거나 교체하기가 번거롭습니다. 헤어드라이어가 고장 나면 전기 기술자가 방 전체 전원을 끄고 배선을 탈착한 뒤 교체해야 합니다. 고결합 코드에서도 마찬가지로, 문제의 모듈을 교체하거나 리팩터링하기 전에 다른 모듈들을 먼저 변경해야 하는 경우가 많습니다.
반면 플러그가 달린 헤어드라이어는 교체가 쉽습니다. 벽면 소켓에서 뽑아내기만 하면 됩니다. 느슨하게 결합된 소프트웨어도 마찬가지로, 교체·제거·유지가 더 쉽습니다.
유지보수성은 소프트웨어 개발에서 매우 중요한 요소입니다. 유지보수가 어려운 솔루션은 변경 비용이 크게 상승해 비즈니스의 기능 릴리즈가 줄어듭니다. 느슨하게 결합된 코드는 프로젝트의 유지보수성을 높여 새로운 기능을 더 빠르게 개발할 수 있게 합니다.
High Cohesion
저자들은 SOLID 원칙을 논의하면서 단일 책임 원칙 (SRP, Single Responsibility Principle) 을 강조합니다:
단일 책임 원칙은 각 클래스가 변경될 이유가 하나만 있어야 한다는 것을 의미합니다.
SRP와 관련해 저자들은 응집도(cohesion) 를 클래스나 모듈의 요소들이 기능적으로 얼마나 관련되어 있는가로 정의합니다. 관련성이 낮으면 응집도가 낮아지고, 이는 클래스가 SRP를 위반할 가능성을 높입니다.
느슨하게 결합된 코드를 작성하려 할 때 SRP는 매우 귀중한 가이드라인이 됩니다. 높은 응집도를 가진 모듈 간에만 느슨한 결합이 이루어져야 하며, 그렇지 않으면 소프트웨어가 이해하기 어려워지고 유지보수성이 떨어집니다.
Volatile and Stable Dependencies
느슨하게 결합된 코드는 유지보수성을 높여주지만, 시임(seam) 이라는 비용이 추가됩니다.
구체 타입 대신 추상화에 대해 코딩할 때마다 시임을 도입하게 됩니다. 시임은 애플리케이션이 구성 요소들로 조립되는 지점을 의미하며, 옷감이 바느질로 이어지는 방식과 비슷합니다.
이 비용 때문에 시임은 휘발성(volatile) 의존성 이 필요한 경우에만 도입하는 것이 중요합니다. 휘발성 의존성은 안정적인 의존성과 달리 다음 중 하나에 해당합니다:
- 다른 환경(예: 다른 머신이나 프로세스)에서 실행됩니다. 예를 들어 데이터베이스가 이에 해당합니다.
- 아직 존재하지 않거나 개발 중입니다.
- 비결정적 행동을 포함합니다.
- 새로운 버전이 호환성을 깨뜨릴 수 있습니다.
Conclusion
이 책에서 배운 개념은 여기서 다 다루지는 못했습니다. 구조, 설명, 비유 덕분에 읽기 쉬운 책이며, 모두에게 한 번쯤은 구매해 읽어보고, 질문이 떠올랐을 때 꺼내볼 수 있도록 책꽂이에 두길 권합니다.