Kim-Baek 개발자 이야기

[스프링 핵심원리] 4. 좋은 객체 지향 설계의 5가지 원칙 (SOLID) 본문

개발/Spring

[스프링 핵심원리] 4. 좋은 객체 지향 설계의 5가지 원칙 (SOLID)

김백개발자 2021. 9. 24. 17:38
김영한님의 [스프링 핵심 원리] 강의를 정리하고, 내가 생각한 내용까지 정리하는 포스팅

이전 포스팅에서는 좋은 객체 지향 프로그래밍이 어떤 것인지에 대해서 알아보았다. 말로만 좋은 객체 지향을 하라고 하면 어렵기 때문에 이미 여러 뛰어난 개발자가 만들어놓은 좋은 객체 지향 설계의 원칙이 있다.

앞글자를 따서 SOLID 라고도 불리는데, 대학생 때 공부를 했던 기억이 나는데, 중요한 내용이 만큼 잘 알아두면 좋다.

클린 코드라는 책을 쓴 유명한 로버트 마틴이 정리한 내용이다. 로버트 마틴이라는 분은 클린 코드도 그렇고 코드를 보기좋고 깔끔하게 작성하는 것을 굉장히 강조하는 것 같다.

SRP 단일 책임 원칙

말 그대로 하나의 클래스에서는 하나의 책임만을 가져야 한다는 것이다. 사실 상당히 애매한 말일 수 있다. 책임의 범위를 크게하냐 작게 하냐에 따라서 달라지는 내용이기 때문이다.

하지만 중요한 기준은 변경이다. 해당 클래스에서 변경이 있을 때 파급 효과가 적다면 단일 책임 원칙을 잘 지키고 있다고 생각할 수 있다.

예를 들어서, UI 버튼이 하나가 변경이 되었는데 DB 쿼리까지 변경되는 일이 발생한다면 이것은 단일 책임 원칙이 잘 지켜지고 있다고는 할 수 없는 것이다.

OCP 개방-폐쇄 원칙

소프트웨어 요소가 확장에는 열려있으나, 변경에는 닫혀 있어야 한다는 뜻이다. DIP와 더불어 가장 중요한 원칙인데, 이전에 본 다형성을 생각해보면 쉽다. 

public class MemberService {
	//아래의 두 개중 필요한 것을 선택해서 사용할 수 있게 된다.
	MemberRepository memberRepository = new MemoryMemberRepository();
    
    MemberRepository memberRepository = new JdbcMemberRepository();
}

클라이언트는 역할만 보고 있으면 구현체가 변경되더라도 상관이 없다는 것이다. 변경이 없다는 것을 코드의 변경이 없다는 것으로 이해하면 조금 더 편하다. 그런데 여기서 보면, MemberRepository라는 역할을 갖기지만 실제 구현 객체는 클라이언트가 코드를 변경해서 사용하고 있는 것을 알 수 있다.

다형성을 사용하고 있지만, OCP가 지켜지지 않는 것이다. 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다. ( 나중 강의에서 설명할 내용 )

LSP 리스코프 치환 원칙

프로그램의 정확성을 깨지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 다형성에서 하위 클래스는 인터페이스의 규약을 다 지키게 된다. 다형성을 지키기 위해서 만들어진 원칙인데, 사실 인터페이스만 implement 하면 컴파일은 성공하게 된다.

그러나 이 규약은 컴파일 성공을 넘어서, 기능까지를 이야기하는 원칙이라고 할 수 있다. 예를 들어서 자동차 역할을 구현한 K3는 엑셀 기능은 앞으로 가는 기능이여야 한다. 테슬라도 마찬가지로 엑셀은 기능적으로 앞으로 가야하는 것이다. 엑셀을 밟았을 때 뒤로 가더라도 자동차의 역할은 가져온 것이나 기능적으로 정확성을 깨트린 것이기 때문에 LSP 원칙 위반이다.

ISP 인터페이스 분리 원칙

특정 클라이언트를 위한 인터페이스 여러 개가 범용적인 인터페이스 하나보다 더 낫다는 원칙이다. 자동차 인터페이스를 만약에 운전 인터페이스와 정비 인터페이스로 분리를 했다고 해보자. 그리고 사용자 클라이언트는 운전자 클라이언트와 정비사 클라이언트로 분리를 한다.

만약 정비 인터페이스에서 수정이 일어났다고 하면 운전자 클라이언트에는 영향이 없을 것이다. 하지만 범용적인 자동차 인터페이스로 사용하고 있었다면 이를 사용하던 운전자 클라이언트, 정비사 클라이언트 모두가 수정이 필요했을 것이다.

이를 통해서 인터페이스가 더 명확해지고, 대체 가능성이 높아진다.

DIP 의존관계 역전 원칙

프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다" 라는 원칙이다. 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻이다.

예전에 봤던 예시를 보자. 운전자가 구현 클래스라 할 수 있는 K3에 의존을 하고 있었다고 해보자. K3만 알고, K3에 존재하는 기능들만 운전자는 알고 있는 것이다. 자동차가 테슬라 모델3로 변경되면 어떨까? 실제 세계에서는 기본적으로 운전하는 것은 가능하겠지만, K3만 알고 있다는 가정으로는 테슬라의 기능은 사용할 수 없게 된다고 보면 된다.

public class MemberService {
	//아래의 두 개중 필요한 것을 선택해서 사용할 수 있게 된다.
	MemberRepository memberRepository = new MemoryMemberRepository();
    
    MemberRepository memberRepository = new JdbcMemberRepository();
}

이 코드의 경우도 MemeberRepository 라는 인터페이스에 의존을 하고 있지만, 실제로 구현 클래스도 동시에 의존하고 있다. 의존하고 있다는 것은 해당 코드를 알고 있다는 것이라고 보면 된다.

이렇게 객체 지향 설계의 5가지 원칙을 지키기 위해서는 추가적으로 필요한 것들이 존재한다. 그것이 바로 다음부터 설명하게 되는 스프링인 것이다.

 

 

반응형
Comments