스프링 부트 모듈 계층간 의존성 역전시키기

jonghyun.log·2023년 5월 22일
4

spring

목록 보기
6/7
post-thumbnail
post-custom-banner

이전 글에서 멀티 모듈을 세팅하는 방법을 포스팅 했는데요, 이전 글에서 한가지 설명하지 않고 넘어간 부분이 있습니다. 바로 도메인 모듈의 의존성을 역전 시키는 방법인데요.

위 그림에서 domain은 서비스 코드에 대응하고 storage:db-jpa는 레포지토리에 대응하는 모듈입니다.
원래는 의존성 관계가 domain -> storage:db-jpa 순으로 되어야 정상적인 로직 수행이 가능합니다.
하지만, 도메인 모듈을 외부의 모듈과 격리시키기 위해 의존성 역전을 진행하였는데요.

이번 포스팅에서는 어떻게 의존성 역전을 할 수 있는지에 대해 포스팅을 해보겠습니다.

참고) 의존성 관련 개념은 mangkyu님의 의존성 정리글을 참조하시기 바랍니다.

일반적인 의존관계 살펴보기

그러면 일반적인 의존관게를 지닌 서비스 -> 리포지토리 구조를 보겠습니다.

public class UserService {
    private final UserRepository userRepository;
    ...
}
public class UserRepository{
	...
}

보통은 위 그림처럼 서비스 코드에서 레포지토리 코드를 의존하여 사용하게 됩니다.
서비스에서 영속성 관련 로직을 호출해야 하니 당연한 과정이지요.

여기서는 설명의 편의를 위해서 class 로 설명했습니다.

하지만, 이렇게 하면 도메인 모듈을 격리시키는 것이 불가능 하기 때문에
도메인 모듈이 오염되기 쉬운 코드가 만들어 집니다.
이것을 해결하기 위해서는 어떻게 해야 할까요?

DIP - 의존성 역전 원칙

의존성 역전의 힌트는 바로 DIP에 있는데요.
바로,로버트 마틴의 SOLIDDIP(Dependency Inversion Principle - 의존성 역전 원칙) 입니다.

이는 추상화에 의존해야 하며 구체화에 의존하면 안된다는 의미이며
다르게 말하면, 구체적 클래스가 아닌 인터페이스에 의존해야 하며 다형성을 이용해 유연한 코드를 만들어야 한다는 의미입니다.

의존성 역전 원칙 코드에 적용하기

의존성 역전 원칙에 따르면 구체적 클래스가 아닌 인터페이스에 의존해야 한다고 합니다.
위의 코드를 한번 변경해 보겠습니다.

public class UserService {
    private final UserRepository userRepository;
    ...
}
public interface UserRepository{
	...
}
public class UserRepositoryImpl implements UserRepository{
	...
}

위의 코드와 차이점이 뭔지 아시겠나요?

  1. 서비스의 의존성을 클래스에서 인터페이스로 바꿉니다.
  2. 그 인터페이스를 도메인 모듈 내부로 옮깁니다.
  3. 도메인 외부에서 도메인 내부의 UserReposity 인터페이스 구현체를 만들어서 사용합니다.

즉, 의존성을 구체적 클래스에서 추상적인 인터페이스로 바꾼 후
그 인터페이스는 의존성 역전 시키고 싶은 계층안으로 보내고
구현체를 외부 계층으로 보내주면 의존성 역전이 완성됩니다.

이렇게 코드를 구성하게 되면 도메인 모듈에서 외부의 모듈이 어떤 것이 오던지 상관없이 개발이 가능합니다.
단지, 도메인 모듈에서는 서비스에서 의존하고 있는 인터페이스의 변경만 못하게 강제하게 되면 코드를 변경할 일이 없어지게 되는 것이죠.

그리고 외부의 모듈에서는 도메인 모듈에서 정의한 인터페이스에 맞게 구현체를 구현해주기만 하면
어떤 모듈이던 도메인 모듈과 호환이 가능한 마법이 일어나게 됩니다.

위 방법은 비단 모듈이 아니라 패키지 구조에서도 적용이 가능합니다.
다른 패키지를 두고 위 모듈과 같은 구조를 가져가면 의존성이 역전이 일어나게 되지요.

참고) 물론, 보통 JPA를 사용했을 때 Repositoryinterface로 만들어서 spring data JPA가 만들어주는 인터페이스 구현체를 DI 받아서 사용하기 때문에 이야기가 조금은 다릅니다.
하지만, 위의 내용을 이용하면 계층간 의존성 역전이 가능하기에 위와 같이 서술했습니다.
좀 더 구체적으로 JPA를 사용한 모듈간 의존성 역전 예시는 이후의 포스팅으로 정리할 예정입니다.

모듈간 의존성 역전 예시 정리글 추가했습니다.
https://velog.io/@jonghyun3668/%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%BD%94%EB%93%9C%EC%97%90-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0#%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%AA%A8%EB%93%88%EA%B3%BC-jpa-%EB%AA%A8%EB%93%88%EC%9D%98-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84

배운점

평소 DIP 관해서는 알고 있다고 생각하던 내용이었으나,
이번에 정리하면서 실제 코드에서 의존성 역전은 어떻게 하는것인가에 대해 더 깊게 알게 되었습니다.
특히나,

  • 의존하는 클래스를 인터페이스로 바꾼다.
  • 인터페이스를 구현하는 구현체를 만든다.
  • 의존하는 인터페이스와 그 구현체의 위치를 적절한 곳에 위치시킨다.

위 3가지 간단한 과정만 거치면 다른 계층간에 의존성이 역전되는게 매우 흥미로웠네요.
앞으로 유용하게 사용할 수 있는 테크닉이 될 것 같습니다 ㅎㅎ

post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 28일

와 진짜 너무 좋은 설명 감사합니다. 클린 아키텍처, 멀티모듈, 의존성 역전 너무 맛있습니다. 감사합니다.

답글 달기