[김영한님 - 스프링의 핵심원리] Spring - 스프링이 필요한 이유(1)

2022. 9. 18. 14:04Spring

728x90

스프링이 필요한 이유

 

결론부터 말하자면 , 스프링은 좋은 객체지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크이다.

 

스프링을 공부하기전 스프링의 필요한 이유를 알아야한다. 

그러기 위해서 먼저, 객체지향적으로 설계된 자바 코드를 보도록 하겠다.

 

객체지향이란??

“객체”들의 모임, 각각의 객체는 메세지를 주고받고, 데이터를 처리함. 유연하고 변경이 용이(부품을 갈아끼우듯이) ⇒ 다형성

객체 지향 특징

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

클라이언트는 인터페이스만 알면 된다.

인터페이스 → 구현클래스

다형성을 통해 인터페이스를 구현한 객체 인스턴스를 실행시점에 유연하게 변경할 수 있다.

⇒ 클라이언트는 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.

 

결론 : 인터페이스를 잘 설계해야한다.

 

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

  1. SRP : 단일 책임 원칙(single responsibility principle)
  2. OCP : 개방-폐쇄 원칙(Open/closed principle)
  3. LSP : 리스코프 치환 원칙(Liskov substition principle)
  4. ISP : 인터페이스 분리 원칙(Interface segregation principle)
  5. DIP : 의존관계 역전 원칙(Dependency inversion principle)

SRP 단일 책임 원칙

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 하나의 책임이라는 것이 모호하다.
  • 예를 들어서, 게시판을 구현한다고 할 때, 멤버와 게시판의 역할을 나누는 것
  • 따라서, 중요한 기준은 변경하려고 할 때 그 부분만 수정하면 되는 것!

OCP 개방-폐쇄 원칙

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

LCP : 리스코프 치환 원칙

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다. ⇒ 인터페이스의 본래 설계 목적에 맞게 구현 클래스를 만들어야함

ISP 인터페이스 분리 원칙

  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
  • 인터페이스가 명확해지고, 대체 가능성이 높아진다.

DIP 의존관계 역전 원칙

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

 

 

일반적인 자바 애플리케이션 구조

 

다이어그램으로 표현하면 아래와 같다.

 

코드로 표현하면 아래와 같다.

 

public class Member {

    //학번
    private Long memberId;

    // 1: 남자, 0: 여자
    private int sex;
    private String memberPassword;
    private String memberName;
    private String memberNumber;
    private Grade grade;
    getter,setter
    constructor
}

 

public interface MemberService {
    void join(Member member);
    Member findMember(Long memberId);
}
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository = new
            MemoryMemberRepository();

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

 

public interface MemberRepository {
    void save(Member member);
    Member findById(Long memberId);
}
public class MemoryMemberRepository implements MemberRepository{
    private static final Map<Long, Member> store = new HashMap<>();
    @Override
    public void save(Member member) {
        store.put(member.getMemberId(), member);
    }

    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}

 

위 코드들은 다형성을 잘 활용하였고, 인터페이스와 구현 객체를 잘 분리하였다.

OCP,DIP와 같은 객체지향 설계원칙을 잘 준수한 것처럼 보인다.

->하지만, 그렇지 않다.

 

위 클래스들의 의존관계를 분석해보면, 인터페이스뿐만아니라 구현 클래스에도 의존하고 있다.

  • 인터페이스 의존 : MemberRepository
  • 구현 클래스 의존 : MemoryMemberRepository

=> 현재 코드들은 기능을 확장해서 변경하면, 클라이언트 코드에 영향을 준다.

따라서, OCP 위반이다.

 

예를들어서, MemberRepository의 구현 클래스를 MysqlRepository로 바꾸고 싶다고 할 때, 

 

 private final MemberRepository memberRepository = new
            MemoryMemberRepository(); -> private final MemberRepository memberRepository = new MysqlRepository로 바꿔야한다. => 곧 클라이언트 코드를 건드리는 것이다.

 

 

결론 : 위의 코드들은 다형성을 잘 활용했지만, 그것만으로는 충분하지 않다.

 

 

객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다 → 스프링

 

 

 

 

위 포스팅은 김영한님의 스프링-핵심-원리-기본편 강의를 수강한 후 작성한 포스팅입니다.

 

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

 

 

 

728x90