Unisquads
로그인
홈
이용약관·개인정보처리방침·콘텐츠정책·© 2026 Unisquads
이용약관·개인정보처리방침·콘텐츠정책
© 2026 Unisquads. All rights reserved.

OCP (r1)

이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.14 23:09

OCP

정식 명칭

Open Compute Project

설립 연도

2011년

설립 주체

Meta (당시 페이스북)

분류

오픈 소스 하드웨어 프로젝트

핵심 목표

데이터센터 하드웨어 설계의 개방, 공유, 혁신

주요 적용 분야

데이터센터, 서버, 스토리지, 네트워킹

상세 정보

배경

대규모 데이터센터 운영의 효율성과 비용 문제 해결을 위해 시작

운영 방식

커뮤니티 기반의 협업과 오픈 소스 설계 공유

주요 산출물

서버, 랙, 전원, 냉각, 네트워크 장비 등의 공개 설계도

참여 기업

인텔, 마이크로소프트, 구글, IBM, 엔비디아 등 다수

주요 이점

비용 절감, 에너지 효율 향상, 공급망 유연성, 벤더 종속 감소

표준화 기구 연계

하위 프로젝트 예시

Open Rack, Olympus 서버, Open CloudServer

라이선스

주로 OCP CLA(기여자 라이선스 협정) 및 Apache 2.0

주요 행사

OCP 글로벌 서밋, 지역별 포럼

현재 상태

데이터센터 인프라 산업의 주요 오픈 협업 체계로 성장

1. 개요

OCP는 객체 지향 프로그래밍과 소프트웨어 설계의 핵심 원칙 중 하나로, '소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하며, 수정에는 닫혀 있어야 한다'는 개념을 담고 있다. 이 원칙은 1988년 버트란드 메이어가 저서 '객체 지향 소프트웨어 설계'에서 처음 제시했으며, 이후 로버트 C. 마틴이 SOLID 원칙의 'O'로 공식화하여 널리 알려지게 되었다.

이 원칙의 목표는 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있도록 시스템을 설계하는 것이다. 이를 통해 기존에 동작하던 코드에 영향을 주지 않고 시스템의 행위를 확장할 수 있으며, 이는 곧 유지보수 비용을 줄이고 시스템의 안정성을 높이는 데 기여한다. OCP는 단순한 코딩 규칙이 아닌, 모듈 간의 의존성을 관리하고 결합도를 낮추는 설계 철학으로 이해되어야 한다.

OCP가 적용된 전형적인 예로는 다양한 그래픽 사용자 인터페이스 위젯이나 플러그인 기반의 아키텍처를 들 수 있다. 이러한 시스템에서는 새로운 위젯이나 플러그인을 추가하더라도 애플리케이션의 핵심 프레임워크 코드를 수정할 필요가 없다. OCP는 변화가 예상되는 부분을 예측하고, 추상화를 통해 그 변화를 격리함으로써 달성된다.

2. OCP의 기본 원칙

OCP는 소프트웨어의 구성 요소(클래스, 모듈, 함수 등)가 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다는 설계 원칙이다. 이는 기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있도록 시스템을 구조화해야 함을 의미한다. 이 원칙은 1988년 버트런드 메이어가 처음 제시했으며, SOLID 원칙의 두 번째 원칙으로 자리 잡았다.

핵심은 "확장에 열려 있음"과 "수정에 닫혀 있음"이라는 두 가지 축으로 구성된다. "확장에 열려 있음"은 애플리케이션의 요구사항이 변경될 때, 새로운 동작을 추가하여 모듈을 확장할 수 있어야 함을 말한다. 예를 들어, 새로운 결제 수단이나 보고서 형식을 지원해야 할 때, 기존 시스템이 이를 수용할 수 있는 구조를 가져야 한다. 반면 "수정에 닫혀 있음"은 이러한 확장을 위해 기존의 검증되고 안정적인 코드를 직접 수정하지 않아야 함을 의미한다. 기존 코드의 변경은 새로운 버그를 유발할 위험을 항상 내포하고 있기 때문이다.

이 두 원칙은 상충되는 것처럼 보이지만, 추상화를 통해 조화를 이룬다. 공통적인 기능을 인터페이스나 추상 클래스로 정의하고, 구체적인 구현은 이 추상화에 의존하도록 만드는 것이다. 이후 새로운 기능이 필요하면, 이 추상화를 구현하는 새로운 클래스를 추가함으로써 시스템을 확장한다. 결과적으로 기존의 핵심 로직은 변경되지 않은 채(닫혀 있음) 새로운 행위를 추가(열려 있음)할 수 있게 된다.

원칙

의미

목적

확장에 열려 있음(Open for extension)

새로운 기능이나 동작을 추가할 수 있어야 함

시스템의 유연성과 적응성을 보장

수정에 닫혀 있음(Closed for modification)

기능 추가를 위해 기존 코드를 변경하지 않아야 함

기존 코드의 안정성과 신뢰성을 유지

이러한 접근 방식은 소프트웨어가 시간이 지남에 따라 진화하면서도, 변경으로 인한 부작용을 최소화하는 데 기여한다.

2.1. 확장에 열려 있음

개방-폐쇄 원칙에서 '확장에 열려 있음'은 소프트웨어 모듈(예: 클래스, 함수, 컴포넌트)이 새로운 요구사항이나 변경 사항이 발생했을 때, 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있도록 설계되어야 함을 의미한다. 이는 모듈의 동작을 변경하거나 확장하는 것이 가능해야 한다는 것을 핵심으로 한다. 예를 들어, 새로운 결제 수단을 시스템에 추가해야 할 때, 기존의 결제 처리 로직을 직접 고치는 대신 새로운 결제 방식 클래스를 만들어 기존 구조에 통합할 수 있어야 한다.

