[스프링 핵심원리 기본편] 객체 지향 설계와 스프링

흑수·2022년 1월 20일
1

김영한씨의 스프링 핵심 원리 - 기본편 강의를 듣고 공부 겸 정리하는 글입니다.

스프링은 왜 만들었을까?

스프링은 자바 언어 기반의 프레임 워크로서 자바의 특징을 극대화할 수 있습니다. 여기서 이야기하는 자바의 가장 큰 특징은 객체 지향 언어라는 점입니다!!

결국, 스프링은 좋은 객체 지향 어플리케이션 개발을 돕는 프레임 워크라고 할 수 있겠네요.

객체 지향 프로그래밍이라는 것은 프로그램을 일련의 명령어 집합으로 보는 것이 아닌, 서로 메세지를 주고 받고 데이터를 처리하는 독립된 객체들의 모임으로 보는 것입니다(협력의 관계).

이러한 특성으로 인해, 프로그램을 더욱 유연하고 변경이 용이하게 만들어요.

  • 레고 블럭 조립하듯이
  • 키보드, 마우스 갈아끼우듯이
  • 컴포넌트를 다른 컴포넌트로 쉽게 교체하듯이

그렇다면 객체 지향의 대표적인 특징에는 무엇이 있을까요?

추상화

특징 중에서 가장 헷갈렸던 개념인데, 정리하자면 여러 클래스에서 공통적으로 이용하는 필드와 메소드를 뽑아내는 작업을 추상화라고 합니다. 주로 인터페이스로 만들거나 추상 클래스로 만들어요. 예를 들면, 강아지, 고양이, 원숭이 모두 걸을 수 있다는 특징이 있으니 이걸 뽑아내서 인터페이스를 만들 수 있다는거죠.

캡슐화

비슷한 기능을 하는 메소드나 필드들을 묶어서 하나의 클래스에 담는 것을 의미합니다. 이렇게 캡슐화를 하게 되면 클래스의 필드들을 private로 지정하기 때문에 필드에 접근하기 위해서는 getter와 setter를 이용해야 하고 이것이 캡슐화의 장점인 정보은닉을 가능하게 해요.

상속

실제로 부모가 자식에게 재산을 나누어주듯, 부모 클래스가 가지고 있는 필드와 메소드를 자식 클래스에서도 이용할 수 있습니다. 이렇듯 상속을 하게 되면 중복되는 불필요한 코드를 줄일 수 있고, 유지 보수가 훨씬 쉬워진답니다.

다형성

다형성은 동일한 함수를 실행해도 다르게 동작할 수 있음을 의미해요. 주로 오버라이딩오버로딩을 통해서 이루어지는데,, 오버라이딩은 부모를 상속한 자식 클래스에서 부모 메소드를 재정의해서 함수 내용을 바꾸는 것을 의미하고, 오버로딩은 함수 이름은 같지만 반환타입, 파라미터 타입, 갯수를 변경하는 것을 의미해요.

역할, 구현

역할과 구현은 분리되어야 해요. 운전자와 자동차가 존재한다고 할 때, 운전자의 역할을 하는 클라이언트는 자동차의 역할(엑셀, 후진, 시동 등)만 알면 된다는 거에요.
실제로 자동차의 구현체(제네시스, K5, 람보르기니 등)가 어떻게 돌아가고 내부가 어떻게 이루어졌는지에 대해서 운전자는 몰라도 된다는 거죠.

회사에서 알아서 자동차의 역할에 따라 만들었기 때문에 운전자는 자동차의 역할만 안다면, 제네시스를 타든 람보르기니를 타든 모두 이용할 수 있다는 말입니다.

장점

  • 클라이언트는 대상의 역할(자동차의 역할, 인터페이스)만 알면 됨.
  • 클라이언트는 구현 대상의 내부 구조를 몰라도 됨.
  • 클라이언트는 구현 대상의 내부 구조가 변경되어도 영항 받지 않음.
  • 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않음.

