이번 포스팅에서는 객체 지향 설계 5대 원칙이라고 불리는 "SOLID"에 대해서 공부한 내용을 정리해 보고자 합니다.
1. 객체 지향 설계 5대 원칙 : SOLID
- 객체 지향 설계 5대 원칙, SOLID는 클린 코드(Clean code)의 저자로 유명한 로버트 마틴이 좋은 객체 지향 설계를 위해 5가지 원칙을 정리한 것을 의미합니다.
- 크게 아래와 같이 5가지 원칙이 존재합니다.
(1) SRP(Single Responsibility Principle) : 단일 책임 원칙
(2) OCP(Open-Closed Principle) : 개방 - 폐쇄 원칙
(3) LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
(4) ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
(5) DIP(Dependency Inversion Principle) : 의존 관계 역전 원칙
2. Single Responsibility Principle (SRP) : 단일 책임 원칙
- 단일 책임 원칙(SRP)은 클래스(객체)는 단 하나의 책임만을 가져야 한다는 원칙을 의미합니다.
- 여기서 책임(Responsibility)이라는 단어가 모호한 표현일 수 있습니다. (책임이 크거나 작거나 또는 문맥과 상황에 따라 달라질 수 있기 때문입니다.)
- 중요한 것은 클래스는 단 하나의 책임을 가지고 하나의 역할만 수행할 수 있도록 설계되어 있어야 한다는 뜻이며 만약 역할이 변경된다고 하더라도 역할 변경으로 인한 파급 효과가 적어진다면 단일 책임 원칙을 잘 따른 것이라고 볼 수 있습니다.
- 하나의 클래스에 여러 가지 책임과 기능이 들어있다면 만약 해당 클래스를 수정(기능 수정)해야 할 때, 유기적으로 복잡하게 연결된 코드로 인해 코드의 수정으로 인한 파급 효과가 커지므로 코드 변경부터 테스트까지 많은 시간이 소요되며 꼬리물기식으로 책임이 순환될 수 있기에 되도록이면 하나의 클래스에 하나의 책임(기능)만 부여하여 파급 효과를 최대한 줄여야 단일 책임 원칙을 잘 지킨 코드라고 할 수 있습니다.
3. Open-Closed Principle (OCP) : 개방 - 폐쇄 원칙
- 개방 - 폐쇄 원칙(OCP)은 소프트웨어 요소의 확장 측면에선 열려 있으나(Open), 변경(수정) 시엔 닫혀 있어야(Closed) 한다는 원칙입니다.
- 이는 다형성을 활용하여 인터페이스를 구현한 클래스를 만들어 새로운 기능을 구현하는 부분으로 한 번 생각해 보겠습니다.
- 이를 통해 개방 - 폐쇄 원칙은 기존의 코드를 변경하지 않으면서 새로운 기능을 추가(확장)할 수 있도록 설계되어야 한다는 원칙을 의미하며 이는 기존 코드 변경없이, 새로운 요구 사항(기능)을 적용할 수 있도록 코드를 설계해야 한다는 뜻이기도 합니다.
- 변경(수정)에는 닫혀 있어야 하는 이유는, 새로운 요구 사항이 발생했을 때 만약 객체의 대부분을 수정해야 하는 상황이 생겼다면 해당 코드는 유연하게 대처할 수 없는 코드라고 인식될 수밖에 없기 때문입니다. (유지보수성 감소하게 된다.)
4. Liskov Substitution Principle (LSP) : 리스코프 치환 원칙
- 리스코프 치환 원칙(LSP)은 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 교체할 수 있어야 한다는 원칙입니다.
- 핵심은, 자식 클래스는 최소한 자신의 부모(슈퍼) 클래스에서 가능한 행위의 수행이 모두 보장되어야 한다는 의미이기도 합니다.
- 다형성에서 자식 클래스는 해당 상위 클래스의 인터페이스 규약을 모두 지켜야 하고 이는 다형성을 지원하기 위한 원칙이기도 하며 인터페이스를 구현한 구현체는 믿고 사용하려면 해당 원칙이 필요하게 됩니다.
- 예를 들면, 이전 포스팅에서 소개된 자동차 인터페이스에서 액셀 기능은 앞으로만 움직이는 기능이고 만약 뒤로 움직이도록 구현했다면 이는 리스코프 치환 원칙(LSP)을 위반한 코드입니다.
5. Interface Segregation Principle (ISP) : 인터페이스 분리 원칙
- 인터페이스 분리 원칙(ISP)은 인터페이스를 사용 목적에 맞게 각각 분리할 수 있어야 한다는 원칙으로, 범용적인 인터페이스보다는 클라이언트의 목적에 맞게 효율적으로 인터페이스를 분리하여 제공해야 한다는 원칙이기도 합니다.
- 예시)
자동차 인터페이스 → 운전 기능 인터페이스, 정비 인터페이스 등으로 분리
사용자 클라이언트 → 운전자 클라이언트, 정비사 클라이언트 등으로 분리
- 이를 통해 인터페이스(역할)가 명확해지고, 대체 가능성이 높아지게 됩니다.
6. Dependency Inversion Principle (DIP) : 의존 관계 역전 원칙
- 의존 관계 역전 원칙(DIP)은 추상화에 의존하고, 구현된 클래스에 의존하지 않아야 된다는 원칙입니다.
- 쉽게 말해 구현 클래스에 의존하지 않고, 추상화된 인터페이스에 의존하라는 뜻을 가집니다.
- 앞에서 자주 언급된 역할에 의존하게 해야 한다는 것과 같습니다. 객체 세상도 클라이언트가 인터페이스에 의존하게 설계되어야 이후 요구사항 발생 시 유연하게 구현체를 변경할 수 있게 됩니다. 만약 구현 클래스에 의존하게 되면 유지보수가 어려워질 수밖에 없습니다.
- 객체들이 서로 정보를 주고받을 때, 의존 관계가 형성되며 객체들은 정의된 원칙을 갖고 정보를 주고받습니다. 이때 정의된 원칙이란 추상화가 높은 상위 클래스(인터페이스)와 통신해야 한다는 원칙을 가져야 하며 이렇게 작성된 코드가 의존 관계 역전 원칙(DIP)을 잘 지켜가며 작성한 코드라고 볼 수 있습니다.
- 위의 코드를 보면 MemberService는 인터페이스에 의존하고 있지만 동시에 구현 클래스에도 동시에 의존하고 있으므로 DIP를 위반한 코드이며,
// private MemberRepository memberRepository = new MemoryMemberRepository(); // 기존 코드
private MemberRepository memberRepository = new JdbcMemberRepository();
- MemberService 클라이언트가 구현 객체를 직접 선택하고 있는 상황에서 구현 객체 변경을 위해 클라이언트 코드까지 수정해야 하는 상황까지 발생했으므로 분명 다형성을 이용했지만 DIP와 함께 OCP까지 위반되었습니다.
- 스프링에서는 이러한 문제를 해결하기 위해 객체를 생성한 후 별도의 연관 관계를 맺어주는 추가적인 설정자가 필요하다는 한계점에 도달하게 됩니다.
7. 객체 지향 설계와 Spring 프레임워크
- 위에서 분명 다형성을 이용했지만 OCP, DIP가 위반되는 상황이 발생하게 되었습니다.
- 이를 극복하기 위해 스프링 프레임워크에선 DI(Dependency Injection), DI 컨테이너를 제공함으로써 OCP, DIP를 지키면서 코드를 설계할 수 있도록 지원하고 있습니다.
- 클라이언트의 코드 변경 없이 확장이 가능하며 쉽게 부품을 교체하듯이 코드를 설계할 수 있습니다.
- 순수 자바 코드로만은 OCP, DIP를 모두 지킬 수 없었기에 과거에 개발자들이 DI 컨테이너를 개발(스프링 프레임워크의 핵심 기술)하게 되었습니다.
8. 내용 정리
(1) 모든 설계 구조에 역할과 구현을 분리하여 설계합니다.
(2) 이전 포스팅의 자동차와 운전자의 역할, 공연 예시를 떠올려 볼 수 있습니다.
(3) 이상적인 설계 방법으로 모든 설계에 인터페이스를 부여할 수 있으나 실무에서는 인터페이스 도입 시 추상화라는 비용이 발생하게 되므로 만약 기능을 확장할 일이 없다면, 구체 클래스를 직접적으로 사용하고 향후 요구 사항 발생 시 코드 리팩터링을 통해 인터페이스를 도입해 보는 방법도 실무에서 고민해 볼 수 있는 레벨입니다.
========================================================================
※ 해당 포스팅은 InFlearn에서 현재 우아한형제들, 배달의 민족 서비스 개발팀장(기술이사)으로 재직 중이신 김영한님의 "Spring 핵심 원리 - 기본편" 강의를 듣고 공부한 내용을 정리하였습니다.
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
※ 해당 포스팅에 대해 내용 추가가 필요하다고 생각되면 기존 포스팅 내용에 다른 내용이 추가될 수 있습니다.
개인적으로 공부하며 정리한 내용이기에 오타나 틀린 부분이 있을 수 있으며, 이에 대해 지적해 주시면 감사하겠습니다.
댓글