DIP(Dependency Inversion Principle)

기술/아키텍처 2021. 5. 9. 08:57 Posted by 아는 개발자

좋은 아키텍처는 변동성이 큰 모듈에 의존하지 않는 것이다. 그런데 개발하다 보면 예상치 못한 버그도 종종 생기기 마련이기 때문에 어떤 클래스는 릴리즈마다 계속 수정을 할 수 밖에 없다. 그런데 이때마다 새로운 함수를 추가하고 새로운 변수가 등장한다면 이 클래스를 의존하는 다른 모듈에도 영향이 미친다. 이런 형태면 하나의 클래스를 수정하는데도 다른 클래스까지 영향을 주게 된다.

 

그림 1

위 그림에선 사용자가 버그가 많은 결제 시스템 클래스를 의존하고 있다. 지금까지 pay 함수에 버그가 많아서 3개의 레거시 함수가 있다. 이런 형태는 새로운 함수가 추가될 때 마다 사용자의 코드에 영향을 주게 되는 사례다.

 

해법은 인터페이스를 이용하는 것이다. 모듈은 안정화된 인터페이스에 의존하고 변동성이 큰 실제 구현체는 인터페이스를 바꾸지 않는 선에서 수정한다. 인터페이스가 바뀌지 않는것이 보장됐기 때문에 원래 실제 구현체에 의존하는 클래스는 수정이 있어도 코드를 수정하지 않아도 된다. 이런 철학으로 만든 원칙이 DIP(Dependency Inversion Principle) 의존성 역전 원칙이다.

 

그림 2

그림 2는 그림 1에서 DIP를 적용한 버전이다. 사용자는 안정화된 결제시스템 Interface를 참조하고 있기 때문에 수정할 일이 없다. 버그가 많은 결제시스템만 수정해도 소프트웨어의 안정성은 보장된다. 요즘에는 프레임워크 차원에서 이렇게 구현할 수 있도록 지원하고 있다. 안드로이드의 Hilt, Dagger가 DIP를 지원하는 대표적인 라이브러리니 아직 사용해보지 않은 분들은 한번 써보는게 좋을 것 같다.

728x90

'기술 > 아키텍처' 카테고리의 다른 글

클린 아키텍처  (0) 2021.05.20
DIP(Dependency Inversion Principle)  (0) 2021.05.09
ISP (Interface Segregation Principle)  (0) 2021.05.09
LSP (Liskov Substitution Principle)  (0) 2021.05.09
OCP (Open Closed Principle)  (0) 2021.05.04
SRP (Single Responsibility Principle)  (0) 2021.05.04

ISP (Interface Segregation Principle)

기술/아키텍처 2021. 5. 9. 08:28 Posted by 아는 개발자

ISP는 필요 이상 많은 것을 포함하는 모듈에 의존하지 말자는 원칙에서 유래됐다. 이것도 사례를 먼저 보자.

 

위 그림을 보면 결제관리, 환불관리, 포인트 관리 객체가 시스템 클래스를 참조하고 있다. 그런데 결제관리 객체는 pay 함수만 호출 할 것이고, 환불 관리는 refund, 포인트관리는 addPoint함수만 사용할 것이다. 각 객체의 입장에서는 필요 이상으로 시스템 클래스에 의존하고 있는 형태다. 만약 객체에서 불필요하게 다른 함수를 호출한다면 에러가 발생할 위험도 있다.

 

이런 경우에는 시스템을 각각 쪼개주는 방법이 있다. 결제시스템, 환불시스템, 포인트 시스템을 각각 인터페이스로 만들어고 시스템 클래스가 인터페이스의 하위타입으로 구현한다. 그리고 각각의 객체는 관계가 있는 인터페이스에만 의존하도록 만들면 의존하는 모듈에 불필요하게 포함된 함수들을 제거할 수 있다.

728x90

'기술 > 아키텍처' 카테고리의 다른 글

클린 아키텍처  (0) 2021.05.20
DIP(Dependency Inversion Principle)  (0) 2021.05.09
ISP (Interface Segregation Principle)  (0) 2021.05.09
LSP (Liskov Substitution Principle)  (0) 2021.05.09
OCP (Open Closed Principle)  (0) 2021.05.04
SRP (Single Responsibility Principle)  (0) 2021.05.04

LSP (Liskov Substitution Principle)

기술/아키텍처 2021. 5. 9. 08:11 Posted by 아는 개발자

LSP는 1988년에 미국MIT 공과대학 교수였던 바버라 리스코브가 제안한 것으로, 상호 대체 가능한 구성요소를 이용해 소프트웨어 시스템을 만들 수 있으려면, 이들 구성요소는 반드시 서로 치환가능해야 한다는 원칙이다. 문장으로 보면 무슨뜻인지 정확히 파악하기 어려우나 원칙을 지킨 사례와 어긴 사례를 보면 어떤 의미인지 감을 잡을 수 있을 것이다.

 

