250x250
Notice
Recent Posts
Recent Comments
관리 메뉴

탁월함은 어떻게 나오는가?

[SOLID] 의존관계 역전 원칙 (Dependency Inversion Principle) 본문

[Snow-ball]server/객체지향

[SOLID] 의존관계 역전 원칙 (Dependency Inversion Principle)

Snow-ball 2022. 12. 22. 13:09
반응형
의존관계 역전 원칙
객체 지향 프로그래밍에서 의존관게 역전 원칙은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭한다. 이 원칙을 따르면, 상위 계층(정책 결정)이 하위 계층(세버 사항)에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다. 이 원칙은 다음과 같은 내용을 담고 있다.
첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
둘째, 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
이 원칙은 '상위와 하위 객체가 모두가 동일한 추상화에 의존해야 한다'는 객체 지향적 설계의 대원칙을 제공한다.
- 위키 백과 -

 

의존관계 역전 원칙은 간단히 DIP라고 부른다.

 

DIP의 정의는 "상위 모듈은 하위 모듈의 구현 내용에 의존하면 안된다. 상위 모듈과 하위 모듈 모두 추상화된 내용에 의존해야 한다." 결국은 변화하기 쉬운 것에 의존하지 말고, 하위 모듈에 대한 상위 모듈의 종속성을 줄이는 방법론이다.

여기서 상위 모듈이란 다른 클래스를 사용하는 주된 클래스(기계어 처리에 멀어질수록), 하위 모듈은 사용되는 클래스(기계어 처리에 가까워질수록)를 나타낸다고 생각하면 된다.

상위 모듈은 보통 프로그램의 메인 흐름에 좀 더 가깝고 하위 모듈은 상대적으로 좀 더 멀리 있다.

 

DIP는 상위 모듈이 하위 모듈을 사용할 때 직접 인스턴스를 가져다가 쓰지 말라는 뜻이다.

왜냐하면 인스턴스를 바로 가져다가 쓴다는 말은 하위 모듈의 구체적인 내용에 상위 모듈이 의존하게 되어 하위 모듈에 변화가 있을 때마다 상위 모듈의 코드를 자주 바꿔줘야 하기 때문이다.

 

결국 DIP는 소프트웨어를 유연하게 하며, 기술적 요소가 비즈니스 로직을 침범하는 일을 막기 위해서는 필수적이다.

 

밑에 그림을 봐보자.

자주 사용하는 패턴인 MVC패턴에서 자주 나누는 방법이다. 컨트롤러는 상위모듈에 가까운것이고, 레파지토리는 하위모듈에 가까운것이다. 그렇지만 밑에 그림처럼 컨트롤러가 서비스를, 서비스가 레파지토리를 의존하게 된다면 레파지토리에서 서비스로, 서비스에서 컨트롤러로 영향을 줄 수 있게 된다.

 

 

 

추가적으로, 영향뿐만 아니라 개발적인 관점에서도 어려움이 생길 수 있다.

개발적인 관점에서의 어려움은 2가지로 나눠볼 수 있을것이다.

1) 사람의 사고구조상 프론트에서 던져주는 데이터(state)들을 받는 Controller에서부터 Service 그리고 Repository를 작업하는게 자연스럽다. 하지만, 밑에 방식대로하면 Repository > Service > Controller 식으로 개발을 해야지 오류가 발생하지 않는다.

2) 혼자 개발하는 상황은 흔치 않다. 회원가입 관련 작업을 2명에서 한다고 가정해보자. 개발자 A는 회원가입 로직을, 개발자 B는 로그인 로직을 개발할 수 밖에 없다.그러면 2명의 개발자는 모두 Controller, Service, Repository를 작업하게 된다. 그러면 코드를 Merge 하면서 문제가 생길 확률이 커지게 된다.

 

 

위에 대한 문제들의 해결책은 추상 클래스(인터페이스)로 상위 모듈과 하위 모듈 사이에 추상화 레이어를 만드는 것이다.

이렇게되면 1) 상위 모듈에는 추상 클래스의 자식 클래스의 인스턴스를 사용한다는 가정 하에 그 하위 모듈을 사용하는 코드를 작성해두면 되고, 2) 하위 모듈은 추상 클래스의 추상 메서드들을 오버라이딩만 하면 된다.

 

 

