Spring

[Spring] 객체지향설계(SOLID)와 스프링

이덩우 2023. 6. 19. 01:38

- 스프링?

  • 스프링은 자바 언어 기반의 프레임워크이다.
  • 자바의 가장 큰 특징이 뭘까? - 객체 지향성이다.
  • 결국 스프링은 객체 지향 언어가 가진 강력한 특징들을 살려낼 수 있는 프레임워크이다.
  • 그럼 살려내야 할 객체 지향 특징이 뭘까?

- 객체 지향의 특징

  • 객체 지향의 특징으로는 추상화, 캡슐화, 상속, 다형성 등이 있다. 각각의 특징들을 본 포스팅에서 서술하진 않겠다. 
  • 그래서 객체 지향적으로 프로그래밍을 하면 좋은 점이 뭘까? 추상화니, 다형성이니 어려운 말 쓰지말고 생각해보자.
  • 결론부터 이야기 하자면, 객체 지향 언어의 가장 큰 장점은 "유연성" 이라고 생각한다. 
  • 객체 지향 프로그래밍은 코드 자체를 명령어들의 집합으로 보는 시선을 벗어나, 독립적인 단위로 구성할 수 있고 상호간의 교류가 가능한 "객체"들의 모임으로 생각한다.
  • 이러한 객체의 모임 방식으로 코드를 구성한다면 무언가 수정이 필요한 경우, 해당 클래스만 수정함으로써 원하는 결과를 만들어낼 수 있다. 
  • 마치 각종 필요한 부품을 조립해서 컴퓨터가 완성되듯이, 필요한 레고 조각을 모아서 작품을 완성하듯이 객체들의 모임으로 원하는 프로그램을 만들 수 있다는 것이 객체 지향 언어의 가장 큰 장점이다.
  • 이러한 레고 조립과 같은 특성을 더욱 잘 살려낼 수 있는 "다형성"에 대해서 자세히 알아보자.

- 다형성, 역할과 구현의 분리

  • 다형성은 코드를 역할과 구현으로 나눠주는 특징이다.
  • 무슨 말일까? 다음 예시를 보자.

역할과 구현의 분리

  • 위 상황은 로미오와 줄리엣을 각각 연기할 수 있는 배우들을 나열해놨다.
  • 만약, 로미오라는 "역할"에 대한 "구현체"로 장동건 배우가 발탁되었다면, 줄리엣이라는"역할"에 대한 "구현체"로 어떤 배우가 오던 상관없이 대본에 충실한 채로 연기해야만 할 것이다. 상대가 누구던 상관없이 역할에 충실하는게  훌륭한 배우가 아니겠는가?
  • 이와 같이 역할과 구현을 구분해, 역할에 충실하도록 하는게 바로 "다형성"이다. 이번에는 실제 자바 코드를 예시로 보자.

자바에서 역할과 구현의 분리

  • MemberRepository를 호출하는 MemberService(클라이언트) 입장에서는 저장소라는 "역할"에만 관심이 있을 뿐이지 실제로 로컬 메모리를 사용하는 저장소인지, 상용 데이터베이스를 사용하는 저장소인지는 궁금하지가 않다. 뭐가 됐든 저장소라는 "역할"을 원해서 호출했을 뿐이다.
  • 다른 관점으로 이야기 해보자. 실제 개발 환경에서, 어떤 데이터베이스를 사용해 프로덕션 환경을 구성할지 결정이 안됐다면, 개발자들은 결정이 될 때까지 마냥 기다려야 하는가? 절대 아니다.
  • 위 사진에서 보듯이, 저장소의 "역할"을 맡고있는 인터페이스를 만들고 실제 해당 역할의 구현체를 만들어서 사용하면 어떤 저장소를 필요로 하던지 갈아 끼우기만 하면 된다! --> 오버라이딩을 통해 가능
  • 이것이 다형성의 강력함이다.
public class MemberService {
	
//    private MemberRepository memberRepository = new MemoryMemberRepository();
      private MemberRepository memberRepository = new DBMemberRepository();
    
/*           역할(인터페이스)   =   new 구현체(상속받은 클래스) 형식              */ 
/*                  구현체만 갈아끼우면 조립 완성                              */

}
  • 이처럼 역할과 구현의 분리를 통해 클라이언트에 영향을 주지 않는 변경이 가능해졌다.
  • 스프링은 이러한 다형성을 극대화해서 이용할 수 있도록 도와준다.
  • 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원한다.

 

- 좋은 객체 지향 설계의 5가지 원칙 (SOLID)

  • SRP : 단일 책임 원칙(Single Responsibility Priciple)
    - 한 클래스는 하나의 책임만 가져야 한다.
    - 하나의 책임이라는 것은 문맥과 상황에 따라 다르다. 
    - 코드의 변경이 있을 때, 변경에 따른 파급효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
  • OCP : 개방 - 폐쇄 원칙 (Open/Closed Principle)
    - 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
    - 다형성을 생각해보자. 레고의 조립처럼 확장에는 열려있었고 최소한의 변경을 요구하도록 코드를 구성했다.
    - 사실 아직 다형성 만으로 OCP를 완벽하게 지킬 순 없다. 위 예제도 클라이언트 코드를 어찌됐건 변경을 했다.
    - 뒤로 나아갈수록 OCP를 지키기 위한 방식이 나온다.
  • LSP : 리스코프 치환 원칙 (Liskov Substitution Principle)
    - 다형성에서, 하위 클래스는 인터페이스의 규약을 다 지켜야한다.
    - 예를 들어, 전진 기능의 인터페이스를 만들었다. 이를 상속받은 하위 클래스가 물론 코드상으로 엑셀을 밟을 때 -30의 속도를 내도록 코드를 작성할 수 있겠지만, 이는 인터페이스의 규약을 따르지 않은 것이다.
  • ISP : 인터페이스 분리 원칙 (Interface Segregation Principle)
    - 특정 클라이언트를 위한 인터페이스는 하나보다 여러 개가 낫다
    - 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리
    - 분리하면 각각의 목적이 분명해진다.
  • DIP : 의존관계 역전 원칙 (Dependency Inversion Principle)
    - 추상화(인터페이스)에 의존해야지, 구체화(상속받은 클래스)에 의존하면 안된다.
    - 계속 이야기한 역할 자체에 초점을 둬야한다는 원칙이다.
    - 위 예제 코드를 보면, 클라이언트인 MemberService는 역할(MemberRepository)에도 의존하고
    구현체(Memory & DB Repository)에도 의존한다. --> DIP 위반
    - 이를 해결하기 위한 방법은 OCP와 마찬가지로 뒤로 갈수록 나온다.

- 정리

  • 객체 지향의 핵심은 다형성이다.
  • 오직 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수는 없다.
  • 다형성 만으로는 OCP, DIP를 지킬 수 없다.
  • 뭔가 더 필요하다.
  • 스포하자면, 스프링은 DI(Dependency Injection), DI 컨테이너를 통해 OCP, DIP를 가능하게 지원한다.

 

 

 

출처 : 인프런, 김영한의 스프링 핵심 원리 - 기본편