Spring 기본 2 - SOLID 원칙

홍윤기·2023년 2월 6일
0

DevCamp 스터디

목록 보기
3/3

👋 DevCamp day.2 SOLID 원칙

✍️ SOLID 원칙

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

⭐️ SRP
⭐️ OCP
⭐️ LSP
⭐️ ISP
⭐️ DIP

✍️ SRP란?

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

여기서 하나의 책임이라는 표현이 조금 모호하다. 이는 문맥과 상황에 따라 달라지며 클 수도 있고 작을 수도 있다. 이때 중요한 기준은 "변경"이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이라고 볼 수 있다.

✍️ OCP란?

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

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

🤔 음?

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

public class CarService {
	CarInterface c = new GenesisGV80();	// 기존 코드
	CarInterface c = new PorscheCayenne();	// 변경 코드
}

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

✍️ LSP란?

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

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

public class Book{
	public void read(int page) {
    	page += 1;
    }
}

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

public class ThusSpokeZarathustra extends Book{
	@Override
    public void read(int page) {
   		page -= 1;
    }
}

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

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

✍️ ISP란?

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

interface Car() {
	public void drive();
    public void maintain();
}

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

// 운전자
interface Driver() {
	public void drive();
}
// 정비공
interface Mechanic() {
    public void maintain();
}

✍️ DIP란?

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

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

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

public class CarService {
	CarInterface c = new GenesisGV80();	// 기존 코드
	CarInterface c = new PorscheCayenne();	// 변경 코드
}

Carinterface c = new PorscheCayenne();

이처럼 CarService 클라이언트가 구현 클래스를 직접 선택하고 있다. 이는 DIP 위반이다. 다형성 만으로는 OCP, DIP를 지킬 수 없다. 뭔가가 더 필요하다.

🔍 다음 내용은?

스프링은 DI와 IoC를 통해 OCP, DIP를 가능하게 지원한다. 스프링이 클라이언트 코드의 변경 없이 기능을 확장시키도록 만들고 개발자의 개발을 더 쉽게 만드는 것이다.

따라서 다음 시간은 스프링이 이런 약점을 어떻게 극복하는지 코드로 이해해보겠다.

👩‍🎓 참고사이트 🧑‍🎓

객체 지향 프로그래밍
Spring 개요
스프링 핵심 원리 - 기본편

0개의 댓글