[OOP] 설계의 5가지 원칙, SOLID

구본탁·2025년 4월 9일

OOP

목록 보기
1/2

💬 SOLID원칙의 등장 배경

2000년대 초반, Robert C. Martin의 논문 "Design Principles and Design Patterns”에서 처음 제안되었으며, 이후 Michael Feathers에 의해 SOLID라는 약어로 정리되었다.


"Design Principles and Design Patterns"

by Robert C. Martin


📌 소프트웨어 설계의 문제점

소프트웨어 설계는 시간이 지남에 따라 '부패'할 수 있으며, 이는 유지보수성과 확장성을 저하시키는 주요 원인이다.

  • 경직성 : 소프트웨어 변경이 어렵고, 작은 변경이 전체 시스템에 영향을 미쳐 복잡한 수정 작업을 요구함.
  • 취약성 : 변경 시 예상치 못한 부분에서 오류가 발생하며, 문제가 해결될수록 새로운 문제가 생김.
  • 비이동성 : 코드 재사용이 어려워 기존 코드를 새 프로젝트에서 활용하지 못함.
  • 점성 : 올바른 설계를 유지하기 어렵고, 환경적 제약으로 인해 비효율적인 방법을 선택하게 됨.
    1. 설계 점성 - 설계를 보존하는 방법이 임시방편적인 해결책보다 적용하기 더 어려울 때, 설계 점성이 높은 것
    2. 환경 점성 - 개발 환경이 느리고 비효율적일 때 발생하는 것

🚩 객체지향 설계 원칙 SOLID 등장

설계가 부패하는 주요 원인인 '변화하는 요구사항''잘못된 의존성 관리'를 개선하기 위한 5가지 원칙이다.

SRP : 단일 책임 원칙
OCP : 개방-폐쇄 원칙
LSP : 리스코프 치환 원칙
ISP : 인터페이스 분리 원칙
DIP : 의존 역전 원칙


1. Single Responsibility Principle | 단일 책임 원칙

  • 하나의 클래스는 하나의 책임만 가져야 한다.
  • 클래스를 변경하는 이유는 단 하나여야 한다.

2. Open-Closed Principle | 개방-폐쇄 원칙

  • 모듈은 확장에 대해 열려 있어야 하지만, 수정에 대해서는 닫혀 있어야 한다.
  • OCP 구현 예시
    LogOn 함수와 Modem 인터페이스를 사용하여, 새로운 Modem이 추가되어도 LogOn 함수를 수정할 필요가 없도록 설계함.

LSP와 DIP는 OCP를 돕는다.


3. Liskov Substitution Principle | 리스코프 치환 원칙

  • 하위 타입 객체는 상위 타입 객체에서 가능한 행위를 수행할 수 있어야 한다.
  • 상위 타입 객체를 하위 타입 객체로 대체하여도 정상적으로 동작해야 한다.
  • 상속 관계에서는 꼭 일반화 관계(IS-A)가 성립해야 한다.

4. Interface Segregation Principle | 인터페이스 분리 원칙

  • 클라이언트는 자신이 사용하는 메소드에만 의존해야 한다.
  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 한 개보다 낫다.

SRP와 ISP는 객체가 커지는 것을 막는다.


5. Dependency Inversion Principle | 의존 역전 원칙

  • 의존 관계를 맺을 때, 변하기 쉬운 것보다 변하기 어려운 것에 의존해야 한다.
  • 즉, 구현 클래스에 의존하지 말고, 인터페이스에 의존해야 한다.
  • 저수준 모듈이 변경되어도 고수준 모듈은 변경이 필요없는 형태여야 한다.
    ( Figure 2-17 : 절차적 프로그래밍, Figure 2-18 : 객체지향 프로그래밍 )


↩️SOLID 원칙은 상호보완적이다

  • LSP를 위반하는 예시
    원은 타원의 특수한 경우이지만, 상속을 통해 모델링할 때 문제가 발생한다. 원은 중심점과 반지름만 필요하지만, 타원의 속성(초점, 장축)을 상속받게 되어 불필요한 속성이 포함될 수 있다.

처음 생각한 해결방안

  • '원이 타원을 상속받는 것이 아니라, 타원이 원을 상속받도록 설계하면 되지 않나?'
  • but, 첫째는 의미론적으로 '원 is a 타원', '원 ⊂ 타원'이기 때문에, 타원이 원을 상속받는 것은 옳지 않다.
    둘째는 타원과 원 외에 다른 도형을 추가해야 하는 경우, 상속 구조가 복잡해질 수 있다.

그렇기 때문에,

  • LSP를 위반하지 않도록 설계하기 위해 SRP와 ISP 원칙이 필요하다.
    원과 타원의 인터페이스를 분리(ISP)하고, 클래스를 독립적으로 유지(SRP)하여 설계할 수 있다.
    또한, ISP와 SRP를 준수함으로써 새로운 도형을 추가&변경할 때, 다른 도형 클래스에 영향을 미치지 않도록(개방-폐쇄 원칙, OCP) 할 수 있습니다.

✅ (번외) 패키지 아키텍처 원칙

"Design Principles and Design Patterns"에서는 객체지향 클래스 설계 원칙 외에도 패키지 아키텍처 원칙도 포함하고 있습니다.
패키지 아키텍처 원칙에 대해서도 간단하게 정리하고 넘어가겠습니다.

  1. 재사용-배포 등가 원칙 (Reuse-Release Equivalence Principle, REP)
    패키지는 함께 재사용될 수 있는 클래스와 모듈을 포함해야 하며, 릴리스 번호를 통해 버전을 관리해야 한다.

  2. 공통-폐쇄 원칙 (Common Closure Principle, CCP)
    변경의 이유가 동일한 클래스들을 하나의 패키지로 묶어 변경의 영향을 최소화한다.

  3. 공통-재사용 원칙 (Common Reuse Principle, CRP)
    패키지의 클래스들을 함께 재사용함으로써 불필요한 의존성을 줄인다.

  4. 의존성 역전 원칙 (Acyclic Dependencies Principle, ADP)
    패키지 간의 순환 의존성은 빌드 및 테스트를 복잡하게 만들고 변경의 영향을 예측하기 어렵게 한다. 순환 의존성을 제거하기 위해 의존성 역전 또는 새로운 추상화를 도입할 수 있다.

  5. 안정된 의존성 원칙 (Stable Dependencies Principle, SDP)
    변경이 잦은 불안정한 패키지가 안정적인 패키지에 의존하면, 안정적인 패키지가 불필요하게 변경될 가능성이 있다. 안정적인 패키지는 인터페이스나 추상 클래스를 통해 불안정한 패키지에 간접적으로 의존해야 한다.

  6. 안정된 추상화 원칙 (Stable Abstractions Principle, SAP)
    안정적인 패키지는 변경하기 어렵기 때문에 추상적인 인터페이스나 추상 클래스를 포함하여 확장성을 확보해야 한다. 이를 통해 안정성을 유지하면서도 유연하게 변화에 대응할 수 있다.


🌱 마지막 한마디

SOLID 원칙을 지키는 solid한 마음을 갖자

profile
성공의 반대는 실패가 아니라 포기다

0개의 댓글