이 원칙을 준수하는 설계는 주로 추상화와 다형성에 의존한다. 공통적인 기능을 인터페이스나 추상 클래스로 정의해 두면, 이후에 새로운 구체적인 구현체를 만들어 해당 추상화를 통해 시스템에 연결할 수 있다. 결과적으로, 기존의 코드 베이스는 안정적으로 유지된 채로, 새로운 동작을 가진 모듈을 추가함으로써 시스템 전체의 기능을 확장할 수 있게 된다. 이는 시스템의 수명 주기 동안 발생하는 지속적인 변경 요구에 유연하게 대응할 수 있는 기반을 마련해 준다.

'확장에 열려 있음'의 이점은 소프트웨어의 진화적 특성과 직접적으로 연결된다. 요구사항은 시간이 지남에 따라 변하고, 비즈니스 규칙은 추가되거나 수정된다. 이 원칙을 적용하면 이러한 변화에 대응하는 비용과 위험을 크게 줄일 수 있다. 기존 코드를 수정하지 않기 때문에, 이미 검증되고 안정적으로 동작하는 부분에 새로운 버그가 발생할 가능성이 낮아진다. 따라서 시스템은 새로운 기능을 추가하면서도 기존 기능의 안정성을 유지할 수 있게 된다.

2.2. 수정에 닫혀 있음

수정에 닫혀 있음은 기존의 검증된 코드를 변경하지 않고도 새로운 기능을 추가할 수 있어야 함을 의미한다. 이는 시스템의 핵심 로직이나 기존 모듈을 직접 수정하는 대신, 새로운 클래스나 모듈을 추가하여 기능을 확장하는 방식을 지향한다. 기존 코드를 변경하지 않음으로써, 해당 코드에 의존하는 다른 부분에서 발생할 수 있는 예기치 않은 버그나 사이드 이펙트를 방지한다.

이 원칙을 준수하면, 시스템의 안정성이 크게 향상된다. 이미 테스트를 거치고 정상적으로 동작하는 모듈은 수정의 대상에서 제외되므로, 새로운 요구사항이 발생하더라도 기존 기능의 신뢰성을 훼손하지 않는다. 결과적으로 소프트웨어의 주요 구성 요소는 변경으로부터 보호되며, 이는 장기적인 유지보수 비용을 절감하는 데 기여한다.

실제 구현에서는 인터페이스나 추상 클래스를 통해 안정적인 계약을 정의하고, 구체적인 기능의 변화는 이들을 구현하는 새로운 클래스를 만들어 처리한다. 예를 들어, 보고서 생성 기능에서 PDF 출력 외에 Excel 출력을 추가해야 할 경우, 기존 PDF 생성 클래스를 수정하는 대신 공통 인터페이스를 구현하는 새로운 Excel 생성 클래스를 도입한다.

접근 방식

설명

OCP 준수 여부

기존 클래스 직접 수정

기능 추가를 위해 원본 코드의 메서드를 변경함

위반

조건문 추가 (if/switch)

원본 코드에 새로운 조건 분기를 추가함

일반적으로 위반[1]

새로운 클래스 추가

다형성을 활용해 확장 포인트를 통해 기능 추가

준수

3. OCP 구현 방법

OCP를 구현하는 핵심은 변경이 필요한 부분을 추상화하여, 새로운 기능 추가 시 기존 코드를 수정하지 않고 확장할 수 있도록 설계하는 것이다. 주요 구현 기법으로는 추상화 활용, 인터페이스와 다형성 적용, 그리고 전략 패턴과 같은 디자인 패턴의 사용이 있다.

가장 기본적인 방법은 변하는 부분을 인터페이스나 추상 클래스로 추출하는 것이다. 예를 들어, 다양한 형태의 리포트를 생성하는 시스템에서, 각 리포트 유형별 로직을 공통 인터페이스 뒤로 숨긴다. 새로운 리포트 형식이 필요할 때는 이 인터페이스를 구현하는 새로운 클래스만 추가하면 되며, 리포트를 호출하는 기존 클라이언트 코드는 전혀 변경할 필요가 없다. 이는 다형성을 통해 구체적인 구현체를 런타임에 교체 가능하게 만든다.

전략 패턴은 OCP를 준수하는 대표적인 디자인 패턴이다. 이 패턴은 알고리즘 군을 정의하고 각각을 캡슐화하여 상호 교체가 가능하도록 만든다. 클라이언트는 구체적인 전략의 구현 내용을 알 필요 없이, 추상화된 전략 인터페이스에만 의존한다. 결과적으로 새로운 알고리즘(전략)을 추가하는 것은 기존 코드의 수정이 아닌, 새로운 클래스의 추가로 이루어진다.

적절한 구현 수준을 결정하는 것이 중요하다. 모든 가능한 변경에 대해 미리 추상화를 시도하는 것은 과도한 설계로 이어질 수 있다. 따라서 실제 변경이 예상되는 영역이나, 변경 역사가 있는 부분에 대해 점진적으로 리팩토링을 통해 OCP를 적용하는 것이 현명한 접근법이다. 최종 목표는 변경으로부터 시스템의 핵심 구조를 보호하는 안정적인 추상화 계층을 구축하는 것이다.

3.1. 추상화 활용

추상화는 OCP를 구현하는 핵심 메커니즘이다. 구체적인 구현 세부 사항이 아닌, 변하지 않는 핵심적인 개념이나 동작을 인터페이스나 추상 클래스로 정의하는 과정이다. 이렇게 추상화된 계층은 변경으로부터 안정적인 계층이 되어, 새로운 기능이 추가될 때 기존 코드를 수정하지 않고도 확장할 수 있는 틀을 제공한다.