원칙을 지킨 사례

위 그림은 원칙을 지킨 사례다. 구매자가 결제 시스템을 사용하는데 결제 시스템에선 네이버페이, 카카오페이 둘중 하나를 사용하고 있다. 구매자는 네이버페이를 사용하던, 카카오페이를 사용하던 동일한 결제시스템 인터페이스를 사용하게 될 것이고 영향 받지 않는다. 따라서 결제 시스템은 필요에 따라 하위타입을 치환 가능하다. 객체지향형 프로그램을 개발해본 사람이라면 이렇게 설계하는 건 매우 당연한 일이다.

 

원칙을 어긴 사례

위 그림은 원칙을 어긴 유명한 사례다. 사용자는 직사각형에 의존하고 있는데 직사각형의 하위 타입은 정사각형으로 선언돼있다. 수학적으로보면 정사각형은 직사각형에 포함되기 때문에 위 관계가 맞으나 위 설계상에선 위 관계가 적합하지 않다. 사용자가 의존하는 직사각형 클래스에는 setWidth, setHeight 함수가 있다. 너비와, 높이를 설정해주는 함수다. 그런데 정사각형은 높이가 너비가 같은 사각형이다. setWidth, setHeight 함수가 의도한 대로 동작하지 않게 되므로 정사각형은 하위타입으로서 적합하지 않다.

728x90

'기술 > 아키텍처' 카테고리의 다른 글

클린 아키텍처  (0) 2021.05.20
DIP(Dependency Inversion Principle)  (0) 2021.05.09
ISP (Interface Segregation Principle)  (0) 2021.05.09
LSP (Liskov Substitution Principle)  (0) 2021.05.09
OCP (Open Closed Principle)  (0) 2021.05.04
SRP (Single Responsibility Principle)  (0) 2021.05.04

OCP (Open Closed Principle)

기술/아키텍처 2021. 5. 4. 18:39 Posted by 아는 개발자

SOLID 원칙의 두번째 원칙인 OCP (Open-Closed Principle)은 소프트웨어가 기존 코드를 수정하기보단, 새로운 코드를 추가함으로써 시스템의 행위를 변경할 수 있는 설계 원칙이다. OCP 원칙을 들어보지 못했더라도 Spring이나 안드로이드 최신 개발 프레임워크에서 지향하는 구조 (Spring Repository-Service-Controller 구조, 안드로이드 MVVM)를 사용하고 있다면 암묵적으로 OCP 원칙에 따르고 있게 되는데 이 원칙의 핵심은 소프트웨어를 이루는 컴포넌트를 저수준에서 고수준으로 계층화하는 것이다.

 

위 그림은 웹페이지를 출력하는 클라이언트를 단순하게 표현한 것이다. Interactor는 서버에 있는 데이터를 가져 올 수 있는 인터페이스의 역할을 하고, Controller는 Interactor를 통해서 노출할 정보를 가져오고 Presenter는 Controller에서 가져온 정보를 일차적으로 처리한 후 WebView는 Presenter에서 일차적으로 처리한 정보를 이용해 화면에 노출한다. 데이터의 연산이 가장 적은 WebView는 가장 저수준의 컴포넌트고 서버와 맞닿아 있는 Interactor는 가장 고수준의 소프트웨어다. 

 

컴포넌트간의 의존 관계가 일방향이기 때문에 컴포넌트의 변화가 미치는 영향을 최소화 할 수 있어 소프트웨어의 수정이 쉬워진다. 화면상의 버튼의 위치를 수정하게 되는 경우에는 WebView만 수정하면 되게 되고 불러오는 일부 데이터 처리의 변경하고 싶다면 Presenter를, 불러오는 데이터를 변경한다면 Controller를 수정하면 된다. 변경하려는 구조가 저수준에서 고수준으로 갈수록 수정이 미치는 영향이 커지지만, 저수준의 컴포넌트의 변화에서 고수준 컴포넌트를 보호 할 수 있어 유지관리에 유리하다. 

 

 

728x90

'기술 > 아키텍처' 카테고리의 다른 글

클린 아키텍처  (0) 2021.05.20
DIP(Dependency Inversion Principle)  (0) 2021.05.09
ISP (Interface Segregation Principle)  (0) 2021.05.09
LSP (Liskov Substitution Principle)  (0) 2021.05.09
OCP (Open Closed Principle)  (0) 2021.05.04
SRP (Single Responsibility Principle)  (0) 2021.05.04

SRP (Single Responsibility Principle)

