Spring, 단계적으로 OCP가 어떻게 지켜지는지
스프링 빈이 뭔지 어노테이션들이 뭘 뜻하는지 같은 설명은 피하고 스프링이 무슨일을 해주는건지 중점으로 기록한다.
1단계. 인터페이스에 의존하게 한다
예를들어 서비스에서 사용하는 JDBC로 db와 소통하는 JDBCMemberRepository 가 있다.
public class OrderServiceImpl implements OrderService{
private JDBCMemberRepository jdbcMemberRepository = new JDBCMemberRepository();
}
OCP, Open Closed Principle, 개방 폐쇄 원칙 (확장에는 열려있으나 변경에는 닫혀있어야 한다)
개방 폐쇄 원칙을 지키기 위해 아래와 같이 MemberRepository 라는 인터페이스를 만들고, 구현체를JDBCMemberRepository 로 선택했다.
public class OrderServiceImpl implements OrderService{
private MemberRespository memberRespository = new JDBCMemberRepository();
}
문제점
- 실제로는 OrderServiceImple 은 MemberRepository 에도 의존하고, JDBCMemberRepository 에도 의존한다.
- 만약 구현체를 변경하고 싶으면 결국 코드의 변경이 발생한다
2단계. 구현체를 주입한다
결국 문제는 구현체가 OrderServiceImpl 에서 만들어지는 것이다.
즉 누군가 밖에서 구현체의 레퍼런스를 넣어주면 좋을것 같다.
우리가 원하는 그림은 아래와 같다.
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
public OrderServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
이걸 해결하기 위해서는 OrderServiceImpl 객체가 만들어질때 아래와 같이 JDBCMemberRepository의 구현체가 주입이 되어야한다.
public class AppConfig {
public OrderServiceImpl orderServiceImpl() {
return new OrderServiceImpl(new JDBCMemberRepository());
}
}
3단계. Spring의 출현
스프링을 사용하면 스프링 컨테이너가 구현체의 레퍼런스를 대신 주입해준다.
@Configuration
public class AppConfig {
@Bean
public OrderServiceImpl orderServiceImpl() {
return new OrderServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new JDBCMemberRepository();
}
}
위와 같은 경우를 스프링 빈 수동 등록 이라고 하는데,
Configuration 클래스에 내가 사용해야하는 빈을 직접 정의하고 주입해야 하는 객체도 정의해주는 방법이다.
이렇게 해주면 OrderServiceImpl의 레포지토리가 의존성 주입에 의해 JDBCMemberRepository가 된다.
만약 OrderServiceImpl 에서 JDBCMemberRepository 가 아닌 다른 레포지토리 예를들어 LocalMemberRepository 를 사용하고 싶다면?
OrderServiceImpl 의 코드가 아닌 외부의 AppConfig 에서 주입을 해주면 된다.
이외에도 컴포넌트 스캔을 사용한 자동등록이 있고,
Lombok 을 사용해서 빈 클래스의 생성자나 게터 세터 까지 직접 코드를 짜지 않게 만들어 줄 수도 있다.
하지만 이런 내용들은 스프링을 어떻게 다루는지 그리고 어떻게 더 생산성을 높이는지에 대한 내용이고
이 글에서는 스프링이 결국 하는 일이 뭔지 에 대해서 정리해 봤다.
스프링은 정말 많은 기능들을 제공하지만 결국 제일 중요하고 코어가 되는 내용은 의존성 주입으로,
객체를 사용하는 클라이언트 쪽의 코드를 변경하지 않아도 된다는 점이다.
즉 어떤 클래스에서 의존하는 구현체가 변경되어야 할때, 클래스 코드의 변경이 아닌 외부의 설정 클래스의 코드가 바뀐다.
클라이언트의 코드가 바뀌지 않는 것이 OCP 의 핵심이다.
뭔가 기능이 변경된다면 코드의 어딘가는 당연히 바뀌어야 한다.
하지만 클라이언트쪽 코드가 변경되는 것은 좋지 않다.
- 클라이언트 코드를 수정한다는 것은 클라이언트 코드가 해당 세부 사항과 긴밀하게 결합되어 있다는 것을 의미
- 즉 향후 변경이 필요할때 여러곳에서 변경해야하게 된다
- 클라이언트 코드를 수정한다는 것은 기능이 확장될때마다 클라이언트 코드 쪽 코드가 변경, 늘어나고 중복된 코드가 많아진다
- 즉 확장성이 줄어든다