-> 블로그 이전

[Spring - 기본] 객체 지향 설계와 스프링

2022. 5. 11. 19:06Language`/Spring

좋은 객체 지향 프로그래밍?

객체 지향의 특징은 매우 기본적인 다음 4가지이다

  • 추상화
  • 캡슐화
  • 상속
  • 다형성

 

"객체 지향" 프로그래밍의 의미는 "절차 지향" 프로그래밍에서 전체적인 명령어를 하나의 흐름으로 보고 처리하는 것과 달리 전체적인 명령어를 "여러개의 독립적인 객체"로 나누어서 프로그래밍 하자는 것이다

 

여기서 나누어진 "독립적인 객체들"은 서로 메시지를 주고받을 수도 있고 데이터를 처리할 수도 있다 :: "협력"

 

그리고 "다형성"이라는 강력한 특징을 활용해서 프로그램을 유연하고 변경이 용이하게 만들 수 있다

 

유연하고 변경이 용이하다는 의미는 집에 있는 콘센트?의 개념과 비슷하다

콘센트라는 것은 규격이 정해져있고 규격에 맞는 플러그들은 전부 꽂을 수 있다

 


다형성

다형성의 의미는 매우 모호하지만 결론적으로 말하자면 "역할과 구현"으로 분리하는 개념이다

 

이러한 관계를 Client - Server 구조라고 일단 생각을 하자

 

결국 Client는 운전자이고 Server는 자동차이고 Client는 Server에 접근을 해서 어떠한 일 처리를 할 것이다

 

Server를 더 자세히 보면 "역할 & 구현"으로 나뉘어져 있다

 

역할은 말그대로 "자동차 역할" :: Client는 비용과 시간을 아끼고 물건을 실어나르기 위해서 자동차를 타고 다닐 것이다

 

이 때 자동차를 그냥 단순히 "엑셀 브레이크 있고 타고 다니는 것"이라고 추상적으로 생각을 해보자

  • 엑셀 : 앞으로 가는 기능
  • 브레이크 : 멈추는 기능

그러면 자동차를 구현한 K3든 아반떼든 페라리든 역할은 "엑셀 브레이크 있고 타고 다니는 것" 전부 동일할 것이다

 

달라지는 것은 외형적으로든 내부적인 기술이든 바뀐다. 하지만 "역할"은 바뀌지 않는다

 

이러한 예제를 토대로 "다형성"을 다음과 같이 정의할 수 있다

1. Client는 역할(인터페이스)만 알면 된다
2. Client는 구현체의 내부 구조를 몰라도 된다
3. Client는 구현체의 내부 구조가 변경되어도 영향을 받지 않는다
4. Client는 구현체 자체를 변경해도 역할만 알고있으면 상관 없다

>> 여기서 알 수 있는 가장 중요한 점은 인터페이스(역할)의 설계를 처음부터 제대로 설계해야 한다는 점이다

 

Spring에서는 이러한 "다형성"을 극대화해서 활용할 수 있도록 여러가지 Content를 제공해준다 

대표적으로 DI가 있다

 


좋은 객체 지향 설계의 5가지 원칙 (Robert C. Martin)

1. SRP (Single Reponsibility Principle) : 단일 책임 원칙

"하나의 클래스는 하나의 책임만 가져야 한다"

그냥 결론적으로 어떠한 "변경"을 가했을 때 해당 클래스가 받는 영향이 없으면 없을수록 그 클래스는 SRP를 잘 따른 것이다

 

2. OCP (Open/Closed Principle) : 개방-폐쇄 원칙 ☆

확장에는 열려있고 변경에는 닫혀있다는 말이 굉장히 모호한 의미를 가지고 있다

 

확장이라는 것은 어떠한 기능을 추가한다고 생각할 수 있고 기능을 추가하려면 결국 코드를 추가한다는 말과 동일한 말이다

>> 여기서 "다형성"을 활용해야 한다

어떠한 "역할"을 구현한 클래스에 대해서 그 클래스를 변경하려고 하지말고 "역할"을 새롭게 구현한 다른 클래스를 하나 더 만들면 된다는 의미이다

 

※ OCP의 문제점

public class MemberService{
    private MemberRepository memberRepository;

    memberRepository = new MemoryMemberRepository();
}

이러한 코드가 원래 있었다고 하고 여기다가 Memory라는 배열말고 MySQL DB를 활용해서 MemberService를 운영하고 싶어졌다

 

그러면 OCP에 따라서 MemoryMemberRepository를 변경하지 말고 인터페이스인 MemberRepository에 대한 새로운 구현체를 하나 더 만들면 된다

public class MemberService{
    private MemberRepository memberRepository;

    //memberRepository = new MemoryMemberRepository();
    memberRepository = new JdbcTemplateMemberRepository();
}

이렇게 바뀐 코드만 보면 OCP를 잘 지킨것처럼 보인다

 

위에서 정의한 "다형성"의 정의를 다시 보게되면 "3. Client는 구현체의 내부 구조가 변경되어도 영향을 받지 않는다, 4. Client는 구현체 자체를 변경해도 역할만 알고있으면 상관 없다"이란 정의가 존재한다

 

위의 코드에서 Client는 결국 MemberService이고 Server는 JdbcTemplateMemberRepository라고 볼 수 있다

 

그런데 구현체 자체를 변경해도 Client는 영향을 받으면 안되는데 결국 MemberService 내부의 코드를 수정하게 되었다

 

이렇게 Client의 코드를 수정하게 되면 결국 OCP를 지키지 않게 된 것이다

 

따라서 객체간의 연관관계를 Mapping해주는 별도의 클래스나 생성자가 존재해야 이러한 문제가 발생하지 않을 것이다

  • 이것은 Spring Container의 기능중 하나인 DI가 해결해줄 것이다

 

3. LSP (Liskov Substitution Principle) : 리스코프 치환 원칙

"프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다"

LSP는 인터페이스에 대한 규약을 반드시 지킨채로 구현하라는 원칙이다

 

예를 들어서 자동차 인터페이스에 "엑셀"기능이 있고 "엑셀"은 앞으로 가야한다라는 규약이 존재한다

그런데 "엑셀"을 뒤로가게 구현했다고 가정하자

 

Complie Error는 발생하지 않겠지만 처음에 설계한 인터페이스의 규약을 지키지 않고 개발을 한것이다

 

따라서 LSP는 그냥 상식적으로 설계한대로 구현하면 지켜지는 원칙이다

 

4. ISP (Interface Segregation Principle) : 인터페이스 분리 원칙

"범용 인터페이스 하나보다 특정 클라이언트를 위한 인터페이스 여러개가 더 낫다"

ISP는 "인터페이스는 구체적일수록 좋다"라는 원칙이다

 

예를 들어서 "자동차 인터페이스"를 더 구체적으로 나눌 수 있다면 "운전 인터페이스 / 정비 인터페이스"로 나눌 수 있을 것이다

 

5. DIP (Dependency Inversion Principle) : 의존관계 역전 원칙

DIP는 "구현 클래스에 의존하지 말고 역할 인터페이스에 의존해라"라는 원칙이다

 

예를 들어서 공연을 하는데 로미오 역할에 A 남배우 & 줄리엣 역할에 B 여배우가 있다고 하자

 

상식적으로 공연 대본에 맞춰서 같이 연습을 하든 혼자서 연습을 하든 하는게 일반적이겠지만 A 남배우와 B 여배우는 공연 대본을 무시한채 그냥 서로 연습을 했다고 하자

 

만약 여기서 B 여배우가 어떤 문제 때문에 C 여배우로 교체되었다고 가정하자

 

그러면 A 남배우 입장에서는 공연 대본이라는 "역할"을 무시한채로 "구현"된 B 여배우와만 연습을 했기 때문에 C 여배우와는 당장 2시간 뒤 공연인데 공연을 할 수가 없을 것이다

 

그런데 만약에 A 남배우가 공연 대본이라는 "역할"에 의존해서 연습을 했다면 상대 줄리엣 역할을 구현한 어떤 여배우와 공연을 하든 상관없이 대본대로 공연하면 된다


위의 OCP의 문제점에서 사용한 예시를 DIP에서 활용하자면

public class MemberService{
    private MemberRepository memberRepository;

    //memberRepository = new MemoryMemberRepository();
    memberRepository = new JdbcTemplateMemberRepository();
}

현재 MemberService라는 Client는 DIP를 지킨다고 하면 Server쪽 "역할"인 MemberRepository만 알고있어야 할 것이다

 

그런데 코드를 보면 "역할"인 MemberRepository와 더불어서 "구현"인 JdbcTemplateMemberRepository도 알고있다

 

따라서 DIP를 위반했다고 볼 수 있다

 

이러한 5가지 원칙을 통해서 "다형성"만으로는 OCP & DIP를 위반하지 않을 수 없다는 것을 알게 되었다

따라서 뭔가 더 필요한데 이 필요한 것들을 Spring에서 지원해준다 :: IoC / DI

 

Spring은 DI라는 기술을 통해서 다형성을 극대화해서 활용하고 객체 지향 5가지 원칙을 잘 지킬 수 있게 해준다