스프링
스프링은 자바 언어 기반의 프레임워크이고 자바 언어의 가장 큰 특징은 객체 지향 언어라는 것이다.
스프링은 이런 자바의 객체 지향 언어의 특징을 잘 살려내는 프레임워크이다. 따라서 스프링은 좋은 객체 지향 어플리케이션을 개발할 수 있게 도와준다.
좋은 객체 지향이란?
객체 지향으로 설계한 프로그램은 유연하고 변경이 용이하다.
다형성
컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는것 ex) 운전자가 자동차가 만약에 다른 자동차로 바뀐다고 해도 자동차가 하는 역활에 따라서 자동차가 구현되었기 때문에 운전하는데 크게 문제가 없다.
따라서 클라이언트는 대상의 역활(인터페이스)만 알면 된다. 내부구조를 몰라도 되며 내부 구조가 변경되어도 영향을 받지 않고 대상 자체를 변경해도 영향을 받지 않는다. 자바에서는 역활은 인터페이스, 구현은 인터페이스를 구현한 클래스인 구현 객체로 된다.
곧, 다형성의 본질은 클라이언트를 변경하지 않고도 서버의 구현 기능을 유연하게 변경할 수 있는 것이다.
따라서 인터페이스를 안정적으로 잘 설계하는 것이 중요하다. 만약, 인터페이스가 바뀌면 큰 변경이 발생한다. 자동차가 비행기로 바뀐다면?
스프링은 다형성을 극대화해서 이용할 수 있다. 스프링에서 이야기하는 제어의 역전(ioc), 의존관계 주입(di)은 다형성을 활용해서 역활과 구현을 편리하게 다룰 수 있도록 지원한다.
좋은 객체 지향 설계의 5가지 원칙(SOLID
)
1. SRP
- 단일 책임 원칙(Single Responsibility Principle
)
한 클래스는 하나의 책임만 가져야 한다.
중요한 기준은 변경
이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
2. OCP
- 개방-폐쇄 원칙(Open/Closed Principle
)
확장에는 열려 있으나, 변경에는 닫혀 있어야 한다.
문제점
구현 객체를 변경하려면 클라이언트 코드를 변경해야 한다.
분명 다형성을 사용했지만 OCP
원칙을 지킬 수 없다.
이 문제를 해결하기 위해서는 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다. 이 역활을 스프링 컨테이너
가 한다.
3. LSP
- 리스코프 치환 원칙(Liskov Substitution Principle
)
다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것이다. 다형성을 지원하기 위한 원칙이며, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다. 단순히 컴파일 단계에서 성공하고 실패하는 것을 넘어서 지켜야하는 규칙이다. 자동차 인터페이스의 액셀은 앞으로 가라는 기능이며, 이를 뒤로 가도록 구현하면 LSP에 위반되는 것이다. 속도가 다르더라도 앞으로 가게 만들어야 한다.
4 . ISP
- 인터페이스 분리 원칙(Interface Segregation Principle
)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
자동차 클라이언트를 운전 인터페이스 + 정비 인터페이스로 분리하면 정비 인터페이스 자체가 변해도 운전자 클리이언트에 영향을 주지 않는다. 인터페이스가 명확해지고, 대체 가능성이 높아진다.
5. DIP
- 의존관계 역전 원칙(Dependency Inversion Principle
)
추상화에 의존해야지, 구체화에 의존하면 안된다. 쉽게 설명하지면 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다.
앞에서 이야기한 역활에 의존하게 해야 한다는 것과 같다. 클라이언트도 인터페이스를 의존해야 유연하게 구현체를 변경할 수 있다. 구현체에 의존하게 되면 변경이 아주 어려워 진다.
MemberRepository m = new MemoryMemberRepository();
위와 같은 코드는 DIP
위반이다. 인터페이스와 구현 클래스에 동시에 의존하고 있기 때문에
정리
다형성이 핵심이지만 다형성만으로는 OCP,DIP를 지킬 수 없다.
그렇기 때문에 뭔가가 더 필요하다.
그 무언가의 역활을 스프링
이 해주게 된다.
스프링
스프링은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원한다.
DI
(Dependency Injection
) : 의존 관계, 의존성 주입DI 컨테이너
제공
클라이언트 코드의 변경 없이 기능 확장할 수 있다.
순수하게 자바로 OCP
, DIP
원칙들을 지키면서 개발해보면 결국 스프링의 DI 컨테이너
를 만들게 된다.
실무적인 고민
인터페이스를 도입하면 추상화라는 비용이 발생한다. 코드를 한번 더 열어봐야 한다.
그래서 만약, 기능을 확장할 가능성이 없다면, 구현 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것도 괜찮은 방법이다.