이 장을 읽기 전에
결합도와 응집도에서 다룬 “낮은 결합도, 높은 응집도"라는 기준을 안다고 가정한다. SOLID는 이 추상적인 기준을 “이런 코드를 보면 이 원칙을 적용해라"는 구체적인 다섯 가지 규칙으로 풀어놓은 것이다.
SOLID는 왜 다섯 개로 나뉘어 있는가
SOLID는 단일 책임 원칙(Single Responsibility), 개방-폐쇄 원칙(Open-Closed), 리스코프 치환 원칙(Liskov Substitution), 인터페이스 분리 원칙(Interface Segregation), 의존성 역전 원칙(Dependency Inversion)의 앞글자를 딴 이름이다. 다섯 원칙 각각은 로버트 마틴(Robert C. Martin)이 2000년대 초 여러 글에서 정리했고, 이를 SOLID라는 하나의 약어로 묶어 부르는 방식은 이후 마이클 페더스(Michael Feathers)가 제안한 것으로 널리 알려져 있다. 다섯 원칙 모두 결국 결합도와 응집도에서 다룬 “모듈이 서로 덜 알게, 각자는 하나의 일에 집중하게” 만드는 것을 목표로 하지만, 각각 “어떤 상황에서” 그것이 깨지는지를 다르게 짚는다.
단일 책임 원칙(SRP): 가장 오해받는 원칙
단일 책임 원칙은 “클래스는 변경할 이유를 하나만 가져야 한다"고 말한다. 여기서 가장 흔한 오해가 “하나의 메서드만 가져야 한다"거나 “클래스가 작아야 한다"는 것이다. 실제로는 **“변경의 이유”**가 핵심이다. 결합도와 응집도에서 다룬 OrderManager 예시를 다시 보면, 계산 로직이 바뀌는 이유(세율 변경)와 이메일 형식이 바뀌는 이유(마케팅팀 요청)는 서로 무관한 팀·무관한 시점에 발생한다 — 이 “서로 다른 변경 이유"가 한 클래스에 뒤섞여 있다는 것이 SRP 위반의 진짜 신호다.
| |
이제 영수증 형식을 HTML로 바꿔야 한다면 ReceiptPrinter만 건드리면 되고, 세율 계산 방식이 바뀐다면 InvoiceCalculator만 건드리면 된다 — 각 클래스가 변경 이유를 하나씩만 갖는다.
개방-폐쇄 원칙(OCP): 기존 코드를 건드리지 않고 확장한다
개방-폐쇄 원칙은 “확장에는 열려 있고 변경에는 닫혀 있어야 한다"고 말한다. 새로운 결제 수단을 추가할 때마다 기존 결제 처리 코드에 분기를 추가해야 한다면, 그 코드는 새 결제 수단이 늘어날 때마다 계속 수정돼야 하므로 OCP를 위반한다.
| |
새 결제 수단(예: 간편결제)을 추가하려면 SimplePayPayment 클래스 하나만 새로 만들면 되고, process_payment_v2 함수와 기존 CardPayment·BankTransferPayment 코드는 한 줄도 건드리지 않는다 — 이것이 “확장에는 열려 있고 변경에는 닫혀 있다"는 OCP의 실제 의미다.
리스코프 치환 원칙(LSP): 자식이 부모의 약속을 깨면 안 된다
리스코프 치환 원칙은 “자식 클래스는 부모 클래스가 쓰이는 모든 곳에서 부모를 대체할 수 있어야 한다"고 말한다. 고전적인 위반 예시는 “정사각형은 직사각형이다"라는 상속 관계다.
| |
resize_to_width_10은 Rectangle을 기대하고 작성됐지만, Square를 넣는 순간 “너비만 바뀐다"는 암묵적 계약이 깨진다 — 부모 타입을 다루는 코드가 자식 타입에서도 똑같이 동작해야 한다는 LSP를 위반한 것이다.
인터페이스 분리 원칙(ISP): 쓰지 않을 메서드까지 강제하지 않는다
인터페이스 분리 원칙은 “클라이언트가 쓰지 않는 메서드에 의존하도록 강제하지 말라"고 말한다 — 하나의 거대한 인터페이스보다, 필요한 기능별로 작게 나눈 인터페이스 여러 개가 낫다.
| |
SimplePrinterV2는 실제로 지원하지 않는 scan·fax 메서드를 구현할 필요가 없다 — Printable 인터페이스는 프린터가 실제로 제공하는 기능만 요구한다.
의존성 역전 원칙(DIP): 구체 클래스가 아니라 추상화에 의존한다
의존성 역전 원칙은 “고수준 모듈이 저수준 모듈의 구체적인 구현이 아니라 추상화에 의존해야 한다"고 말한다 — 결합도와 응집도에서 OrderProcessor가 EmailNotifier라는 구체 클래스 대신 “알림을 보낸다"는 인터페이스에 의존하도록 바꾼 것이 바로 이 원칙의 적용이다.
| |
비교: 다섯 원칙이 겨냥하는 문제
| 원칙 | 겨냥하는 문제 | 핵심 질문 |
|---|---|---|
| SRP | 서로 다른 이유로 바뀌는 코드가 한 곳에 뒤섞임 | 이 클래스가 바뀌는 이유가 몇 가지인가? |
| OCP | 기능 추가 때마다 기존 코드를 수정해야 함 | 새 기능을 기존 코드 수정 없이 추가할 수 있는가? |
| LSP | 자식 클래스가 부모의 계약을 깨뜨림 | 부모를 자식으로 바꿔도 기존 코드가 안전한가? |
| ISP | 쓰지 않는 메서드까지 구현하도록 강제됨 | 이 인터페이스가 너무 많은 걸 요구하지 않는가? |
| DIP | 고수준 로직이 구체적인 구현 세부사항에 얽매임 | 이 의존이 추상화를 향하는가, 구체 클래스를 향하는가? |
흔한 오개념
“SRP는 클래스를 무조건 작게 쪼개라는 뜻이다” — 위에서 다룬 대로 핵심은 “변경 이유의 개수"이지 “코드 줄 수"가 아니다. 서로 같은 이유로 바뀌는 로직을 억지로 여러 클래스에 쪼개 놓으면, 오히려 하나의 변경이 여러 파일에 흩어져 응집도가 낮아지는 역효과가 난다.
언제 적용하고 언제 과잉 설계가 되는가
다섯 원칙 모두 추상화 계층을 하나씩 더하는 방향으로 작동하므로, 무조건 적용하면 오히려 코드를 읽기 어렵게 만드는 **과잉 설계(Over-engineering)**가 된다. 판단 기준은 “이 부분이 실제로 바뀔 가능성이 있는가"다. 결제 수단이 하나뿐이고 앞으로도 추가될 계획이 없다면 OCP를 위해 인터페이스를 미리 만들 필요는 없다 — 실제로 두 번째 결제 수단이 필요해지는 시점에 리팩토링해도 늦지 않다. 반대로 요구사항 문서나 로드맵에 “다른 결제 수단도 지원 예정"처럼 변경이 예정된 지점이라면, 처음부터 확장 가능한 구조로 설계하는 비용이 나중에 기존 코드를 뜯어고치는 비용보다 작다. 요약하면, SOLID는 “변경이 예상되는 지점에 미리 놓는 방지턱"이지, 모든 클래스에 기계적으로 적용하는 체크리스트가 아니다.
다른 개념과의 연결
DIP의 “추상화에 의존"은 결합도와 응집도에서 다룬 낮은 결합도의 구체적인 실천 방법이고, SRP는 높은 응집도의 실천 방법이다. 다음 챕터에서는 이 원칙들을 검증된 해법으로 구체화한 디자인 패턴 개요를 다룬다.
평가 기준
이 챕터를 읽은 후에는 다음을 할 수 있어야 한다. SOLID 다섯 원칙 각각이 겨냥하는 구체적인 문제 상황을 설명할 수 있다. 단일 책임 원칙을 “메서드 개수"가 아니라 “변경 이유의 개수"로 올바르게 판단할 수 있다. SOLID를 과도하게 적용했을 때 발생하는 과잉 설계의 징후를 식별할 수 있다.
참고 자료
Martin, R. C. (2003). Agile Software Development, Principles, Patterns, and Practices. Prentice Hall.
- Robert C. Martin: The Principles of OOD — SOLID 다섯 원칙을 처음 정리한 원 저자의 글
- Refactoring Guru: SOLID Principles — 다섯 원칙과 위반 코드 예시를 다룬 실무 참고 자료
![Featured image of post [Computer Terms] SOLID 원칙 개요](/post/computerterms/solid-principles-overview/wordcloud_hu_8c81593268cc4120.webp)
![[Computer Terms] 인증과 인가 (Authentication, Authorization)](/post/computerterms/authentication-and-authorization/wordcloud_hu_b34b34781fc1ffcc.webp)
![[Computer Terms] 결합도와 응집도 (Coupling, Cohesion)](/post/computerterms/coupling-and-cohesion/wordcloud_hu_4fa8b9f443122636.webp)
![[Computer Terms] SOLID 원칙 개요](/post/computerterms/solid-principles-overview/wordcloud_hu_485180c9cc39a48b.webp)
![[Computer Terms] 로드 밸런싱 (Load Balancing)](/post/computerterms/load-balancing/wordcloud_hu_a127eb0bb64ce817.webp)
![[Computer Terms] 샤딩과 복제 (Sharding, Replication)](/post/computerterms/sharding-and-replication/wordcloud_hu_5bdd44ecbdf63dcd.webp)
![[Computer Terms] 디자인 패턴 개요 (Design Patterns)](/post/computerterms/design-patterns-overview/wordcloud_hu_24f0125600c9cc49.webp)
![[Computer Terms] 리팩토링과 코드 스멜 (Refactoring, Code Smell)](/post/computerterms/refactoring-and-code-smells/wordcloud_hu_893b2b850bf2415e.webp)
![[Refactoring] 코드 리팩토링의 중요성과 모범 사례](/post/2024-09-09-refactoring/wordcloud_hu_2ef77156263c9a2.webp)
![[SoftwareTesting] 소스 코드 테스트 커버리지 메트릭과 활용](/post/2024-08-19-test-coverage/wordcloud_hu_5f670d08f61abc22.webp)