본문 바로가기

Design/Architecture

[Clean Architecture]SOLID 원칙 : 4.ISP 인터페이스 분리 원칙

ISP 란?

이 단원에서는 책에서 한줄로 명확하게 ISP를 정의하는 문장은 없었으나, 내용상 아래와 같이 요약할 수 있겠다.

 

유연하며 낮은 결합도가 낮은 구조를 통해 불필요한 의존성을 제거해야한다는 원칙.

 

 

[Clean Architecture] ISP 적용이 필요한 다이어그램

책에 있는 위 다이어그램을 통해 ISP를 이해해보도록 하자. (정적 타입 언어를 사용한다 가정)

 

 위 그림을 보게되면, OPS라는 클래스의 인스턴스를 User1, User2, User3라는 프로그램에서 사용하고있다. 이때, User1은 OPS.op1()만, User2는 OPS.op2()만, User3는 OPS.op3()만 사용한다고 가정한다. 위 구조상 User1의 경우 op2와 op3를 사용하지 않음에도 불구하고, op1뿐만 아니라 op2, op3에도 의존성을 가지고 있다. 따라서 op1이 아닌 다른 함수의 수정이 발생해도 재컴파일 및 배포가 필요한 비효율성이 발생하게 된다. 즉, 불필요한 의존성이 발생하고 있다는 것을 확인할 수 있다.

 

 이 문제를 해결하기 위해서는 OPS와 User1,2,3 중간에 인터페이스를 통해 이러한 의존성을 분리하는 작업이 필요하다.

 

[Clean Architecture] ISP 적용된 다이어그램

 위 다이어그램을 보게 되면, 대표적으로 User1은 op1함수 사용을 위해 OPS인스턴스를 사용하는 것이 아닌, U1Ops라는 인터페이스를 사용하도록 변경된 것을 확인할 수 있다. 즉, U1Ops는 OPS와 User1사이를 분리시켜주는 역할을 하고있다. 따라서 OPS에는 의존하지 않게 되고, User1과 관계없는 변경이 발생할 경우 재컴파일 및 배포는 불필요하다(나머지 User2, User3도 동일한 유형이니 비슷하게 의식의 흐름으로 짚어보도록 하자). 추가적으로 User1,2,3과 OPS사이의 의존성 역전이 일어난 것을 추가로 확인할 수 있다.

 

User1 프로그램의 입장에서 ISP 원칙 적용을 위한 사고흐름.

  1. User1에서 OPS의 op1함수를 사용하고 있군,, OPS는 어떤 클래스인지 살펴보자!
  2. 엇, OPS에서 op1 말고 다른 함수들도 있구나... (op2는 User2가 사용하고, op3는 User3가 사용을 하고있네.)
  3. op1 말고 다른 함수가 변경이 된다면 User1도 쓸데없이 다시 컴파일하고 배포해야하잖아???
  4. OPS랑 User1을 분리해야겠어. U1Ops 인터페이스로 결합도를 낮춰보도록하자!
  5. 이렇게 하면 OPS에 직접적으로 의존하지도 않고, op1 함수외에 다른 변경에는 영향을 받지 않으니 ISP가 잘 적용되었구나.

 

ISP 와 언어

ISP적용이 언어의 특성과 관련이 있음을 이야기한다.

 

정적 타입 언어

  • import, use, include와 같은 타입 선언문을 사용하도록 강제.
  • 타입 선언문으로 인한 의존성 발생.
  • 따라서 의존성이 있는 파일의 변경에 따른 재컴파일 또는 재배포가 강제되는 상황 발생.

동적 타입 언어

  • 소스 코드에서 타입 선언문이 존재하지 않음.
  • 대신 런타임에서 추론이 발생.
  • 소스 코드의 의존성이 아예 없으며, 따라서 재컴파일과 재배포가 필요없다.

**따라서, 동적 타입 언어를 사용하면 정적 타입 언어를 사용할 때보다 유연하며 결합도가 낮은 시스템을 만들 수 있음.

 

JAVA의 경우

  • 기본적으로 정적 타입의 언어.
  • 비-final, 비-private 인스턴스 변수에 대해서 런타임에 호출할 정확한 메서드가 결정됨.(late binding)
  • 컴파일 타임에는 호환되는 시그니처의 메서드가 타입 계층구조 어딘가에 존재하는지까지만 확인할 뿐이다.
  • 위 첫번째 다이어그램에서 자바의 경우는 User2만 재컴파일 하면 된다.

** ISP는 언어 종류에 따라 영향받는 정도가 다름을 알 수 있음.

 

ISP 와 아키텍처

 ISP의 개념은 단순히 소스 코드에 국한 된 것이 아니라, 아키텍처 수준에서도 신경을 써야한다.

 책의 예시대로 System S가 Framework F를 새롭게 도입하려고 할 때, F가 S와는 관계없는 Database D를 포함하는 기능을 가지고있다면, D의 변경에 따라 F 그리고 아무런 연관이 없는 S도 재컴파일 및 배포해야하는 상황이 발생 할 수 있다.

 

 

결론

교훈 : 불필요한 짐을 실은 무언가에 의존하면 예상치도 못한 문제에 빠진다.