예를 들어, 다양한 형태의 도형 넓이를 계산하는 시스템에서, 각 도형 클래스(예: Rectangle, Circle)가 calculateArea() 메서드를 직접 구현하면 새로운 도형이 추가될 때마다 넓이를 계산하는 로직을 수정해야 한다. 대신 Shape라는 추상 클래스나 인터페이스를 도입하고 calculateArea() 메서드를 추상 메서드로 선언한다. 이후 모든 구체적인 도형 클래스는 이 Shape를 상속하거나 구현하여 자신만의 넓이 계산 로직을 제공하면 된다. 새로운 Triangle 도형이 추가되더라도, Shape를 구현하는 새 클래스만 추가하면 되며 기존의 Shape 인터페이스나 다른 도형 클래스는 전혀 수정할 필요가 없다.

접근 방식

설명

OCP 준수 여부

구체적 클래스에 의존

각 도형 타입을 조건문(if/else 또는 switch)으로 분기 처리

위반. 새로운 도형 추가 시 조건문 로직 수정 필요

추상화에 의존

Shape 인터페이스에 의존하고, 각 도형이 이를 구현

준수. 새로운 도형은 인터페이스를 구현하는 새 클래스로 추가

따라서 추상화를 활용한 OCP 구현의 핵심은, 변하는 것과 변하지 않는 것을 분리하고, 클라이언트 코드가 구체적인 구현이 아닌 추상적인 개념에 의존하도록 설계하는 것이다. 이는 시스템의 핵심 모듈을 변경의 영향을 받지 않게 하여 안정성을 높이고, 확장 포인트를 명확하게 정의하는 효과가 있다.

3.2. 인터페이스와 다형성

인터페이스는 OCP를 구현하는 핵심 도구이다. 인터페이스는 '무엇을 해야 하는지'에 대한 계약을 정의하지만, '어떻게 하는지'에 대한 구체적인 구현은 포함하지 않는다. 이렇게 추상화된 계약에 의존함으로써, 클라이언트 코드는 구체적인 구현 클래스의 변경으로부터 보호된다. 새로운 기능이 추가되어야 할 때, 기존 인터페이스를 구현하는 새로운 클래스를 생성하면 되며, 기존 코드를 수정할 필요가 없어진다.

다형성은 이러한 인터페이스를 통해 다양한 객체가 동일한 메시지에 대해 각기 다른 방식으로 반응할 수 있게 하는 객체지향의 특성이다. 클라이언트는 인터페이스 타입을 통해 객체를 참조하고, 런타임에 실제 구현 객체의 메서드가 호출된다. 이는 시스템이 확장에 열려 있도록 만든다. 예를 들어, Notifier라는 인터페이스가 send() 메서드를 정의한다면, EmailNotifier, SMSNotifier, PushNotifier 등 새로운 알림 방식을 추가하더라도, 알림을 요청하는 클라이언트 코드는 변경되지 않는다.

인터페이스와 다형성을 활용한 OCP의 전형적인 구조는 다음과 같다.

구성 요소

역할

인터페이스 또는 추상 클래스

변하지 않는 고수준의 정책이나 규칙을 정의한다.

구체적인 구현 클래스들

인터페이스의 계약을 충실히 이행하는 다양한 저수준의 세부 사항을 구현한다.

클라이언트 코드

인터페이스 타입에만 의존하여, 구체 클래스의 존재를 알지 못한다.

이 접근법의 핵심은 의존성의 방향이다. 구체적인 구현 세부사항이 추상화된 인터페이스에 의존하도록 하고, 고수준의 모듈은 추상화에 의존하도록 해야 한다. 이렇게 하면 고수준 모듈은 저수준 모듈의 변경이나 확장에 영향을 받지 않게 되어, 수정에 닫혀 있는 상태를 유지할 수 있다.

3.3. 전략 패턴

전략 패턴은 OCP를 준수하는 대표적인 설계 기법 중 하나이다. 이 패턴은 실행 중에 알고리즘을 선택할 수 있도록, 알고리즘 군을 정의하고 각각을 캡슐화하여 상호 교환 가능하게 만든다[2]. 클라이언트 코드는 구체적인 알고리즘 구현이 아닌 추상적인 인터페이스에 의존하게 된다.

구현 방식은 일반적으로 다음과 같다. 먼저 공통의 연산을 정의하는 전략 인터페이스를 선언한다. 그 후, 이 인터페이스를 구현하는 다양한 콘크리트 클래스를 생성하여 각각의 구체적인 알고리즘을 구현한다. 마지막으로, 컨텍스트 클래스는 전략 인터페이스 타입의 참조를 유지하며, 필요에 따라 다른 콘크리트 전략 객체로 교체한다.

구성 요소

역할

전략(Strategy)

모든 알고리즘이 구현해야 하는 공통 인터페이스를 정의한다.

콘크리트 전략(Concrete Strategy)

전략 인터페이스를 구현하는 구체적인 알고리즘 클래스이다.

컨텍스트(Context)

전략 객체를 참조하며, 이를 통해 알고리즘을 실행한다.

이 패턴을 적용하면 새로운 알고리즘을 추가해야 할 때, 기존의 컨텍스트나 다른 전략 클래스의 코드를 수정하지 않고도 새로운 콘크리트 전략 클래스만 작성하면 된다. 이는 시스템이 "확장에는 열려 있고, 수정에는 닫혀 있다"는 OCP의 핵심을 직접적으로 실현한다. 정렬, 결제 수단 처리, 로깅 방식 변경 등 동일한 문제에 대한 여러 해법이 존재하는 상황에서 널리 활용된다.

4. OCP의 장점