기술/아키텍처 2021. 5. 4. 17:30 Posted by 아는 개발자

소프트웨어 디자인 원칙으로 유명한 SOLID에서 처음으로 소개되는 항목 SRP는 Single Responsibility Principle 의 준말이다. 풀네임의 뜻을 통해 원칙의 의미를 추측한다면 "모든 모듈은 하나의 일만 해야 한다"로 오해하기 쉬운데 이건 함수인 경우에 적용되는 원칙이지 모듈 범위에서 SRP가 뜻하는 바는 이와 다르다. 디자인 원칙에서 SRP는 "하나의 모듈은 하나의, 오직 하나의 액터에 대해서만 책임져야 한다"는 것을 의미한다. 아마 이 문장의 느낌이 바로 와닿지가 않을 것 같기 때문에 다음 예제를 소개한다.

 

위 그림 상에의 Employee 클래스는 초과근무 시간(overtimeHours)을 속성으로 갖고 있고 재무팀에선 이 정보를 이용해 초과근무 수당을 계산하는 함수를(overtimeHours) 관리하고, 인사팀에서는 허위로 초과근무 시간을 채우는 사람이 없는지 감시하고자 초과근무 시간을 보고하는 함수(reportHours)를 관리하고 있다. 두개의 액터가 하나의 클래스를 변경 할 수 있는 요인이기 때문에 이미 SRP원칙을 어긴 구조다.

 

갑자기 인사팀에서 초과 근무 시간을 보고하는 함수를 수정해야하는 일이 생겼다. 할 일은 많은데 CEO는 계속 초과근무 시간을 줄이라고 하니 어쩔 수 없이 데이터를 조작하기로 했다(실제로 그러시면 안됩니다). 회사원 별로 근무한 초과 시간에서 30% 를 낮추고자 했는데 문제는 reportHours() 함수의 리턴 값을 수정한게 아니라 클래스의 속성값인 overtimeHours를 수정한 것이다. 이 속성값은 인사팀만 사용하는 것이 아니라 재무팀도 사용하고 있었기 때문에 calculatePay 함수가 영향을 받게 됐고 근로자는 실제로 초과 근무한 시간의 30%를 제외한 부분만 인정돼 수당의 70% 만 받게되는 사건이 벌어졌다. 그 결과 재무팀은 초과근무한 직원들로부터 끝없는 컴플레인을 받게 됐고 이 소식을 들은 CEO는 인사팀이 데이터를 조작했다는 사실을 알게돼 인사팀장이 아주 난처해졌다는 웃픈 가상의 이야기.

 

몇몇 분들은 바보 같이 overtimeHours를 수정한게 문제라고 할 것 같다. 개발자가 공통으로 사용하고 있는 속성 값을 건드린 건 아마추어적인 실수긴 하다. 그러나 실제 코딩을 해보면 이런 아마추어적인 실수를 종종 하게된다. 모든 코드를 분석할 시간이 없기 때문에 하나의 클래스를 참조하는 모든 연관관계를 보는 것은 불가능하다. 그래서 등장한 소프트웨어 원칙은 이런 실수를 미리 만들지 않게끔 구조를 세울 수 있도록 한다. 앞서 소개한 예제를 SRP원칙을 적용해 변형한 버전은 이렇게 바꿔볼 수 있다.

 

HoursRepoter, PayCalculator 두 개의 클래스가 새로 생겼고 각각의 클래스는 인사팀, 재무팀이 관리한다. Employee는 HoursRepoter클래스와 PayCalculator 클래스를 참조해 calculatePay 와 reportHour 함수의 리턴값을 처리한다. overtimeHours는 인사팀, 재무팀 모두 변경할 수 없게끔 분리했기 때문에 앞으로 초과근무 수당이 미지급되는 사건이 벌어진다면 모두 재무팀의 문제로(?) 돌릴 수 있게 됐다. 

 

SRP원칙을 처음 들어봤을 땐 쉽다고 생각했는데 예제를 공부하면서 깊이 생각해보게 되고 내가 짠 코드를 보면 볼수록 더 깊이가 있는 원칙인 것 같다. 클래스 구조를 단순하게만 바라보게 되는건 아닌지 다시 한번 생각해보게끔 하는 원칙이다.

728x90

'기술 > 아키텍처' 카테고리의 다른 글

클린 아키텍처  (0) 2021.05.20
DIP(Dependency Inversion Principle)  (0) 2021.05.09
ISP (Interface Segregation Principle)  (0) 2021.05.09
LSP (Liskov Substitution Principle)  (0) 2021.05.09
OCP (Open Closed Principle)  (0) 2021.05.04
SRP (Single Responsibility Principle)  (0) 2021.05.04