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

kdkdhoho·2022년 2월 15일
0

Spring

목록 보기
7/26

이 글은 인프런 - 스프링 핵심 원리 기본편을 보고 공부한 것을 정리한 글입니다.

  • 스프링의 핵심 가치 => 객체 지향 프로그래밍

  • 강의를 학습할 때 어디서, 어떻게, 왜 스프링 기술을 적용하는지 이해하는 것이 중요

  • 스프링의 Core Concept

    1. 스프링은 자바 언어 기반의 프레임워크
      자바 언어의 가장 큰 특징 => 객체 지향 언어

    2. 스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크

    3. 스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크

객체 지향 특징

  1. 추상화
  2. 캡슐화
  3. 상속
  4. 다형성

객체 지향 프로그래밍

객체 지향 프로그래밍은 컴퓨터 프로그램의 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다.
각각의 객체메시지를 주고받고, 데이터를 처리할 수 있다. (협력)

객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.

  • 유연하고 변경이 용이?
    => 레고 블럭 조립하듯이, 키보드, 마우스 갈아 끼우듯이..
    => 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법
    => 다형성

다형성

역할구현을 분리

  • 역할구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리해진다.

  • 장점

    1. 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
    2. 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
    3. 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
    4. 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.
  • 자바에서의 다형성 활용

    • 역할 = 인터페이스, 구현 = 구현 객체
    • 객체를 설계할 때, 역할구현을 명확히 분리
    • 객체 설계시 역할(인터페이스)를 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기
    • 객체의 협력이라는 관계부터 생각
      • 혼자 있는 객체는 없다.
      • 클라이언트: 요청, 서버: 응답&요청
      • 수 많은 객체 클라이언트와 객체 서버는 서로 협력 관계를 가짐
  • 다형성의 본질

    • 인터페이스를 구현한 객체 인스턴스를 실행 시점유연하게 변경할 수 있다.
    • 다형성의 본질을 이해하려면 협력이라는 객체사이의 관계에서 시작해야함
    • 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
  • 인터페이스를 안정적으로 잘 설계하는 것이 중요.
    만약 인터페이스가 변경된다면, 서버, 클라이언트 모두에 큰 변경영향이 발생한다.
    USB 인터페이스가 변경된다면?

스프링과 객체 지향

스프링은 위에서 언급한 다형성극대화해서 이용할 수 있게 지원
=> 스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원

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

SOLID
1. SRP(Single Responsibility Principle): 단일 책임 원칙
2. OCP(Open/Closed Priciple): 개방-폐쇄 원칙
3. LSP(Liskov Substitution Principle): 리스코프 치환 원칙
4. ISP(Interface Segregation Principle): 인터페이스 분리 원칙
5. DIP(Dependency Inversion Principle): 의존관계 역전 원칙

1. SRP

  • Single Responsibility Principle. 단일 책임 원칙

  • 한 클래스는 하나의 책임만 가져야 한다.
    하지만, 하나의 책임이라는 것은 문맥과 상황에 따라 다르다.

  • 중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것
    예) UI변경 시 sql코드부터 어플리케이션 수정에 들어가야한다면 단일 책임 원칙을 잘 따르지 못한 것

2. OCP (중요)

Open-Closed Principle. 개방-폐쇄 원칙

  • 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다.
    => 다형성 활용
  • 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현

  • 예) 스프링 강의 입문편에서 MemoryMemberRepository에서 JdbcMemberRepository로 확장하는 것

  • 문제점

    • 이전 실습 예제 코드에서, MemberService 클라이언트가 MemberRepository 를 선택할 때,
      기존 MemberRepository m = new MemoryMemberRepository(); 에서
      MemberRepository m = new JdbcMemberRepository();변경을 해주어야 한다.

    • 여기서 OCP 원칙이 깨지게 되는 것이다.

    • 이러한 문제점을 해결하기 위해 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다.
      이것을 스프링이 해결해줌

3. LSP

Liskov Substitution Principle. 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨드리지 않으면서 하위 타입의 인스턴스를 바꿀 수 있어야 한다.
    이 말은, 구현체가 인터페이스의 기능과 역할을 어겨서는 안된다는 의미

  • 좀 더 풀어쓰자면, 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것.

  • 다형성을 지원하기 위한 원칙이며,
    인터페이스를 구현한 구현체는 믿고 사용하려면 이 원칙이 필요하다.

4. ISP

Interface Segregation Principle. 인터페이스 분리 원칙

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

  • 예를 들어, 자동차의 경우 자동차를 위한 인터페이스보다, 운전자를 위한 인터페이스, 정비를 위한 인터페이스..
    이렇게 나누는 것이 더 좋다는 의미다.

    • 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스
    • 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트
  • 인터페이스를 분리하면 인터페이스가 명확해지고, 대체 가능성이 높아진다.

5. DIP (중요)

Dependency Inversion Principle. 의존관계 역전 원칙

  • 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다."
    의존성 주입은 이 원칙을 따르는 방법 중 하나이다.

  • 쉽게 얘기해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻
    예) MemberServiceMemoryMemberRepository를 바라보지말고, MemberRepository를 바라보라는 뜻

  • 앞에서 이야기한 역할에 의존하게 해야 한다는 것과 같다.
    만약, MemberServiceMemoryMemberRepository와의 관계만을 통해 구현되었다면, JdbcMemberRepository로 변경하려고 할 때 변경이 매우 어렵게 된다.

    여기서 의존한다는 것은, 대상을 알고있다는 것이다.

  • 그런데 MemberService는 인터페이스에 의존하지만, 구현 클래스에도 동시에 의존한다.
    MemberService 클라이언트가 구현 클래스를 직접 선택한다는 말이다.
    MemberRepository m = new MemoryMemberRepository();
    이는 DIP 위반이다.

정리

  • 객체 지향의 핵심은 다형성

  • 하지만, 다형성만으로는 쉽게 부품 갈아 끼우듯이 개발할 수 없고,
    구현 객체를 변경할 때 클라이언트 코드도 변경해야 하고,
    OCP, DIP를 지킬 수 없다.

  • 그래서 뭔가가 더 필요하다 !


객체 지향 설계와 스프링

스프링은 DIDI 컨테이너 기술로 다형성 + OCP, DIP를 가능하게 지원해준다.

DI(Dependency Injection): 의존관계, 의존성 주입

따라서 스프링을 사용하면, 클라이언트 코드 변경 없이 기능을 확장할 수 있다.
비로소 쉽게 부품을 교체하듯이 개발할 수 있게 된다.

실무 고민

인터페이스를 도입하면 추상화라는 cost가 발생한다.
따라서 기능을 확장할 가능성이 없다면, 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩토링으로 인터페이스를 도입하는 것도 방법이다.

profile
newBlog == https://kdkdhoho.github.io

0개의 댓글