본문 바로가기
Concepts/SW Architecture

설계원칙 - SOLID(OCP)

by ocwokocw 2021. 2. 10.

- 이 글은 로버트 C.마틴의 Clean Architecture를 기반으로 작성되었습니다. (가능하면 책을 읽어보는것을 추천한다.)

https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle

- 개요

소프트웨어 개체(클래스, 모듈, 함수)는 확장에는 열려있어야 하며, 변경에는 닫혀 있어야 한다. 즉, 개체의 행위는 확장할 수 있어야 하지만 이때 개체를 변경해서는 안된다. 만약 요구사항 확장하는데 기존 소프트웨어를 많이 수정해야 한다면 안된다는것이다.


- 예제

특정 데이터를 가공하여 웹 페이지에 보여주는 시스템이 있다고 하자. 이때 갑자기 이해관계자가 PDF나 Excel 로 보여줄 수 있게 기능을 추가해달라고 한다. 이때 소스코드를 얼마나 변경해야 할까? 

 

이상적으로 S/W 설계가 되었다면 업무규칙 모듈(특정 데이터를 가공한 모듈까지)의 소스코드는 변경이 없어야 하며, PDF나 Excel View를 추가(확장)할 수 있어야 한다.

해당 요구사항으로 컴포넌트 다이어그램을 한번 작성해보자. S/W 설계에 크게 신경쓰지 않았다면 그림은 아래와 같이 된다.

위의 다이어그램에서 화살표는 컴포넌트의 의존관계를 나타낸다. 예를 들어 Report Service는 Product Database를 사용(의존)하고 있다.

 

스프링이나 DDD 관련된 글을 읽어본적이 있다면 기업의 업무규칙(핵심가치)를 우선시한다는 늬앙스의 글을 본적이 있을 것이다. 컴포넌트에도 수준(Level)이 있다. 업무규칙이 정의된 Product Service는 가장 높은 수준의 컴포넌트이며, 다른 모듈이 변경되었다고해서 시시때때로 변경되면 곤란하다. 반면 가장 저수준의 컴포넌트에는 View들 및 Database가 있다. 가장 저수준이라는것은 세부사항이라는것을 의미하기도 한다.

 

위의 다이어그램은 그런점에서 썩 좋다고 말할 수는 없다. Product Service가 저수준의 Product Database 모듈에 의존하고 있으며, Presenter는 View에, Controller는 Presenter에 의존하고 있다. 이 의존관계를 뒤집으려면 어떻게 해야할까?

답은 interface에 있다. 아래 Diagram을 보자.

이전 다이어그램에 interface가 더해졌다. 이 interface 화살표 방향을 유심히 살펴보자. 고수준에서 저수준으로 향하던 의존성이 반대로 변경되었다. View -> Presenter -> Controller -> Service, Database -> Service로 핵심가치인 Service에 의존하게 되었다. 이제 저수준 컴포넌트의 변경으로 부터 고수준의 컴포넌트 변경이 일어나지 않게 되었다.


- 정보 은닉

그럼 모든 interface는 의존방향을 역전시키기 위해서만 존재하는가? 

 

여러분이 회사에 입사하던 시절을 잘 생각해보자. 할일이 없어서 멀뚱멀뚱 가만히 있는데 사수도 업무를 바로 시키기는 애매해서 게시판을 하나 만들어보라고 한다. 어떻게 만드는거지? 라면서 프로젝트 소스코드를 뒤진다. 소스코드를 보다보니 Controller, Service, ServiceImpl, Dao 패턴으로 코드를 작성하여 프로그램을 동작시키는것을 발견하였다. 아마 똑같이 게시판 기능을 구축했을것이다.

 

여기서 Controller는 Service에 의존한다. 컴포넌트 수준의 입장에서만 보자면 핵심가치인 Service가 변경되면 Controller도 변경이 되어야 하기 때문에 Controller가 Service를 의존(사용)하는것은 당연하다. 근데 왜 Service에 대해 interface를 만들고 해당 interface를 구현(implements)할까? 이는 controller가 service에 대해, 또는 타 service가 해당 service에 대해 너무많이 알지 못하도록 은닉하기 위함이다.


- 결론

OCP의 목적은 시스템을 쉽게 확장시키고, 이로 인한 변경을 최소화 하기 위함이다. 컴포넌트를 설계해보고 저수준(세부사항) 컴포넌트와 고수준의 컴포넌트(핵심가치)가 무엇인지 파악해보자. 그리고 고수준의 컴포넌트가 저수준에 코드에 의존하지 않는지 잘 생각해보자.

 

저수준의 컴포넌트가 고수준의 컴포넌트에 의존하도록 만들어야 한다.

댓글