객체 지향 설계와 스프링

스프링의 컨셉

스프링의 과거와 핵심 컨셉

자바 언어 기반의 프레임워크

스프링은 객체 지향 언어가 가진 강력한 특징을 살려내어 좋은 애플리케이션을 개발할 수 있도록 도와주는 프레임워크이다.

스프링으로 문제를 해결하고자 했던 부분은 EJB의 특징 중 하나였던 EJB 가 만들어 놓은 의존성에 의존 하여 코드를 개발 해야하는 점이다.

  • 이러한 문제점은 객체지향이 갖고 있는 좋은 스타일을 잃고 EJB 스타일에 맞춰 개발을 할 수 밖에 없었다.

그래서 1차적인 문제 해결 방법으로 POJO 라는 순수한 자바로 다시 돌아가는 프로젝트를 선택하기도 했다.

하지만, 스프링은 DI 컨테이너 방식을 활용하여 좋은 객체 지향 방식으로 애플리케이션을 만들어낼 수 있는 도구라는 것을 Rod Johnson이 책을 출간하며 많은 개발자가 주목하게 되었다.

"스프링의 컨셉인 좋은 객체지향 프로그래밍이란 뭘까?"

유연하고 변경이 용이한 다형성을 극대화 하여 사용할 수 있는 도구이다.

자동차를 타는 운전자는 변하지 않지만 자동차는 매번 새로운 모델을 출시한다. 하지만 그 타겟층은 언제나 같은 운전자를 타겟으로 삼고 출시한다.

"이 것은 운전자는 새로운 차가 나오더라도 새로운 차를 운전하는 데 지장이 없다." 라는 얘기다.

이 내용을 서비스의 시선으로 바라보면, 클라이언트에게 아무런 영향을 주지 않고 새로운 기능을 출시할 수 있다는 것이다.

정리하기

역할과 구현을 명확히 분리하면 단순하게 설계할 수 있고 유연해지며 변경도 편리하다.

  • 클라이언트는 대상의 역할(인터페이스)만 알면 된다.

  • 클라이언트는 역할을 구현하고 있는 구현체를 변경해도 영향을 받지 않는다.

구현보다 역할이 먼저 탄생해야한다.

  • 즉, 서비스 클래스보다 인터페이스를 먼저 설계 하며 인터페이스를 안정적으로 설계 해야한다.

위 좋은 장점에 비해 분명한 한계점이 존재한다.

그 한계점은 "인터페이스" 자체가 변하는 경우 결국 내부 구현 코드와 인터페이스를 사용하는 클라이언트의 코드가 변경이 일어나기 때문이다.

그래서 인터페이스를 최대한 변경이 없는 선에서 안정적으로 설계하는 것이 대안이며 핵심이다.

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

SRP - 단일 책임 원칙

하나의 클래스는 하나의 책임만 가져야한다. 하지만 책임의 크기가 굉장히 모호하다.

그래서 중요한 기준은 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따랐다고 볼 수 있다.

OCP - 개방 폐쇄 원칙 [중요!]

확장에는 열려있으면서 변경에는 닫혀있어야한다.

인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현 하는 다형성을 활용하더라도 구현 객체를 변경하기 위해서는 클라이언트 코드가 변경이 일어난다.

// MemberRepository m = new MemoryMemberRepository();
MemberRepository m = new JdbcMemberRepository();

위 코드 처럼 새로운 구현 코드를 반영하기 위해서 기존 사용하던 구현체를 주석처리 하는 변경이 일어난다.

이 문제점을 해결하기 위해서 객체를 적재적소에 주입시킬 수 있는 별도의 설정자가 필요한데, 스프링 컨테이너가 이 역할을 대행 해주고 있다.

LSP - 리스코프 치환 원칙

다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야한다.

  • 즉, 인터페이스가 설계한 기능의 규약을 보장을 해줘야한다.

자동차가 엑셀을 밟는다는 기능이 있고, 이 기능은 앞으로 간다는 규약이 있다.

  • 만약 구현하는 구현체에서 엑셀이 뒤로 가는 기능을 구현했을 때 전혀 컴파일 단계에선 오류가 없지만 인터페이스가 설계한 규약에선 어긋난다.

이러한 예시 상황을 어긋나지 않도록 설계하는 원칙이다.

ISP - 인터페이스 분리 원칙

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

  • 자동차 인터페이스

    • 운전 기능을 설계한 인터페이스

    • 정비 기능을 설계한 인터페이스

위 자동차 인터페이스가 만약, 한 개의 자동차 인터페이스로 관리되고 있다고 가정해보자.

정비 기능에 문제가 생겨서 코드를 수정할 때 자동차 인터페이스 전체가 영향을 받는다.

이런 거대한 인터페이스 하나를 수정하는 것 보다 작은 기능에 대한 인터페이스를 수정하는게 유지보수 측면에서 도움이 되며, 정비 인터페이스 자체가 변하더라도 클라이언트에게 영향을 주지 않는다.

DIP - 의존관계 역전 원칙 [중요!]

프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다."

클라이언트 코드가 구현된 코드를 바라보지 말고 인터페이스를 바라보도록 설계해야한다.

요약

결론적으로 객체 지향의 핵심은 다형성인 것은 확실하다.

  • 하지만, 다형성만으로 코드를 갈아끼울 수 없다.

  • 다형성만으로 구현 객체를 변경할 때 클라이언트 코드도 함께 변경 된다.

  • 다형성만으로 OCP, DIP를 지킬 수 없다.

다시 스프링으로!

스프링은 DI 컨테이너 기술로 다형성 + OCP, DIP 를 가능 하도록 지원한다.

"스프링이 없던 시절"

객체지향 설계를 하기 위해 OCP, DIP 원칙을 지키면서 개발을 해보니 너무 할 일이 많았다. 위에서 잠깐 언급했던 DI 컨테이너 기술이 순수 자바로 구현 되는 것 처럼 말이다.

Last updated