백엔드 개발자라면 대답해야 할 100가지 질문

1. SOLID 원칙

ignuy 2023. 7. 15.

✏️로버트 마틴이 클린코드를 지향하며 고안한 객체지향의 5가지 원칙을 통틀어 객체지향 5원칙(SOLID)이라 칭한다. 일단 한번 보면 개념은 알아듣긴 하지만 막상 실현하려면 생각보다 어려움이 따른다. SRP, OCP, LSP, ISP, DIP - 5개의 원칙의 앞글자를 따서 SOLID라고 부른다.

🌟SRP
🌟OCP
🌟LSP
🌟ISP
🌟DIP

✍️SRP란?

SRP란, Single Responsibility Principle(단일 책임 원칙)으로 '한 클래스는 하나의 책임만 가져야 한다.'는 뜻이다.

 

여기서 하나의 책임이라는 표현은 조금 모호하게 들릴 수 있다. 이는 문맥과 상황에 따라 달라지며 클 수도 있고 작을 수도 있다. 중요한 것은 "변경"이다. 변경이 있을 때 파급효과가 적으면 단일 책임 원칙을 잘 따른 것이라고 볼 수 있다. 하나의 기능을 변경하는데 영향의 파급 범위가 시스템 전체인 스파게티 코드는 SRP를 지키지 않은 대표적인 예시라고 볼 수 있다.

✍️OCP란?

OCP란, Open/Closed Principle(개방/폐쇄 원칙)으로 '소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.'라는 의미이다.

 

모순적으로 느낄 수 있지만 확장을 위해 기존 코드를 변경할 필요가 없다는 것을 이해해야 객체 지향을 이해할 수 있다. 다형성을 활용하여 인터페이스를 구현한 새로운 클래스를 하나 만듦으로써 새로운 기능을 구현할 수 있다. 

🤔흐음..?

OCP 원칙을 고수하기 위해선 문제가 하나 발생한다. 인터페이스의 구현 객체를 변경하기 위해선 java 코드의 수정을 피할 수 없다. 따라서 순수 java 코드로 프로그래밍을 진행한다면 변경에 닫혀있다면 확장에도 닫혀있어야 하고 확장에 열려있으려면 변경에도 열려있어야 한다.

위 코드처럼 GV80을 카이엔으로 변경하는 과정에서 무조건 소스코드의 변경이 일어난다. 분명히 다형성을 사용했지만 OCP 원칙을 고수할 수 없다. 따라서, 객체를 생성하고 연관관계를 맺어주는 별도의 설정자가 필요하다. 이 작업은 java 코드에서 주로 Spring이 도맡는다.

✍️LSP란?

LSP란, Liskov Substitution Principle(리스코프 치환 원칙)으로 '프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.'는 의미이다.

 

단순히 java 소스코드 컴파일에 성공하는 것이 아니라, 구현의 변경이 있어도 기능적으로 동일한 동작이 보장되어야 한다는 것이다.

Book 클래스는 read() 메서드를 실행하면 page가 1씩 증가한다.

니체의 명도서 "차라투스트라는 이렇게 말했다" 클래스는 Book 클래스를 상속하였고 read()라는 메서드를 오버라이딩하였다. 하지만 이 책은 너무나 난해한 나머지 read() 메서드를 실행하면 page가 1씩 감소한다.

이 경우, 컴파일에는 전혀 문제될 것 없지만 부모 클래스에서 규정하고 있는 read 기능을 무시하고 있으므로 LSP에 위배되었다고 말한다.

 

✍️ISP란?

ISP란, Interface Segregation Principle(인터페이스 분리 원칙)으로 '특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.'를 의미한다.

일반적인 인터페이스를 구체적인 여러 인터페이스로 나눈다면 객체지향 관점에서 더 좋은 설계이며 ISP를 고려한 설계가 된다.

✍️DIP란?

DIP란, Dependency Inversion Principle(의존관계 역전 원칙)으로 '프로그래머는 구현체 같은 구체화에 의존하기보다 인터페이스나 추상 클래스와 같은 추상화에 의존해야 한다.'는 의미이다.

 

즉, 클라이언트는 구현 클래스를 바라보는 것이 아니라 인터페이스만 바라봐야 한다. 이는 다형성에서 얘기했던 역할(인터페이스)에 의존해야 한다는 것과 같은 이야기이다.

DIP는 OCP와 밀접한 관련이 있다. DIP가 위배되면 OCP 역시 위배될 가능성이 높다. OCP를 설명할 때 사용한 CarService는 인터페이스에 의존하지만, 구현 클래스에 동시에 의존하고 있다.

Carinterface c = new PorscheCaynne();

이처럼 CarService클라이언트가 구현 클래스를 직접 선태갛고 있다. 이는 DIP위반이다. 다형성만으로는 OCP, DIP를 지킬 수 없다. 뭔가가 더 필요하다. (자바 개발자는 이걸 Spring이라고 부른다.)

 

댓글