OCP를 준수하는 설계는 소프트웨어의 유지보수성을 크게 향상시킨다. 새로운 기능을 추가하거나 기존 기능을 변경할 때, 기존 코드를 수정하지 않고 확장만으로 이를 달성할 수 있기 때문이다. 이는 기존 코드의 안정성을 해치지 않으면서 변경을 수용하는 것을 의미하며, 결과적으로 버그 발생 가능성을 줄이고 테스트 범위를 최소화하는 효과가 있다.

또한, 잘 정의된 인터페이스를 통해 기능을 분리하면 모듈의 재사용성이 증가한다. 특정 추상화에 의존하는 클라이언트 코드는 구체적인 구현의 변경으로부터 보호받으며, 새로운 구현체를 쉽게 교체하거나 추가할 수 있다. 이는 다양한 컨텍스트에서 동일한 컴포넌트를 재사용할 수 있는 기반을 마련해 준다.

가장 중요한 장점 중 하나는 시스템의 장기적인 안정성을 보장한다는 점이다. 핵심 비즈니스 로직이나 프레임워크의 기반 구조는 변경에 닫혀 있도록 설계되어, 새로운 요구사항이 발생하더라도 이 부분은 영향을 받지 않는다. 이는 소프트웨어의 핵심 부분이 부수적인 확장으로 인해 훼손되는 것을 방지하며, 시스템의 견고함과 신뢰성을 유지하는 데 기여한다.

장점

설명

유지보수성 향상

기존 코드 수정 없이 확장만으로 변경 가능. 리팩토링과 기능 추가가 용이함.

재사용성 증가

안정된 인터페이스를 통해 모듈을 다양한 컨텍스트에서 재사용 가능.

시스템 안정성 보장

핵심 로직은 변경에 닫혀 있어, 확장으로 인한 부작용과 버그 발생 위험 감소.

테스트 용이성

기존 코드를 변경하지 않으므로, 영향받는 범위가 적어 회귀 테스트 부담이 줄어듦.

4.1. 유지보수성 향상

OCP를 준수하면 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다. 이는 기존 코드의 동작이 변경되지 않음을 의미하므로, 새로운 기능 추가로 인해 기존 기능이 오작동할 가능성이 크게 줄어듭니다. 결과적으로 시스템의 유지보수성이 향상됩니다. 버그 수정이나 기능 확장 시 영향 범위를 최소화하여 테스트 부담을 줄이고, 변경 사항을 더 빠르고 안전하게 적용할 수 있습니다.

또한, 변경이 필요한 부분이 명확하게 모듈화되어 있기 때문에 코드를 이해하고 수정하는 데 드는 시간과 비용이 절감됩니다. 개발자는 변경의 파급 효과를 크게 걱정하지 않고도 특정 모듈이나 인터페이스의 구현체를 집중적으로 수정할 수 있습니다. 이는 장기적인 프로젝트 생명주기에서 코드베이스의 건강성을 유지하는 데 결정적인 역할을 합니다.

측면

OCP 적용 전

OCP 적용 후

변경 영향도

광범위한 코드 수정 필요

특정 모듈만 수정 또는 확장

테스트 범위

전체 시스템 재테스트 필요

추가/변경된 부분에 대한 집중 테스트 가능

버그 발생 위험

기존 기능 오작동 가능성 높음

기존 코드 변경 없어 안정성 유지

요약하면, OCP는 변경으로부터 시스템을 보호하는 설계 방어막을 제공합니다. 이는 소프트웨어의 유지보수성을 높여 개발 생산성을 증대시키고, 전체적인 프로젝트 비용을 낮추는 효과를 가져옵니다.

4.2. 재사용성 증가

기존 코드를 변경하지 않고도 새로운 기능을 추가할 수 있기 때문에, OCP를 준수하는 설계는 코드 재사용성을 크게 높인다. 모듈이 특정 변경에 대해 닫혀 있고 확장 포인트를 통해 새로운 동작을 추가하도록 설계되면, 해당 확장 포인트를 활용하는 다양한 컨텍스트에서 동일한 모듈이나 인터페이스를 재사용할 수 있다.

예를 들어, 다양한 형식의 보고서를 생성하는 시스템에서 OCP를 적용했다고 가정해보자. 공통 인터페이스를 정의하고, PDF 생성, HTML 생성 등 각 구체적인 형식은 이 인터페이스를 구현하는 별도의 클래스로 만든다. 이후 CSV 형식의 보고서가 새로 필요해지면, 기존의 보고서 생성 로직이나 다른 형식 생성 클래스를 전혀 수정하지 않고, 새로운 CSV 생성 클래스만 인터페이스를 구현하여 추가하면 된다. 이렇게 되면 기존의 보고서 생성 시스템 인프라는 그대로 재사용되며, 새로운 형식만 '플러그인' 방식으로 추가된다.

이러한 재사용성은 다음과 같은 이점을 제공한다.

  • 개발 효율성: 검증된 기존 모듈을 안전하게 재사용함으로써 개발 시간과 비용을 절감한다.

  • 일관성: 동일한 인터페이스와 추상화를 통해 구현된 기능들은 일관된 사용법과 동작을 보장한다.

  • 시스템 통합 용이성: 표준화된 확장 메커니즘을 통해 서드파티 모듈이나 라이브러리를 보다 쉽게 통합할 수 있다.

결국, OCP는 시스템을 확장 가능한 구성 요소들의 집합으로 만듦으로써, 이러한 구성 요소들이 다양한 시나리오에서 반복적으로 활용될 수 있는 기반을 마련한다. 이는 소프트웨어의 경제적 가치를 높이는 핵심 요소 중 하나이다.

4.3. 시스템 안정성 보장

