[Clean Architecture] Clean Architecture OCP(Open-Closed Principle 개방-페쇄 원칙)
카테고리: Clean Architecture
OCP의 정의
개방-폐쇄 원칙 이라는 용어는 1988년에 버트란트 마이어가 만들었는데, 정의는 다음과 같습니다.
소프트웨어 객체는 확장에는 열려 있어야 하고, 변경에는 닫혀있어야 한다.
다시 말해 소프트웨어 객체의 행위는 확장할 수 있어야 하지만, 이때 객체를 변경해서는 안됩니다. 소프트웨어 아키텍처를 공부하는 가장 근본적인 이유가 바로 이 때문입니다. 만약 요구사항을 살짝 확장하는 데 소프트웨어를 엄청나게 수정해야 한다면, 그 소프트웨어 시스템을 설계한 아키텍트는 엄청난 실패에 맞닥뜨린 것입니다.
즉, 반대로 이야기하면 소프트웨어 아키텍처가 훌륭하다면 어떤 요구사항이 변경되었을 때, 코드의 양이 가능한 최소화 될것입니다. (이상적인 변경량은 0 입니다.)
그렇다면 어떻게 하면 될까요? 서로 다른 목적으로 변경되는 요소를 적절하게 분리하고, 이들 요소 사이의 의존성을 체계화 함으로써 변경량을 최소화할 수 있습니다.(여기서 DIP의 개념이 사용됨)
위 그림을 이해하는데 시간이 많이 걸렸습니다. <I>
로 표시된 클래스는 인터페이스이며, <DS>
로 표시된 클래스는 데이터 구조입니다. 화살표가 열려있다면 사용관계이고, 화살표가 닫혀있다면 구현 관계 또는 상속 관계 입니다.
여기서 주목할 점은 모든 의존성이 소스 코드 의존성을 나타낸다는 사실입니다. 예를 들어 화살표가 A 클래스에서 B 클래스로 향한다면, A 클래스에서는 B 클래스를 호출하지만, B 클래스는 A 클래스를 전혀 호출하지 않습니다.
예를들어, FinancialDataMapper
는 구현 관계를 통해 FinancialDataGateway
를 알고 있지만, FinancialDataGateway
는 FinancialDataMapper
에 대해 아무것도 알지 못합니다.
여기서 주목해야 할 또 다른 점은 이중선은 화살표와 오직 한 방향으로만 교차한다는 사실입니다. 아래의 그림은 컴포넌트 관계도 입니다. 이걸 보면, 모든 컴포넌트 관계는 단방향으로 이루어지고 있습니다. 이들 화살표는 변경으로부터 보호하려는 컴포넌트를 향하도록 그려집니다.
만약 A,B 컴포넌트가 있다고 했을 때, A 컴포넌트에서 발생한 변경으로부터 B 컴포넌트를 보호하려면 반드시 A 컴포넌트가 B 컴포넌트에 의존해야 합니다.
위 예제의 경우 Presenter에서 발생한 변경으로부터 Controller를 보호하고자 합니다. 그리고 View 에서 발생한 변경으로부터 Presenter를 보호하고자 합니다. Interactor는 모든 것에서 발생한 변경으로부터 보호하고자 합니다.
(여기서 Interactor란, 비지니스 로직이 존재하는 가장 높은 수준의 정책을 포함하는 계층입니다. 웹에서 Service 계층이 Interactor 에 대응됩니다.)
Interactor는 OCP를 가장 잘 준수할 수 있는 곳에 위치합니다. Database, Controller, Presenter, View 에서 발생한 어떤 변경도 Interactor에 영향을 주지 않습니다.
왜 Interactor가 이처럼 특별한 위치를 차지해야만 하는걸까요? 그 이유는 바로 Interactor가 비지니스 로직을 포함하기 때문입니다. 즉, Interactor는 애플리케이션에서 가장 높은 수준의 정책을 포함합니다. 가장 중요하고 변경이 자주 일어나는 곳이 Interactor이기 때문입니다.
보호의 계층구조가 Level 이라는 개념을 바탕으로 어떻게 생성되는지 주목해 봅시다. Interactor는 가장 높은 수준의 개념이며, 따라서 최고의 보호를 받아야 합니다. View는 가장 낮은 수준의 개념 중 하나이며, 따라서 거의 보호를 받지 못합니다.
이것이 바로 아키텍쳐 수준에서 OCP가 동작하는 방식입니다. 아키텍트는 기능이 어떻게, 왜, 언제 발생하는지에 따라서 기능을 분리하고, 분리한 기능을 컴포넌트의 계층구조로 조직화 합니다. 컴포넌트 계층구조를 이와 같이 조직화하면 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있습니다.
결론
OCP(Open/Closed Principle)는 시스템을 확장 가능하면서도 변경으로부터 보호하는 아키텍처 원칙입니다. 이 원칙을 아키텍처 관점에서 이해하려면, 시스템이 어떻게 확장될 수 있는지, 그리고 변경에 대한 영향을 최소화하는 설계를 고려해야 합니다.
일반적으로 OCP는 Shape 예제로 자주 설명되며, 이 예제는 Shape 클래스의 면적을 계산하는 로직을 변경하지 않고 새로운 형태를 추가하는 방식으로 확장할 수 있음을 보여줍니다. 그러나 이 예제는 단일 클래스의 확장성에 초점을 맞추기 때문에 아키텍처 전반에 대한 OCP를 이해하는 데는 한계가 있을 수 있습니다.
책에서 설명한 아키텍처 관점의 OCP는 시스템의 컴포넌트들이 어떻게 보호 수준을 관리하며, 변경을 최소화하면서도 확장할 수 있는지를 강조합니다. 이 과정에서 Interactor의 개념을 제대로 이해하는 것이 중요한데, 처음 접하는 개념이라 이해하는데 다소 어려움이 있었습니다.
Interactor는 비즈니스 로직을 처리하는 핵심 역할을 하며, 시스템의 다른 부분들과의 결합을 느슨하게 유지하면 서도 변경에 강한 구조를 제공합니다. 웹 프레임워크에서 서비스 인터페이스를 사용하는 이유는 바로 이 점에서 명확히 드러납니다. 서비스 인터페이스를 통해, 비즈니스 로직과 외부 시스템 간의 의존성을 분리하고, 이를 확장할 때 기존 코드에 영향을 주지 않도록 하여 OCP를 준수하는 설계를 할 수 있게 됩니다.
결국, OCP는 시스템의 각 부분을 독립적으로 확장 가능하게 만들면서도, 변경으로 인한 위험을 최소화하는 방향으로 설계되어야 한다는 점이 중요합니다. 이 과정에서 인터페이스나 Interactor와 같은 요소들이 어떻게 중요한 역할을 하는지 이해할 수 있게 되었던 유익한 시간이었습니다.
댓글 남기기