Back-end/JAVA,Spring

스프링 Service에서 ServiceImpl를 사용하는 이유 ?

cheersHena 2022. 8. 3. 00:55
반응형

스프링 Service에서 ServiceImpl를 사용하는 이유? 

대부분의 스프링 프로젝트에서 서비스를 구현할때 Service.java를 인터페이스로 만들고, ServiceImpl 이라는 구현체 클래스를 생성하여 사용한다. 왜 인터페이스와 구현체를 따로 두어 사용할까?

ex) MemberService / MemberServiceImpl

 

1. OOP의 다형성, 개방-폐쇄 원칙 

이론상으로는 인터페이스-구현체 패턴으로 설계하는 이유는 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 두고, 구현체 클래스를 변경하거나 확장해도 이를 사용하는 클라이언트의 코드에 영향을 주지 않도록 하기 위함.

이와 같은 추상화를 통한 구현방식은 객체지향의 특징인 다형성과, 개방-폐쇠원칙(OCP)을 지향하는 설계방식이다.

 

2.AOP Proxy

AOP와 트랜잭션은 서비스 인터페이스에서 처리한다. 

Spring에서 AOP를 구현할 때 JDK의 기본프록시인 JDK Dynamic Proxy를 사용하는데, 이 프록시는 인터페이스 기반으로 동작한다.

예를들어, 인터페이스로 구현되어 있어야 @Transactional 어노테이션이 동작하게 된다. 

* 최근에는 특정 버전부터 CGLIB 라이브러리를 사용하여 클래스 기반으로 AOP Proxy를 만들도록 업데이트 되었다. 

SpringBoot 1.4 이후로 디폴트 클래스 기반인  CGLIB로 만들도록 설정되어서 개발자가 선택하여 사용할 수 있음. 

spring.aop.proxy-target-class = true

// true: CGLIB를 사용하여 클래스를 상속받아 AOP Proxy를 만듦.
// false: JDK Dynamic Proxy를 사용하여 인터페이스 기반의 Proxy를 만듦.

 

2번의 이유는 최근 업데이트 되면서 고쳐졌기 때문에 1번의 이유를 기준으로 생각해보면,

하나의 인터페이스로 여러방식의 구현체를 두어 다형성과 확장성을 위한 이유로 볼 수 있다.

 

ex)

MemberService // 회원 서비스 인터페이스 
 - GeneralMemeberService // 일반멤버 서비스 구현체 클래스 
 - AdminMemeberService // 관리자 멤버 서비스 구현체 클래스

하지만, 실제로 현실의 대부분의 코드는 인터페이스와 구현체 클래스 관계가 1:1 로 구성되어 있다.

이렇게 되면 실질적으로 인터페이스를 사용하는 장점이 무효화 되고 구현체를 가려면 인터페이스를 거쳐야 하기 때문에

오히려 소스코드가 복잡해지는데 관습적으로 이러한 추상화 패턴이 많이 남아있다.

 

관습적인 추상화 설계방식, 꼭 적용해야 하나? 

사실 1:1 관계라고 해서 인터페이스를 모두 없앤다는 것은 리스크가 크다.

지금은 1:1 방식일지 모르나, 객체는 끊임없이 변화하며 확장할 가능성이 있고 개발자는 이에 대비해야 한다.

즉, 미래를 위한 설계를 통해 변화에 효과적으로 대비할 수 있어야 한다.

하지만 명확히 미래에 확장의 여지가 없고, 절차지향적인 코드에서는 사실 서비스를 굳이 인터페이스로 둘 필요는 없다.

 

결론은 무조건적인 인터페이스 설계를 하는 것이 아니라

인터페이스를 만든다면, 만드는 목적을 잘 살려 코드를 짜는 것이 바람직하다는 말이다. 

반응형