기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있기 때문에, OCP는 시스템의 핵심 부분이 변경으로부터 보호받도록 합니다. 이는 기존에 정상적으로 동작하던 코드를 건드리지 않음으로써, 새로운 변경 사항이 예상치 못한 버그나 사이드 이펙트를 발생시킬 위험을 크게 줄입니다. 결과적으로 시스템의 전반적인 안정성과 신뢰성이 향상됩니다.

특히 대규모 애플리케이션이나 지속적으로 진화하는 소프트웨어에서 이 원칙은 중요합니다. 새로운 요구사항이 발생할 때마다 기존 모듈을 직접 수정하면, 변경의 영향 범위를 파악하기 어렵고 회귀 테스트의 부담이 커집니다. OCP를 준수한 설계는 새로운 기능을 독립된 확장(예: 새로운 클래스나 모듈)으로 추가하게 함으로써, 변경의 영향을 지역화하고 시스템의 나머지 부분이 안정적으로 유지되도록 보장합니다.

이러한 안정성은 테스트 용이성과도 연결됩니다. 기존 코드의 동작이 변경되지 않으므로, 해당 부분에 대한 기존 테스트 케이스는 그대로 유효합니다. 새로운 기능에 대해서만 추가적인 테스트를 집중할 수 있어, 테스트 커버리지를 유지하거나 높이는 데 도움이 됩니다. 이는 결국 소프트웨어의 품질과 배포 후의 안정성을 높이는 선순환 구조를 만듭니다.

5. OCP의 단점과 주의사항

OCP는 소프트웨어 설계의 유연성을 높이는 중요한 원칙이지만, 잘못 적용할 경우 오히려 설계를 복잡하게 만들고 생산성을 저하시킬 수 있다. 가장 흔한 문제는 과도한 추상화로 인한 복잡도 증가이다. 변경 가능성이 낮은 부분에까지 미리 추상화 계층을 도입하면, 불필요한 인터페이스와 클래스가 생겨 코드베이스가 비대해진다. 이는 이해하기 어렵고, 개발 속도를 늦추며, 오류 가능성을 높인다. 적절한 추상화 수준을 결정하는 것은 설계자의 경험과 판단에 크게 의존한다.

또한, OCP를 준수하려면 초기 설계 단계에서 변경될 수 있는 부분을 예측해야 한다. 이는 쉽지 않은 작업이며, 예측이 빗나갈 경우 추상화 구조 자체를 재구성해야 하는 상황이 발생할 수 있다. 잘못된 예측에 기반한 추상화는 오히려 변경을 더 어렵게 만드는 역효과를 낳는다. 따라서 변경이 실제로 발생하기 전에는 지나치게 일반화된 설계를 피하고, 실제 요구사항에 기반한 점진적인 리팩토링을 통해 OCP를 적용하는 것이 바람직하다.

주의사항으로는 성능 오버헤드를 고려해야 한다. 다형성과 의존성 주입을 위한 추가적인 간접 참조 계층은 미미하지만 런타임 비용을 발생시킨다. 대부분의 경우 이 비용은 무시할 수 있지만, 극도로 성능이 중요한 시스템에서는 설계 결정 시 고려 대상이 될 수 있다. 결국 OCP는 모든 곳에 적용해야 하는 절대적 법칙이 아니라, 변경 가능성이 높은 핵심 비즈니스 로직이나 확장 포인트에 선택적으로 적용해야 하는 지침으로 이해해야 한다.

5.1. 과도한 추상화 위험

OCP를 준수하기 위해 지나치게 많은 추상화 계층을 도입하면 오히려 설계를 복잡하게 만들고 코드의 가독성을 떨어뜨릴 수 있다. 모든 변경 가능성에 대해 미리 추상화를 시도하는 것은 불필요한 YAGNI 원칙 위반으로 이어질 수 있다. 미래의 변경 사항을 정확히 예측하기 어렵기 때문에, 실제로 필요하지 않은 추상화는 시스템에 불필요한 간접 참조와 복잡성만을 추가한다.

과도한 추상화는 다음과 같은 구체적인 문제를 발생시킨다.

문제점

설명

코드 이해도 저하

개발자가 실제 비즈니스 로직을 파악하기 위해 여러 계층의 인터페이스와 클래스를 거쳐야 함

성능 저하

불필요한 메서드 호출과 객체 생성이 발생할 가능성이 있음

개발 생산성 감소

간단한 기능 추가나 수정에도 많은 파일과 클래스를 수정해야 할 수 있음

따라서 OCP를 적용할 때는 현재 명확히 요구되는 확장점에 대해서만 추상화를 설계하고, 지나친 일반화를 피해야 한다. 변경이 예상되는 부분과 안정적인 부분을 구분하는 것이 중요하다. 실용적인 접근 방식은 요구사항이 명확해지고 변경이 실제로 필요해질 때 리팩토링을 통해 점진적으로 OCP를 적용하는 것이다.

5.2. 설계 복잡도 증가

OCP를 적용할 때 발생하는 주요 문제점 중 하나는 설계의 복잡도가 필요 이상으로 증가할 수 있다는 점이다. 새로운 기능을 추가하기 위해 추상화 계층과 인터페이스를 계속해서 도입하면, 시스템 구조가 지나치게 복잡해져 이해하고 관리하기 어려워진다. 특히 규모가 작거나 변경이 빈번하지 않은 프로젝트에서는 이러한 복잡도가 오히려 생산성을 저해할 수 있다.

설계 복잡도 증가는 다음과 같은 구체적인 문제를 야기한다.

문제점

설명

학습 곡선 증가

새로운 개발자가 복잡한 추상화 구조를 이해하는 데 많은 시간이 소요된다.

코드 가독성 저하

간단한 로직도 여러 계층을 거쳐 구현되어 코드를 따라가기 어렵다.

디버깅 난이도 상승