위처럼 interface를 추상화 레이어로 사용해서 상위 모듈과 하위 모듈이 inteface에 의존하게 된다면, 위의 3가지 문제점들이 해결된다.

1) 레파지토리 변경으로 서비스 변경, 서비스 변경으로 컨트롤러 변경을 해야하는 일을 없앨 수 있다.

2) 추상화 레이어에 의존하게 되니 사람의 사고가 흐르는데로 Controller > Service > Repository 순서로 작업할 수 있다.

3) A개발자는 Controller단에서 회원가입 로직과 로그인 로직을, B개발자는 Service단에서 회원가입 로직과 로그인 로직을 사용할 개발할 수 있다. 

 

 


 

정리

DIP를 지켜지기 위해서는 2가지를 지켜야한다는 것을 생각해볼 수 있다.

 

추상 타입에 의존하라

프로그램에는 추상화 수준이라는 개념이 있는데, 추상화 수준은 입출력으로부터의 거리를 뜻한다. 추상화 수준이 낮다는 것은 기계와 가까운 구체적인 처리를 말하며, 추상화 수준이 높다는 것은 사람과 가까운 추상적인 처리를 말한다.

위의 그림의 일부처럼 Service와 Repository 두 클래스 모두 추상 타입인 Interface를 향한 의존 관계를 갖게 된다면, 높은 추상화 수준의 모듈(Service)이 낮은 추상화 수준의 모듈(Repository)에 의존하는 상황도 해소되고 '두 모듈 모두 추상 타입에 의존할 것'이라는 원칙도 지켜진다. 본래 구상 타입의 구현에 의존하던 것이 추상 타입을 의존하게 되면서 의존 관계가 역전된다.

일반적으로 추상 타입은 자신을 사용할 클라이언트가 요구하는 정의다. 즉, Repository는 Service를 위해 존재하는 것이다. 그리고 Interface를 준수해 Repository를 구현하면 방침의 주도권을 Service에게 넘긴다는 뜻이 된다. '추상 타입이 구현의 세부 사항에 의존해서는 안 된다. 구현의 세부 사항이 추상 타입에 의존해야 한다'는 원칙은 이런 식으로 준수된다.

 

둘째. 주도권을 추상 타입에 둬라

전통적인 소프트웨어 개발 기법에서는 추상화 수준이 높은 모듈이 나은 모듈에 의존하는 형태로 만들어지는 경향이 있었다. 다시 말하면 추상 타입이 세부 사항에 의존하는 형태다.

추상 타입이 세부 사항에 의존하면 낮은 추상화 수준의 모듈에서 일어난 변경이 높은 추상화 수준의 모듈까지 영향을 미치게 되는 이상한 상황이 발생한다. 중요도가 높은 도메인 규칙은 항상 추상화 수준이 높은 쪽에 기술되는데, 낮은 추상화 수준의 모듈에서 일어난 변경 때문에 더 중요한, 추상화 수준이 높은 모듈을 수정해야 하는 상황(예를 들어, 데이터스토어 변경 때문에 비즈니스 로직을 변경하는)이 일어나는 것이다. 이는 바람직한 상황이 아니다.

 

주체가 되는 것은 추상화 수준이 높은 모듈, 추상 타입이어야 한다. 추상화 수준이 낮은 모듈이 주체가 되어서는 안 된다. 

 

추상화 수준이 높은 모듈은 낮은 모듈을 이용하는 클라이언트다. 클라이언트가 할 일은 어떤 처리를 호출하는 선언이다. 앞서 설명했듯이 인터페이스는 구현할 처리를 클라이언트에 선언 하는 것이며 주도권은 인터페이스를 사용할 클라이언트에 있다. 추상화 수준이 낮은 모듈을 인터페이스와 함께 구현하면 좀 더 중요돡 높은 고차원적 개념에 주도권을 넘길 수 있다.

 

 


 

 

referance

* 의존관계 역전 원칙 - 위키백과

* 3. [의존관계역전원칙] 의존 관계 역전 원칙 정리

* 의존성 역전 원칙 (DIP: Dependency Inversion Principle)

* SOLID 원칙 5 - DIP: 의존성 역전 원칙 (Dependency Inversion)

* 도메인 주도 설계 철저 입문 - 위키북스

* SOLID. 의존관계 역전 원칙(Dependency inversion principle)

 

 

 

 

반응형
Comments