service를 interface와 함께 써야할까?

최준호·2023년 11월 1일
0

Appling

목록 보기
6/12

🔴 service를 interface와 함께 써야할까?

프로젝트를 진행할 때 꼭 interface를 작성하여 service를 작성해야할까? 라는 생각이 들어 프로젝트를 진행할때 interface를 두지 않고 진행하고 있었다.

interface를 두지 않으므로 얻은 장점은 간편성 이였다. 프로젝트를 만들때 interface에 의존하지 않아 service에 내 코드를 그대로 적으면 되었기 때문이다.

여기서 또 내가 간편성을 얻으며 들었던 생각은 어차피 서비스를 변경하는 경우는 적지 않나? 굳이 interface에 의존해서 서비스를 갈아낄 일이 거의 없을텐데 귀찮게 일을 더 하나 만드는 건 아닐까? 라는 생각으로 프로젝트를 진행하고 있었다.

🟠 interface를 사용하지 않으므로 얻어낸 단점

interface에 의존하지 않아 service를 그대로 작성한 나의 대가는 컸다.

🟢 결합성 증가

우선 service가 controller 코드와 강하게 결합되어 interface를 사용하여 service를 갈아끼우는 것은 불가능해진다.
이 점은 내가 알고도 그냥 진행했던 부분이다.

🟢 코드 가독성 감소

이 점이 가장 크기도 했다. interface가 있다면 각 메서드들의 설명을 interface에 정의해두면 보기가 편하다. 하지만 interface가 없이 service에 주구장창 주석으로 달아놓은 각 메서드의 설명들은 service 소스 때문에도 잘 보이지 않고 해당 메서드를 찾아야 그때서야 내가 설명을 볼 수 있었다.

🟢 테스트 어려움

이 점이 가장 컸다.
orderController 에서 service에 대한 반환 값을 정의하려고 한다. 물론 mock 라이브러리를 사용하여

given(orderService.method(any(Object.class))).willReturn(Optional.of(Object))

와 같이 반환 하는 내용을 모킹할 수도 있다. 하지만 이러한 점은 테스트 코드에 대한 러닝 커브를 높이고 테스트 마다 복붙해주는 경우가 발생하기도 한다.

이러한 경우 Fake Class를 생성하여 해당 작동이 된것처럼 속여서 반환하는 기술도 쓸 수 있는데 이런 부분에서 interface 의존하지 않는 service는 추상화의 유연성을 포기했기 때문에 확장할 수가 없다...!

그래서 나는 다시 interface를 사용하려고 한다.

물론 초기에 작은 프로젝트로 시작할때는 꼭 interface를 고려하지 않아도 되지만 테스트나 프로젝트의 확장성을 생각한다면 interface를 적용하는 것이 좋이 않을까 생각이 든다!

🔴 Transaction 관련하여 참고 ~!

예전 Spring Boot 2.x 이하의 경우 Transaction을 적용하려면 interface가 필수였다. service를 proxy로 만들어서 transaction을 적용했기 때문인데 proxy를 만들때 interface에 의존된 service들을 가지고 만들었기 때문에 interface가 필수 였다. 하지만 2.0 이상 버전에서는 interface를 정의하지 않아도 @Service 어노테이션을 사용하여 빈으로 정의하면 해당 서비스의 메서드에서 transaction을 사용할 수 있도록 처리가 되었다.

🟠 JDK Dynamic Proxy

인터페이스 기반 프록시: JDK Dynamic Proxy는 반드시 인터페이스를 구현한 클래스에만 적용다.

동적 프록시 생성: 프록시 객체는 실행 시 동적으로 생성되며, 프록시가 호출되면 InvocationHandler를 통해 원본 메서드 호출을 가로챈다.

코드 생성 오버헤드가 낮음: JDK Dynamic Proxy는 런타임에 프록시를 생성하므로 클래스를 동적으로 생성할 필요가 없어 일반적으로 코드 생성 오버헤드가 낮다.

🟠 CGLIB Proxy

인터페이스 없이 클래스 프록시 생성: CGLIB Proxy는 클래스의 하위 클래스를 생성하기 때문에 인터페이스를 구현하지 않은 클래스에 대해서도 프록시를 생성할 수 있다.

스프링 AOP에서 주로 사용: 스프링 프레임워크의 AOP 구현에서 CGLIB Proxy를 기본적으로 사용한다. 스프링 AOP는 기본적으로 인터페이스를 구현하지 않은 클래스에 대한 프록시를 생성하므로 CGLIB Proxy를 사용합니다.

클래스 상속 필요: CGLIB Proxy는 프록시 대상 클래스를 상속받아야 하므로 final 클래스나 private 생성자를 가진 클래스에는 적용할 수 없다.

코드 생성 오버헤드가 있을 수 있음: CGLIB Proxy는 클래스를 동적으로 생성하기 때문에 코드 생성 오버헤드가 있을 수 있다.

🟠 나는 세팅한적이 없는데? 내가 사용하고 있는건 뭐지?

Spring Boot 2.0 이상 버전에서는 자동으로 interface가 있다면 JDK Dynamic Proxy로... class만 있다면 CGLIB Proxy로 세팅하여 사용해준다!

🟢 내가 세팅하고 싶다면?

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AppConfig {
    // 설정
}

proxyTargetClass = false -> JDK Dynamic Proxy 사용
proxyTargetClass = true -> CGLIB Proxy 사용

으로 직접 세팅할 수도 있다.

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글