호출 흐름이 여러 클래스와 인터페이스를 넘나들어 문제 지점을 찾기 힘들다.

초기 개발 비용 상승

확장 가능성을 고려한 설계에 더 많은 시간과 노력이 투입된다.

따라서 OCP를 적용할 때는 프로젝트의 규모, 예상 수명, 변경 빈도 등을 종합적으로 고려하여 적절한 수준의 추상화를 결정해야 한다. 모든 부분에 원칙을 무조건적으로 적용하기보다는, 실제로 변경이 예상되는 영역에 집중하여 적용하는 것이 바람직하다. 이는 YAGNI 원칙과도 연결되는 접근 방식이다.

6. 실전 적용 사례

OCP는 이론적 개념을 넘어 다양한 소프트웨어 시스템 설계에 실제로 적용되어 그 유용성을 입증한다. 가장 대표적인 적용 사례는 플러그인 아키텍처이다. 이 아키텍처에서는 코어 시스템이 변경되지 않은 상태로, 새로운 플러그인 모듈을 추가함으로써 기능을 확장한다. 예를 들어, 텍스트 편집기에 문법 검사 기능이나 코드 하이라이터 기능을 추가할 때, 편집기의 주요 소스 코드를 수정하지 않고도 새로운 플러그인을 설치하기만 하면 된다. 이는 코어 시스템이 추상화된 인터페이스에 의존하고, 구체적인 기능은 해당 인터페이스를 구현한 플러그인에 위임하기 때문에 가능하다.

또 다른 주요 적용 분야는 결제 시스템의 모듈화이다. 초기 시스템이 신용카드 결제만 지원한다고 가정할 때, OCP를 준수한 설계에서는 새로운 결제 수단(예: 모바일 페이, 가상계좌, 암호화폐)을 추가해야 하는 요구사항이 발생해도 기존 결제 처리 로직을 수정하지 않는다. 대신, 공통의 PaymentProcessor 인터페이스를 정의하고, 각 결제 수단은 이 인터페이스를 구현한 독립적인 클래스로 만든다. 새로운 결제 방식이 생기면 해당 인터페이스를 구현하는 새 클래스만 추가하면 시스템은 기존 코드의 변경 없이 새로운 기능을 통합한다.

적용 분야

핵심 메커니즘

OCP 준수 방식

플러그인 아키텍처

확장 포인트(인터페이스) 제공

코어 시스템은 수정에 닫히고, 새 플러그인 추가로 확장에 열림

결제 시스템

공통 결제 인터페이스 정의

새로운 결제 수단 클래스를 추가하여 기능 확장, 기존 결제 로직은 변경하지 않음

보고서 생성기

다양한 출력 형식(HTML, PDF, CSV) 지원

ReportExporter 인터페이스를 통해 각 형식별 구현체를 독립적으로 추가

GUI 프레임워크에서의 위젯이나 컨트롤 라이브러리 개발에도 OCP가 광범위하게 적용된다. 프레임워크는 기본 버튼, 텍스트 상자 등의 컴포넌트를 제공하지만, 사용자가 커스텀 스타일이나 동작을 가진 새로운 컴포넌트를 만들고자 할 때, 프레임워크의 기존 코드를 변경할 필요가 없다. 사용자는 기본 컴포넌트 클래스를 상속하거나 정해진 인터페이스를 구현함으로써 자신만의 컴포넌트를 만들어 시스템에 추가할 수 있다. 이처럼 OCP는 소프트웨어를 보다 유연하고 미래의 변화에 대응하기 쉬운 구조로 만드는 실용적인 설계 지침으로 작동한다.

6.1. 플러그인 아키텍처

플러그인 아키텍처는 OCP를 구현하는 대표적인 사례이다. 이 아키텍처는 핵심 애플리케이션을 변경하지 않고도 새로운 기능을 플러그인 형태로 추가할 수 있도록 설계한다. 핵심 시스템은 안정적인 인터페이스나 추상 클래스를 정의하고, 구체적인 기능 확장은 외부 모듈인 플러그인이 담당한다. 결과적으로 기존 코드는 수정에 닫혀 있고, 새로운 플러그인을 추가함으로써 시스템은 확장에 열려 있게 된다.

이 방식의 구체적인 구현은 보통 인터페이스나 추상 클래스를 통해 이루어진다. 예를 들어, 텍스트 편집기 소프트웨어는 문서 변환기라는 인터페이스를 정의할 수 있다. 핵심 애플리케이션은 이 인터페이스를 통해 플러그인을 호출하지만, PDF 변환 플러그인이나 HTML 변환 플러그인과 같은 구체적인 구현에 대해서는 알지 못한다. 새로운 파일 형식에 대한 지원이 필요할 때는 기존 편집기 코드를 수정하는 대신, 해당 인터페이스를 준수하는 새로운 플러그인 모듈만 개발하여 추가하면 된다.

플러그인 아키텍처 구성 요소

역할

OCP와의 연관성

코어 시스템/호스트 애플리케이션

확장점(인터페이스)을 제공하고 플러그인을 로드 및 실행한다.

수정에 닫혀 있음: 새로운 기능 추가를 위해 코어를 변경할 필요가 없다.

확장점(인터페이스/추상 클래스)

플러그인이 구현해야 할 계약을 정의한다.

확장에 열려 있음: 이 계약을 준수하는 무한한 플러그인을 추가할 수 있다.

플러그인(구현체)

확장점을 구현하여 구체적인 기능을 제공한다.

시스템의 주요 확장 메커니즘이다.

