본문 바로가기

개발 관련 지식

Clean Architecture 정리 - 3부 8장 개방-폐쇄 원칙

정의

소프트웨어 개체의 행위는 확장할 수 있어야 하지만, 이때 개체를 변경해서는 안 된다.

만약 새로운 요구사항을 반영하기 위해 기존 코드를 광범위하게 수정해야 한다면, 그 구조는 OCP를 위반하고 있는 것이다.


단일 책임 원칙과 OCP

기능을 잘게 나누고 각각의 책임을 분리하는 단일 책임 원칙(SRP)은 OCP를 실현하기 위한 선행 조건과 같다.

SRP가 잘 지켜진 구조에서는 새로운 기능을 추가하더라도 기존 코드의 수정은 최소화된다.

책임의 분리가 확장의 단위를 만들어 주기 때문이다.

 

예시: 도서 추천 시스템

한 온라인 서점에서 사용자가 책을 검색하면, 기본적으로 텍스트 기반 정보(제목, 저자, 요약)가 출력된다.

 

마케팅팀이 갑자기 말한다.

"추천 도서 섹션에서는 이미지와 키워드를 중심으로 더 시각적으로 보여주고 싶어요."

 

이제 시스템은 두 가지 출력 방식을 제공해야 한다:

  • 일반 검색 결과 → 텍스트 기반 정보
  • 추천 도서 섹션 → 이미지 기반 시각 정보

이때 기존 출력 로직을 조건문으로 복잡하게 만들기보다는, 출력 책임을 분리해서 각각 독립적으로 구성하는 것이 바람직하다.

 

책임 분리 구조

  1. 책 데이터를 수집/구성하는 책임
  2. 출력 형식(텍스트 / 시각화)에 따른 변환 책임
    1. 일반 검색 결과 → 텍스트 기반 정보
    2. 추천 도서 섹션 → 이미지 기반 시각 정보

책임을 분리하면, 새로운 View를 추가하더라도 기존 기능(일반 검색 결과)를 손대지 않는다.

바로 이 점이 개방-폐쇄 원칙이 말하는 '변경 없이 확장'이다.


아키텍처 관점에서 OCP 지키기

아키텍처 관점에서 OCP를 지키기 위해 중요한 원칙 중 하나는 컴포넌트 간의 의존성 방향 제어다.

 

이를 위해 도서 추천 시스템은 다음과 같은 계층 구조로 나눌 수 있다:

  • BookController: 사용자 요청을 받아 처리 흐름을 제어
  • BookInteractor: 핵심 비즈니스 로직
  • BookPresenter: 화면 출력 방식에 맞게 데이터를 변환 (텍스트/시각화)
  • BookView: 실제 UI 구성 요소 (예: HTML, PDF)
  • BookDatabase: 영속 데이터 저장소

도서 추천 시스템 내 의존 구조는 다음과 같이 계층적으로 정리할 수 있다:

          [BookView]                      [BookView]
               ↓                               ↓
        [View Interface]                [BookPresenter]
               ↑                               ↓
        [BookPresenter]                 [BookController]
               ↓                               ↓
      [Presenter Interface]             [BookInteractor]
               ↑                               ↑
        [BookController]                 [BookDatabase]
               ↓        
     [Controller Interface]
               ↑
        [BookInteractor]
               ↓
      [Database Interface]
               ↑
        [BookDatabase]
  • 화살표 방향은 의존성이 향하는 방향을 의미한다.
    • 의존 당하는 상위 계층은 의존하는 하위 구현체를 모른다.

View, Presenter에 발생한 변경이 Controller나 Interactor에 영향을 주면 안 된다.

Interactor는 다른 객체의 변경에 영향받지 않도록 중앙에 위치해야 한다.

이 계층적 보호 구조 덕분에, 새로운 출력 방식(PDF, 모바일 앱 등)을 추가하더라도

Interactor나 Controller는 아무런 영향을 받지 않으며, 새로운 View와 Presenter만 작성하면 된다.

이는 개방-폐쇄 원칙의 이상적인 예다.


인터페이스의 역할

인터페이스는 단순한 추상화 수단이 아니다.

  • 의존성 역전(DIP): 하위 구현체가 상위 모듈에 영향을 주지 않도록 경로를 뒤집는다.
  • 추이 종속성 차단: 직접 사용하지 않는 모듈에 간접적으로 의존하지 않게 한다.

예:

A → B → C   // 추이 종속성

이 상황에서 A는 C에 영향을 받을 수 있다.

 

이를 막기 위해선:

A → I ← B → C   // 인터페이스 I로 의존성 단절

이처럼 인터페이스는 변경 전파를 차단하는 방패 역할을 한다.


결론

OCP는 시스템을 "유연하게 확장"하면서도 "기존 코드는 보호"하는 것을 목표로 한다.

 

이를 위해선

  • 단일 책임 원칙으로 책임을 나누고
  • 인터페이스로 의존성을 역전시키며
  • 변경 방향이 위로 흐르지 않도록 계층을 설계해야 한다.

※ 본 글은 『Clean Architecture』(로버트 C. 마틴 저) 3부의 8장을 기반으로 학습 목적으로 요약한 글입니다.

※ 이 글은 책의 내용을 요약한 것으로, 원문 없이 읽을 경우 오해의 여지가 있을 수 있습니다. 정확한 이해를 위해 원서의 정독을 권장합니다.