
SOLID
SOLID란 Clean Code의 저자로 유명한 로버트 마틴이 정의한 좋은 객체지향 설계의 5가지 원칙을 정리한 것이다.
- SRP: 단일 책임 원칙
- OCP: 개방-폐쇄 원칙
- LSP: 리스코프 치환원칙
- ISP: 인터페이스 분리 원칙
- DIP: 의존관계 역전 원칙
5가지의 원칙을 하나씩 알아가보도록 하자
단일 책임원칙 - SRP(Single Responsibility Principle)
SRP는 한 클래스는 하나의 책임만 가져야 한다는 것이다.
이 말만으로는 SRP가 무엇인지 와닿지 않는데,
예를 들어서 설명해본다면 어떤 프로젝트에서 UI하나를 고쳤는데 그 안에 SQL, 로직 등 여러가지의 것들을 같이 변경해야한다면 그것은 SRP원칙이 잘 이루어지지 않은 코드이다.
즉, SRP가 잘 지켜졌냐 지켜지지 않았느냐를 판단하는 기준은 '변경'이다.
어떤 코드하나를 변경했을 때 그에 대한 연쇄효과로 다른코드 또한 그 코드에 대해서 맞추어서 변경해야한다면 단일 책임원칙이 잘 지켜지지 않은 것이며, 단일 책임 원칙이 잘 지켜졌다면 후에 유지보수를 할 때 큰 효과를 볼 수 있을 것이다.
개방-폐쇄 원칙 - OCP(Open/Closed Principle)
OCP는 소프트웨어는 확장에는 열려 있으나 변경에는 닫혀 있어야한다는 원칙이다.
여기서 이전에 살펴봤던 '자동차 역할'을 다시 생각하면서 글을 읽어본다면 이해하는데 큰 도움이 될 것이다.
2024.03.20 - [Framework/Spring] - 스프링이란? with 객체지향
먼저 '확장', '변경'이라는 용어를 정의하자면 '확장은' 내가 정의한 역할을 여러가지 구현체를 만들어 나가는 것이 확장이다.
'자동차'에 대해서는 자동차라는 역할을 두고 K3, 아반떼 두 가지의 자동차를 구현해놨지만 여기서 G70, 산타페 차종을 '자동차'역할을 구현하여 차종을 추가한다면 자동차의 구현체는 2가지에서 4가지로 확장되었다. OCP에서 말하는 확장에는 열려있어야한다는 '확장'의 의미가 바로 구현체의 확장을 의미한다.
그 다음은 '변경'에는 닫혀있어야한다고 말한다.
변경은 이전 포스팅해서 설명했던 MemberService클라이언트 그림을 떠올려보자
MemberService클라이언트는 MemberRepository를 의존하며 MemoryMemberRepository와 JDBCMemberRepository는 MemberRepository인터페이스의 구현체이다.
이 때 MemberService클라이언트는 클라이언트의 코드 변경없이도 JDBC 또는 Memory 두 개의 서버중 하나의 서버를 선택하여 서비스를 받을 수 있다.

하지만 위 코드를 보면 알 수 있듯이 서버를 선택하기 위해서는 둘 중 하나의 서버는 주석처리를 해두고 하나의 서버를 선택하여 사용한다는 것을 알 수 있다.
이렇게 되면 OCP원칙의 변경에는 닫혀있어야한다는 것을 위배하는 것이다.
OCP를 위배하지 않게 하기위해서 Spring 컨테이너를 사용하는 것이다.
스프링 컨테이너가 객체를 관리하여 변경에는 닫혀있는 코드를 만들 수 있게 해준다.
리스코프 치환 원칙 - LSP(Liskov Substitution Principle)
리스코프 치환 원칙은 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다고 정의한다.
만약에 자동차 인터페이스의 excel()기능은 자동차를 앞으로 전진시키기위한 목적으로 만들어진 기능인데, 이 기능을 구현체에서 excel()기능 수행시 자동차를 뒤로 가게 만든다면 이것은 리스코프 치환원칙을 위배하는 행위이다.
excel()기능은 앞으로 가는 동작을 수행하기위해 정의된 기능이기 때문에 천천히갈 수 있고, 매우 빠르게 갈수도있지만 반드시 '앞으로 간다'라는 동작을 수행해야한다.
인터페이스 분리원칙 - ISP(Interface Sergregation Principle)
인터페이스는 말 그대로 인터페이스를 분리시켜야한다는 원칙이다.
그렇다면 어떻게 분리할 것이고 어떤 목적으로 분리할 것인가? 를 생각하면 쉽게 이해할 수 있을 것이다.
예를들어서 설명하자면 자동차 인터페이스에는 자동차 주행에 관련된 기능들도 있을 것이고 자동차 정비에 대한 기능들도 정의되어있을 것이다.
이 때 자동차 주행 인터페이스와 자동차 정비 인터페이스 두 가지를 나누어 정의한다면 후에 정비인터페이스를 수정한다면 정비 인터페이스만 수정하고 주행 인터페이스에는 영향을 주지 않는다 이렇게 각 클라이언트 간에 영향을 최소화하도록 인터페이스를 설계한다면 인터페이스가 명확해지고, 대체 가능성이 높아지게 된다.
즉, 인터페이스 분리원칙은 하나의 범용인터페이스로 설계하지 않고 해당 인터페이스를 최대한 잘게 쪼개어 설계하라는 원칙이다.
의존관계 역전 원칙 - DIP(Dependency Inversion Principle)
의존관계 역전 원칙은 프로그래머는 구현체에 의존하지않고 추상화에 의존해야한다고 정의한다.
추상화는 인터페이스를 의미하고 구현체는 그 인터페이스를 구현한 클래스를 의미하는 것이다.
즉, 자동차의 예시로 설명하면 추상화는 Car 인터페이스 클래스를 의미하고, 구현체는 Car 인터페이스를 구현한 아반떼, K3를 의미하는 것이다.
위에 OCP에서도 설명했듯이
MemberRepository 인터페이스를 정의해놓고
MemoryMemberRepository 구현체,
JDBCMemberRepository 구현체 두 가지의 서버를 선택해서 이용하려고 할 때,
다형성만을 사용해서는 DIP에서 정의한 원칙을 지킬수가 없다
MemberRepository를 사용하기 위해서는 반드시 new JDBCMemberRepository를 해주어야 하듯이 구현체를 의존할 수 밖에 없게된다.
그렇다면 DIP는 어떻게 지켜야하는 것일까?
이 DIP를 지키기위해 Spring Framework를 사용하며 이것이 바로 스프링 프레임워크는 Java의 객체지향 특성을 극대화 시켜준다고한 의미에 해답이다.
'Framework > Spring' 카테고리의 다른 글
Spring MVC - @RequestMapping (0) | 2024.04.16 |
---|---|
Spring MVC - 프레임워크 사용 전과 후 & @Controller (0) | 2024.04.15 |
Spring MVC - Dispatcher Servlet과 View Resolver (0) | 2024.04.15 |
스프링 컨테이너와 싱글톤 (0) | 2024.03.23 |
스프링이란? with 객체지향 (0) | 2024.03.20 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!