이러한 아키텍처는 이클립스 IDE, 크롬 브라우저, 다양한 미디어 플레이어 등에서 널리 사용된다. 각 애플리케이션은 코어 기능을 유지한 채로 서드파티 개발자나 사용자가 직접 기능을 확장할 수 있는 생태계를 조성한다. 따라서 플러그인 아키텍처는 OCP의 실용적 가치를 잘 보여주며, 소프트웨어의 수명과 적응성을 크게 향상시키는 전략이다.

6.2. 결제 시스템 모듈화

결제 시스템에서 OCP를 적용하면 새로운 결제 수단을 추가하거나 기존 결제 로직을 변경할 때 시스템의 핵심 코드를 수정하지 않고도 확장할 수 있다. 일반적으로 인터페이스를 정의하여 다양한 결제 방식(예: 신용카드, 계좌이체, 모바일 결제)이 이 인터페이스를 구현하도록 설계한다.

구체적인 구현은 아래 표와 같다. PaymentProcessor라는 공통 인터페이스를 두고, 각 결제 방식은 이를 구현하는 독립적인 클래스로 만든다.

결제 수단

구현 클래스

주요 기능

신용카드

CreditCardProcessor

카드 번호 검증, 승인 요청

계좌이체

BankTransferProcessor

은행 API 호출, 이체 확인

모바일 결제

MobilePaymentProcessor

앱 심사, 토큰 기반 결제

새로운 결제 수단

NewPaymentProcessor

기존 코드 변경 없이 추가 가능

이 구조에서 가맹점에 새로운 결제 서비스(예: 간편결제나 암호화폐)를 도입해야 할 때, 기존의 CreditCardProcessor나 BankTransferProcessor 등의 코드는 전혀 건드리지 않는다. 대신 새로운 CryptoPaymentProcessor 클래스를 작성하여 PaymentProcessor 인터페이스를 구현하기만 하면 된다. 결제를 실행하는 주문 처리 모듈은 구체적인 클래스가 아닌 인터페이스에 의존하기 때문에, 새로운 클래스가 추가되어도 정상적으로 동작한다.

이러한 모듈화는 시스템의 안정성을 높인다. 특정 결제 채널의 API가 변경되거나 장애가 발생했을 때, 해당 모듈만 수정하거나 교체하면 되므로 영향 범위가 최소화된다. 또한 각 결제 방식의 복잡한 비즈니스 로직(예: 수수료 계산, 로깅, 재시도 메커니즘)이 하나의 모듈 내에 캡슐화되어 유지보수가 용이해진다.

7. 다른 SOLID 원칙과의 관계

OCP는 SOLID 원칙의 두 번째 원칙으로, 다른 네 가지 원칙과 밀접하게 연결되어 있다. 특히 단일 책임 원칙(SRP)과 리스코프 치환 원칙(LSP)은 OCP를 효과적으로 구현하기 위한 기반을 제공한다.

단일 책임 원칙은 하나의 클래스나 모듈이 변경되어야 하는 이유가 하나만 있어야 한다고 명시한다. 이 원칙을 준수하면 각 모듈의 책임이 명확해지고, 변경이 발생할 수 있는 지점이 고립된다. 결과적으로 새로운 기능을 추가할 때 기존 코드를 수정하지 않고도, 변경 가능성이 높은 특정 모듈만 확장하면 된다. 이는 OCP의 '수정에 닫혀 있음'을 실현하는 데 필수적이다. 책임이 분리되지 않은 모듈은 새로운 요구사항이 생길 때마다 내부를 수정해야 할 가능성이 높아지기 때문이다.

리스코프 치환 원칙은 하위 타입 객체는 상위 타입 객체를 대체할 수 있어야 한다는 원칙이다. 이 원칙은 다형성을 통한 확장의 안정성을 보장한다. OCP는 주로 추상화(인터페이스나 추상 클래스)에 의존하고, 구체적인 동작은 이를 구현한 클래스에 위임하는 방식으로 구현된다. 이때, 새로 추가된 하위 클래스가 상위 추상화를 정확히 준수하지 않으면, 기존 시스템이 오작동할 수 있다. 따라서 LSP는 OCP가 의존하는 다형적 확장의 신뢰성을 뒷받침하여, 시스템의 안정성을 유지하면서도 '확장에 열려 있음'을 가능하게 한다.

관련 원칙

OCP와의 관계

기여하는 측면

단일 책임 원칙(SRP)

OCP의 전제 조건

변경 지점을 고립시켜, 확장 시 수정 범위를 최소화함

리스코프 치환 원칙(LSP)

OCP의 구현 보장

다형적 확장의 정합성을 보장하여 시스템 안정성을 유지함

의존관계 역전 원칙(DIP)

OCP의 실현 수단

추상화에 의존함으로써 구체적인 변경으로부터 모듈을 보호함

인터페이스 분리 원칙(ISP)

OCP의 세분화

클라이언트별로 세분화된 인터페이스를 제공하여 불필요한 의존성을 제거함

의존관계 역전 원칙(DIP)과 인터페이스 분리 원칙(ISP) 또한 OCP 지원에 기여한다. DIP는 고수준 모듈이 저수준 모듈에 의존하지 않고 추상화에 의존하도록 하여, 저수준 모듈의 변경이나 확장이 고수준 모듈에 영향을 미치지 않게 한다. ISP는 너무 범용적인 인터페이스를 여러 개의 구체적인 인터페이스로 분리함으로써, 클라이언트가 자신이 사용하지 않는 메서드에 의존하게 되어 발생하는 불필요한 재컴파일 및 재배포를 방지한다. 이는 궁극적으로 OCP의 목표인 기존 코드 수정을 최소화하는 데 도움을 준다.

7.1. 단일 책임 원칙(SRP)

