오늘은 Solid 원칙에 대해 작성해보려고 한다.
Solid 원칙은 코드를 더 깨끗하고 견고하게 만들기 위해 개발자들이 따라야 할 기본 가이드라인으로, Robert C. Martin이 제안했다. 객체 지향 프로그래밍에서 코드의 유지보수성, 확장성, 재사용성을 높이는 데 도움을 주는 다섯 가지 설계 원칙이다. 자세한 내용은 아래에 정리하려고 한다.
1. 단일 책임 원칙 (Single Responsibility Principle, SRP)
"클래스는 하나의 책임만 가져야 한다."
- 하나의 객체는 하나의 책임만 담당
- 책임을 적절히 분배하면서 책임 변경이 다른 책임의 변경으로 전파되지 않음.
- 코드 가독성이 좋아지고, 유지보수가 쉬워짐
- 책임을 드러낼 수 있는 명확한 이름으로 클래스를 생성하는게 좋음
- 낮은 결합도와 높은 응집도 고려
예시
사용자 정보 관리 시스템에서 User 클래스가 데이터베이스와의 상호작용 및 UI 로직을 모두 처리한다면 책임이 너무 많아진다. 이를 데이터베이스와 상호작용하는 UserRepository와 UI를 처리하는 UserView 클래스로 분리하면 SRP를 준수할 수 있다.
2. 개방-폐쇄 원칙 (Open-Closed Principle, OCP)
"클래스는 확장에 열려 있어야 하고, 수정에는 닫혀 있어야 한다."
- 기존 코드를 변경하지 않으면서 기능을 추가할 수 있어야함
- 변경 사항이 발생해도 손쉽게 코드를 수정할 수 있음
- 객체지향 프로그래밍의 추상화를 의미
- abstract 또는 interface 키워드를 이용
- 의존 역전 원칙(DIP)의 설계 기반이 됨
예시
사용자 정보 관리 시스템에서 User 클래스가 데이터베이스와의 상호작용 및 UI 로직을 모두 처리한다면 책임이 너무 많아집니다. 이를 데이터베이스와 상호작용하는 UserRepository와 UI를 처리하는 UserView 클래스로 분리하면 SRP를 준수할 수 있습니다.
3. 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
"서브타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다."
- 부모 클래스와 자식 클래스 사이의 행위가 일관성이 있어야 함
- 부모 클래스 상속 시 정의한 원칙을 그대로 따라야 함
- LSP는 협업 시 개발자 간의 약속을 지키는 원칙이기도 함
- 객체 지향 프로그래밍의 다형성을 의미
예시
Bird 클래스를 상속받은 Penguin 클래스가 있다고 가정해 보자. Bird 클래스에 fly() 메서드가 있다면 Penguin은 이 메서드를 사용할 수 없으므로 LSP를 위반하게 된다. 대신, Flyable 인터페이스를 만들고 Bird는 이 인터페이스를 구현하지 않는 방식으로 해결할 수 있다.
4. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
"클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다."
- 클라이언트의 목적과 용도에 적합한 인터페이스 만을 제공
- 유연하게 클래스의 기능을 확장하거나 수정할 수 있다
- SRP는 클래스의 단일 책임을 강조하고, ISP는 인터페이스의 단일 책임을 강조한다
- 인터페이스 분리는 변경사항을 예측하고 설계해야 하는데, 이는 개발자의 경험과 역량이 중요함
예시
프린터 시스템을 설계할 때 Printer 인터페이스가 print(), scan(), fax() 메서드를 모두 갖고 있다면, 단순 프린터 클래스는 필요하지 않은 scan()과 fax() 메서드를 구현해야 할 수 있다. 이를 해결하기 위해 Printable, Scannable, Faxable 등 세부 인터페이스로 나누는 것이 좋다.
5. 의존성 역전 원칙 (Dependency Inversion Principle, DIP)
"고수준 모듈은 저수준 모듈에 의존해서는 안 된다. 둘 다 추상화에 의존해야 한다.".
- 대상을 참조할 때는 추상화된 요소(Interface, abstract class)를 참조
- 추상화된 요소를 구현한 내용이 저수준 클래스(모듈)가 된다
- 고수준 클래스는 변경이 적고, 저수준 모듈은 변경이 잦다.
- 변경에 영향을 덜 받는 구조일수록 재사용성, 확장성 및 유닛테스트가 쉬워짐
예시
쇼핑몰 애플리케이션에서 주문 처리를 담당하는 OrderService가 PaymentProcessor라는 인터페이스에 의존하게 설계한다면, OrderService는 실제 결제 구현에 구애받지 않게 된다. 이를 통해 나중에 다른 결제 방식으로 변경하더라도 OrderService를 수정할 필요가 없다.
마치며
SOLID 원칙을 준수하면 코드의 가독성, 유지보수성, 확장성을 높일 수 있으며, 장기적으로 안정적인 소프트웨어를 개발하는 데 큰 도움이 됩니다. 이러한 원칙은 복잡한 시스템일수록 그 효과가 두드러지며, 팀 단위로 개발하는 환경에서 특히 중요한 가이드라인이 됩니다.
아직 이해가 부족하지만 SOLID 원칙을 이해하고 적절히 활용할 계획입니다.