Github에서 많은 서버 레포지토리를 보다 보면 Service 인터페이스를 구현한 ServiceImpl 구현 클래스를 볼 수 있다. 하지만 대부분의 경우 하나의 Service와 대응되는 ServiceImpl가 단 하나인, 1:1 대응관계를 가지고 있었다. 이 글은 이와 같이 "Service와 ServiceImpl이 1:1 구조를 가질 때, 서비스 클래스를 구체 클래스로 바로 만드는 것이 낫지 않나?" 라는 의문을 갖고 함께 작성되었다.
Service와 ServiceImpl를 분리하는 이유
Service 인터페이스를 만들고 이를 구현한 ServiceImpl 클래스를 만들어 둘을 분리하는 이유를 먼저 알아보자.
1. 인터페이스와 구현 클래스를 분리할 수 있다.
- Service 인터페이스는 비즈니스 로직을 담당한다.
- ServiceImpl 은 Service 인터페이스를 구현하여 실제 로직을 처리한다.
따라서 비즈니스 로직과 구현 로직을 분리함으로써 각각을 독립적으로 개발하고 테스트할 수 있게 된다.
2. 스프링 프레임워크가 제공하는 IoC(Inversion of Control) 기능과 함께 사용할 수 있다.
- 스프링은 IoC 기능을 통해 객체 생성과 의존성 주입을 관리하며, 이를 통해 객체 간의 결합도를 낮추고 유연성 및 확장성을 높일 수 있다.
- Service와 ServiceImpl 구조를 사용하면 Service 인터페이스를 빈으로 등록하고, 이를 ServiceImpl에서 의존성 주입 받아서 사용하는 방식으로 구현할 수 있다.
3. 모든 설계에 인터페이스를 부여하는 것이 이상적이다.
- 하부 구현 기술에 대한 선택을 최대한 미룰 수 있다. 이는 변경의 범위가 작고 유연해진다는 장점을 갖는다.
- 모든 설계는 역할과 구현을 분리하는 것이 좋다. 구현체는 언제든지 유연하게 변경할 수 있도록 만드는 것이 좋은 객체지향 설계이다.
그렇다면 이처럼 역할과 구현을 분리하는 방식의 단점은 없을까?
- 인터페이스를 도입하면 기본적으로 추상화라는 비용이 발생한다. (여기서 말하는 추상화는 성능이 아닌 복잡도에 관한 부분이다.)
- 코드가 복잡해진다. 추상화가 없다면 그냥 코드를 따라가면 되는데, 추상화가 있으면 추상 인터페이스를 보고 어떤 구현체가 실제 동작할지 또 추가로 찾아야 하는 과정을 거쳐야 하는 번거로움이 생긴다.
따라서 추상화가 꼭 필요하지 않은 곳까지 추상화하게 되면 코드를 유지보수하기 더 어려워 질 수 있다.
결론
개방 폐쇄 원칙(OCP)에 기반한 전략 패턴을 미리 설계한 것이 아닌 그저 습관적인 사용이라면 전혀 무의미하다.
기능을 확장할 가능성이 없다면, 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것이 좋은 방법이다.
'IT, 코딩 > Spring' 카테고리의 다른 글
테스트 코드가 필요한 이유 (1) | 2024.03.30 |
---|