역할 = 인터페이스 / 구현 = 인터페이스를 활용한 클래스, 구현 객체

본질

결국 다형성의 본질은 인터페이스를 설계한 구현 객체를 유연하게 교체할 수 있다는 점이랍니다. 즉, 클라이언트를 변경하지 않고 서버의 구현 기능을 자유자재로 변경할 수 있다!!

결국, 스프링은 다형성이 가장 중요하다고 할 수 있습니다!!
(역할과 구현을 분리!!)

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

객체 지향을 설계하는데 좋은 5가지가 있답니다. 앞글자를 따서 SOLID라고 하는데, 클린코드로 유명한 로버트 마틴이 정리했다고 해요. (지독한 면접관을 만나면 물어보곤 한다는,,)

SRP

Single responsibility principle, 단일 책임 원칙
즉, 하나의 클래스는 하나의 책임만 가져야 한다는 것. 최대한 객체간 의존성을 없애기 위함인가 봐요. 책임 범위가 애매하지만 클래스의 변경이 있을 때, 파급 효과가 작다면 SRP를 잘 지킨것..!!

OCP

Open/closed principle, 개방-폐쇄 원칙
확장에는 열려있고, 변경에는 닫혀 있어야 한다는 것. 확장을 하려면 기존 코드를 변경해야 하는데 이것이 무슨 말일까요? 말이 안되는 것 같은데,,
하지만 다형성을 활용한다면 못할 말도 아닙니다! 인터페이스를 구현한 클래스를 통해 새로운 기능을 만들어 낼 수 있습니다.

public class MemberService {
    private MemberRepository memberRepository = new MemoryMemberRepository();
}

public class MemberService {
    // private MemberRepository memberRepository = new MemoryMemberRepository();
    private MemberRepository memberRepository = new JdbcMemberRepository();
}

문제는 MemberService라는 클라이언트가 구현체 MemoryMemberRepository, JdbcMemberRepository도 모두 알아야 한다는 것. 즉, 클라이언트쪽에서 구현체를 직접 변경해야 함을 의미!

이를 가능하게 하려면 연결 시켜주는 무언가가 필요

LSP

Liskov substitution principle, 리스코프 치환 원칙
프로그램의 객체는 하위 객체로 변경이 가능해야 하므로 인터페이스 규약을 모두 지켜야 한다는 것.
예를 들어, 자동차의 엑셀은 모두 앞으로 가게 해야 한다는 것. 어떤 자동차 회사에서 엑셀이라는 기능을 후진으로 만든다면? 대참사

ISP

Interface segregation principle, 인터페이스 분리 법칙
최소한의 기능을 갖는 인터페이스로 나누는 것이 좋다는 것.
자동차 인터페이스는 운전 인터페이스, 정비 인터페이스 이런식으로 나누고 사용자 클라이언트도 운전자 인터페이스, 정비사 인터페이스로 나눈다는 이야기입니다.

DIP

Dependency inversion principle, 의존관계 역전 원칙
개발자는 추상화에 의존하고 구체화에 의존하면 안된다는 것. 즉, 인터페이스에 의존하고 구현체에 의존하지 말라는 것이지만 앞에서 OCP는 구현 클래스에도 동시에 의존하기 때문에 DIP를 위반하게 돼요.

다형성만으로는 OCP와 DIP를 동시에 지킬 수 없으므로 무언가가 더 필요하다.

객체 지향 설계와 스프링

결국, 스프링은 위에서 언급한 다형성 + OCP, DIP를 가능하게 해주는 무언가 존재

  • DI(Dependency Injection)과 DI 컨테이너

머나 먼 옛날에, 실력 좋은 개발자가 OCP, DIP를 모두 만족 시키면서 개발을 했더니 코드가 너무 복잡해졌답니다.

즉, 배보다 배꼽이 더 큰 상황!! 그렇기에 스프링이 탄생했습니다!

profile
기록용

0개의 댓글