대부분의 프로젝트는 Service를 만들 때 MemberService와 같이 서비스를 인터페이스로 설계하고, MemberServiceImpl이라는 구현체인 클래스를 생성해서 사용하는 방식으로 설계된다.

여태 프로젝트를 하면서 그런가보다 하면서 구현했는데, 객체 지향을 공부하면서 문득 내가 사용했던 ServiceImpl은 왜 썼던 걸까? 궁금해져서 포스팅하게 되었다.

🎶Service interface와 ServiceImpl class 구조를 사용하는 이유?

이론상으로 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 확장할 수 있고, 구현체 클래스를 변경하거나 확장해도 이를 사용하는 클라이언트 코드에 영향을 주지 않기 위함이다.
이처럼 추상화를 통한 구현 방식은 객체지향의 특징인 다형성OCP에 연관이 되어있다.

🛠️코드

인터페이스

public interface IService {
    String test();
}

구현체

@Service
public class AServiceImpl implements IService {
    @Override
    public String test() {
        return "AServiceImpl";
    }
}

컨트롤러

@RequiredArgsConstructor
@RestController
public class SampleController {
    private final IService service;
    @GetMapping("/test")
    public String test(){
        return service.test();
    }
}

😒다형성이라며?

그런데 우리가 보통 spring 코드를 짤 때 인터페이스 하나당 구현체 하나와 연결이 되어있다. 1:N관계가 아니라 1:1관계를 형성하면서 인터페이스를 사용하고 있다는 것이다..
너무 관습적인 추상화를 구현하고 있는 것이 아닌가 생각이 든다.

🤷‍♀️인터페이스와 구현체가 분리되어 있지 않다면?

  • 분리 설계를 만족시키기 위해 불필요한 네이밍을 반복할 필요가 없음
  • 클래스와 인터페이스 간의 상호작용을 걱정할 필요가 없음
  • 의존성 감소
  • 단위 테스트를 하기 위해 service 인터페이스와 serviceImpl 구현체를 모두 생성하지 않아도 됨

🤦‍♂️그래서 어떻게 해야함?

결국 인터페이스와 구현체가 1:N이 아니라 1:1 관계라면 때문에 서비스 코드만 존재해도 괜찮지 않을까?
사실 정답은 없다
프로젝트를 설계하는 사람의 마음이다...
그래도 필자는 계속 분리 패턴을 이용할 것이다.
이 구조는 OOP 관점으로 봤을 때 다형성 혹은 개방 폐쇄 원칙(OCP)때문에 사용한다.
하나의 인터페이스에 여러 구현체를 생성하게 하며,
API를 구조할 때 기획 정책은 얼마든지 변경될 수 있다고 느꼈고, 인터페이스는 그대로 두고 구현체를 변경하며 추가하여 OCP를 지키게 할 것이다.

profile
야호

0개의 댓글

관련 채용 정보

Powered by GraphCDN, the GraphQL CDN