단일 책임 원칙(SRP)은 로버트 C. 마틴이 명명한 SOLID 원칙 중 첫 번째 원칙이다. 이 원칙은 "하나의 모듈은 변경의 이유가 하나, 단 하나만 가져야 한다"는 것으로 요약된다[3]. 여기서 '모듈'은 일반적으로 클래스를 의미하며, '변경의 이유'는 해당 클래스가 담당하는 하나의 명확한 책임이나 역할을 가리킨다.

SRP는 OCP의 실현을 위한 필수적인 토대를 제공한다. OCP가 시스템을 확장에는 열려 있도록 설계하는 원칙이라면, SRP는 그 확장의 단위를 명확히 정의하는 역할을 한다. 하나의 클래스가 여러 책임을 지니고 있다면, 그 중 하나의 책임만 변경되어도 클래스 전체가 수정되어야 하므로, 이는 OCP의 '수정에 닫혀 있음'을 위반하게 된다. 반면, 각 클래스가 단일 책임을 가질 때, 새로운 기능 추가는 기존 클래스를 수정하는 대신 새로운 클래스를 생성하는 방식으로 이루어질 수 있어 OCP를 준수하기 쉬워진다.

예를 들어, 사용자 데이터를 처리하는 User 클래스가 데이터베이스 저장 로직과 사용자 정보 검증 로직을 모두 포함한다면, 이 클래스는 저장 방식 변경이나 검증 규칙 변경이라는 두 가지 서로 다른 이유로 수정될 수 있다. SRP에 따라 이 클래스를 User(데이터 구조), UserValidator(검증), UserRepository(저장)로 분리하면, 각 클래스는 하나의 변경 이유만 가지게 되며, 이는 더 안정적이고 확장 가능한 설계로 이어진다.

원칙

핵심 개념

OCP와의 관계

단일 책임 원칙 (SRP)

한 클래스는 하나의 책임만 가져야 한다.

OCP 준수의 전제 조건. 책임이 분리되어야 확장을 위한 변경이 국소화된다.

개방-폐쇄 원칙 (OCP)

확장에는 열려 있고, 수정에는 닫혀 있어야 한다.

SRP로 잘 분리된 모듈을 기반으로 새로운 기능을 추가할 때 적용된다.

따라서 SRP는 OCP를 비롯한 다른 SOLID 원칙을 효과적으로 적용하기 위한 기초가 된다. 잘 정의된 단일 책임은 코드를 더 이해하기 쉽게 만들고, 변경의 파급 효과를 최소화하여 시스템의 전반적인 유연성과 견고성을 높인다.

7.2. 리스코프 치환 원칙(LSP)

리스코프 치환 원칙은 바바라 리스코프가 1987년에 제안한 원칙으로, 객체 지향 프로그래밍에서 하위 타입의 관계를 정의하는 핵심 규칙이다. 이 원칙은 "S가 T의 하위 타입이라면, 프로그램에서 T 타입의 객체를 S 타입의 객체로 치환해도 프로그램의 정확성에 아무런 영향을 미치지 않아야 한다"는 내용을 담고 있다[4]. 간단히 말해, 부모 클래스가 사용되는 모든 곳에서 자식 클래스를 문제없이 대체할 수 있어야 함을 의미한다.

이 원칙을 준수하기 위해서는 하위 클래스가 상위 클래스의 계약을 반드시 지켜야 한다. 여기서 계약은 메서드의 이름, 매개변수 타입, 반환 값, 그리고 가장 중요한 사전 조건과 사후 조건, 불변 조건을 포함한다. 하위 클래스는 상위 클래스의 메서드를 더 엄격한 사전 조건을 요구하거나, 더 약한 사후 조건을 제공하는 방식으로 재정의해서는 안 된다. 또한 상위 클래스가 보장하는 불변 조건을 훼손해서도 안 된다.

OCP와 LSP는 밀접한 상호 의존 관계에 있다. OCP를 통해 추상화에 의존함으로써 시스템을 확장에 열리게 만들 수 있지만, 그 확장(새로운 하위 클래스 추가)이 LSP를 위반한다면 시스템의 정확성이 깨질 수 있다. 즉, LSP는 OCP가 성공적으로 지속되기 위한 필수 전제 조건 역할을 한다. LSP를 위반한 하위 클래스는 기존 코드를 수정하지 않고도 확장한다는 OCP의 본질을 훼손하게 된다.

LSP 위반 사례

문제점

해결 방향

하위 클래스가 상위 클래스 메서드의 예외를 추가로 발생시킴

상위 클래스를 사용하는 클라이언트 코드가 처리하지 않은 예외에 직면할 수 있음

하위 클래스의 동작 범위를 상위 클래스의 계약 내로 제한

하위 클래스가 상위 클래스 메서드의 반환 값을 null로 대체함

클라이언트 코드가 null 체크를 하지 않았다면 널 포인터 예외가 발생할 수 있음

반환 값의 의미나 유효성을 상위 클래스의 계약과 일치시킴

하위 클래스가 상위 클래스 메서드의 핵심 기능을 완전히 변경함(예: 정사각형 클래스가 직사각형의 setHeight 메서드를 재정의할 때 너비도 함께 변경)

클라이언트 코드가 기대하는 직사각형의 수학적 속성이 깨짐

상속 대신 구성을 사용하거나, 올바른 계층 구조 재설계

8. 관련 문서

  • 위키백과 - OCP (Open Closed Principle)

  • 위키백과 - Open–closed principle

  • Oracle Cloud Infrastructure - 공식 홈페이지

  • Microsoft Learn - 개방-폐쇄 원칙(OCP)

  • Refactoring Guru - Open/Closed Principle

  • IBM - What is the open-closed principle?

  • Naver D2 - 객체 지향 설계 원칙: SOLID

리비전 정보

버전r1
수정일2026.02.14 23:09
편집자unisquads
편집 요약AI 자동 생성