디자인 패턴은 본래 고쳐 쓰는 것이다¶
HJ, Ph.D. / Software Architect (js.seth.h@gmail.com) 초안: 2022년 04월 / 개정: 2025년 12월
Design Patterns: Elements of Reusable Object Oriented Software의 서문에서...
One thing expert designers know not to do is to solve every problem from first principles. Rather, they reuse solutions that have worked for them in the past. When they find a good solution, they use it again and again. Such experience is part of what makes them experts. Consequently, you'll find recurring patterns of classes and communicating objects in many object-oriented systems. These patterns solve specific design problems and make object-oriented designs more flexible, elegant, and ultimately reusable. They help designers reuse successful designs by basing new designs on prior experience. A designer who is familiar with such patterns can apply them immediately to design problems without having to rediscover them.
GoF는 디자인 패턴을 ‘발명’한 것이 아니라, 실제 성공 사례들에서 반복적으로 드러난 구조적 해결책을 발견하여 정리한 것이다. 다시 말해 패턴은 처음부터 ‘정답’으로 고정된 것이 아니라, 언제나 상황에 맞게 변형될 것을 전제로 한다.
즉, 디자인 패턴은 처음부터 외워서 그대로 사용하는 게 아니라, 기초로 삼아 고쳐 쓰기 위한 것이다.
Abstract¶
- 디자인 패턴은 정답이 아니며, 그대로 적용되는 답안이 아니다.
- 패턴은 일정한 클래스와 그 통신 구조가 해법으로써 반복 사용되는 것을 정리한 것이다.
- 패턴의 확장성에 집중하지만, 변경가능한 것은 아무것도 결정하지 않는다.
- 진짜는 고정된 ‘역할과 협력 구조’가 본질이며, 각 패턴에 녹아든 정보 처리의 골격이다.
- 처리의 골격은 정보 처리 흐름을 제한하는 장치로서, 교체가능한 부분이 존재하기 위해 필수불가결한 부분이다.
- 궁극적 패턴의 활용은 골격을 기반으로 상황에 맞게 변형하는 것이다.
- 변형하기 위해서는 변하지 않는 협력 구조를 이해해야 한다.
- 대부분은 본질 - 역할 배분과 협력 구조를 제대로 이해하지 못했다.
- 고정이 발휘하는 강제력을 파악해라.
- 우선은 적재 적소에 디자인 패턴을 적용해라
- 그리고, 각 프로젝트는 서로 다른 균형점을 갖기 때문에, 디자인 패턴도 ‘그대로’가 아닌 ‘고쳐 쓸’ 줄 아는 능력이 더 유리하다.
정답인가?¶
소프트웨어 개발에서 디자인 패턴은 오랜 시간 동안 축적된 경험과 지혜의 산물로 여겨진다. 많은 개발자들이 패턴을 배우고, 프로젝트에 적용하며, 이를 통해 더 나은 구조와 유지보수성을 얻고자 한다. 하지만 패턴을 처음 접하는 이들은 종종 패턴을 ‘정답’으로 오해한다. 마치 교과서에 나온 예시를 그대로 따라야만 올바른 설계가 되는 것처럼 느끼기도 한다.
하지만 패턴은 과거의 경험으로부터 공통점을 발견한 것이다.
실제로 디자인 패턴의 본질은 ‘정형화된 해법’이 아니다. 패턴은 반복적으로 등장하는 문제에 대한 해결책의 취합으로, 간략화된 모형으로써 일반적인 해결책을 제시할 뿐이다. 처음부터 모든 상황에 완벽하게 들어맞는 만능 열쇠가 아니다. 각 프로젝트마다 요구사항과 제약, 팀의 역량, 기술 스택 등이 다르기 때문에, 패턴을 그대로 적용하는 것이 오히려 문제를 야기할 수 있다.
예를 들어, 싱글턴 패턴은 객체의 단일성을 보장하는 데 유용하지만, 멀티스레드 환경이나 테스트 코드에서는 예상치 못한 부작용을 일으킬 수 있다. 이런 경우, 패턴을 변형하거나 일부만 적용하는 것이 더 현명한 선택이 된다. 즉, 패턴은 ‘고쳐 쓰는 것’이 본래 목적이다.
물론 외우긴 해야지...
패턴의 본질은?¶
패턴의 본질을 논하기 전에 한 가지, 소프트웨어가 무엇인지를 짚고 넘어가야 한다.
소프트웨어는 튜링 머신 이래 ‘연산 흐름’을 본질로 갖는다. 이후 어셈블리, 명령형 언어, 객체지향 등 많은 개념적 발전을 이루었으나, 여전히 연산 절차를 코딩하고 있다는 점은 같다. 디자인 패턴은 많은 프로젝트에서 반복되는 처리 흐름에서 반복되는 문제에 대한 구조적 해결책을 간단한 모형으로 정리한 것이며, GOF의 분류 기준이 보여주듯이 그 대부분의 문제는 소프트웨어를 변화하는 요소에 적응시키려는 요구에 기인한다.
구조적 해결책 - 구조적이란...
구조적이란 좀 더 거시적 시각에서, 역할과 지위, 자원 배분, 관례와 규칙, 관계를 아울러 발생하는 힘을 의미한다.
사실 이 단어만 가지고 별개의 문서로 정리해야 한다...
그러나 변경 가능한 부분은 아무것도 결정하지 않는다. 무엇인가를 결정하는 것은 오히려 '고정되어 변하지 않는 부분'이다. 즉 디자인 패턴에서 눈여겨봐야 할 핵심은 늘 고정된 부분에 녹아 있다. 고정된 상속 구조, 함수 시그니처, 리턴 형식, 루프의 구조는 정보 처리의 흐름을 제한하여, 강력한 강제력을 행사한다. 동시에 이러한 강제력은 교체 가능한 부분 시스템에 수용되기 위해서 필수불가결한 부분이다.
반이 비어 있는 물컵이 곧 반이 차 있는 물컵이다
또한, 디자인 패턴이 공통성의 관찰에서 발견되었다는 사실이, 모든 프로젝트는 저마다 색깔이 있어 조금씩 다른 응용을 하고 있었다는 점을 반증한다.
패턴은 처음부터 정답이었던 적이 없으며, 고정된 부분에 녹아 있는 '클래스와 그 통신 구조'가 핵심으로써, 그 구조를 재사용하는 것이 궁극적 목적 중 하나였다.
사실, 대부분의 개발자들은 각 패턴의 본질을 이해하지 못했다.¶
예를 하나 들어보자.
우선, 행위패턴에 속하는 전략(Strategy) 패턴을 생각해보자. 전략 패턴의 주요 설명은 '동작을 캡슐화하여, 런타임에 교체할 수 있도록 한다'는 점이다. 구체적인 전략(알고리즘)은 얼마든지 바뀔 수 있지만, 컨텍스트(Context)와 전략(Strategy) 인터페이스 간의 협력 구조 - 파라미터와 리턴 타입은 변하지 않는다. 이 약속의 구조가 바로 처리의 골격이다. 개발자가 이 골격만 유지한다면, 단순한 값을 넘기는 것이 아니라, 데이터베이스 접속과 같은 외부 시스템 접점을 넘기거나, 리턴은 성공/실패 여부만 처리하고, 스트래티지 패턴 내에서 넘겨받은 참조 객체를 통해 필요한 모든 결과를 세팅해 넣는 것도 가능하다. (일종의 Strategy-Builder 결합)
한편, 템플릿 메서드(Template Method) 패턴도 행위패턴에 속한다. 템플릿 메서드 패턴의 요점은 상위 클래스에서 로직의 전체 흐름을 템플릿이라는 이름으로 정의하고, 세부 기능은 하위 클래스에서 구현하도록 하는 것이다. 로직의 흐름은 변하지 않는다. 이때 이 흐름의 구체성이 매우 중요하게 작용하는데, 템플릿 메서드는 사실상 절차 1 ~ 절차 N 사이의 데이터의 형식(Type)과 전달 체계(절차 1의 결과가 절차 2의 입력 등)를 미리 정한다. 일반적 설명에서는 템플릿 메서드가 하위 클래스에서 동작을 자유롭게 변경할 수 있다고 소개된다.
하지만, 실제로는 상위 클래스가 정의하는 절차적 골격이 매우 강한 제약으로 작용한다. 입력·출력의 형식, 단계 간 데이터 전달 구조, 호출 순서 등이 강하게 고정되기 때문에, 오히려 전략 패턴과 비교는 물론, 절대적인 관점에서도 변형의 여지가 매우 좁다.
반면, 전략 패턴은 일반적으로 하나의 함수 시그니처만을 강제하므로 변형 가능성이 크다. 대신, 템플릿 메소드는 부모 클래스에서 공통 처리를 제공하고, 그에 의존적인 시스템을 구성하는 게 가능하지만, 전략 패턴에서는 부모가 공통 기능을 제공할 수는 있으나, 그 사용을 강제할 수는 없기 때문에 공통 요소가 반드시 사용되어야만 하는 시스템은 구성할 수 없다.
한편 전략 패턴에서 전략 루틴의 호출자인 Context를 주요하게 생각하는 경향이 있는데, 이는 Context가 구체적인 구현체를 변경함으로써 기능을 바꿀 수 있다고 여기기 때문이다. 그러나, 템플릿 메서드 또한 누군가가 호출을 할 것이며, Abstract Template Class가 스스로를 호출할 수는 없으니, 당연히 Context가 존재하며, 구체적 구현체의 변경 능력을 가지고 있다.
1994년 GoF의 Designe Patterns 이후, 많은 책이 나왔다. 그러나 템플릿 메소드가 실제로 Context를 요구한다는 점을 지적한 책을 아직 보지 못했다.
이 두 패턴의 고정요소와 변동요소를 정확히 파악하면, 생각보다 많은 점이 유사하면서, 차이점이 큰 것을 알 수 있다. 이는 고정된 부분에서 사소해 보이는 차이로 인해, 그 결정에 녹아든 강제력이 서로 다르게 작용하기 때문이다. 두 패턴은 서로 일정한 호환성이 있으면서도, 확연히 다른 강제력과 확장성을 보여준다.
고정이 발휘하는 강제력¶
프로그램의 원초적 형태는 결국 하드코딩된 계산이다. 일련의 고정된 명령의 절차적 집합이 그 본질이나 우리는 동일한 계산을 다른 자료에 적용하기 위해서 파라미터를 추가했다. 마찬가지로 다양한 변형과 분기를 구조적 해법을 적용해 자유를 부여해왔다. 숙련된 아키텍트가 비용과 타협하지 않아도 된다면, 원하는 만큼의 자유를 부여할 방법은 찾을 수 있다. 하지만 현실적 조율을 통해 자유가 없어도 되는 부분은 고정된 채, 자유로운 확장의 바탕을 이루게 된다. 즉 고정된 바탕에서 기인하는 강제력은 예측 가능성, 안정성, 신뢰성을 담보하는 구성품이 된다.
현실에서 개발하는 서비스는 확정되지 않은 미래 전략도 있으며, 향후의 플랜을 위해서 미리 변경의 여지를 준비해 두기도 한다. 이러한 미지수가 시스템 내에 수용 가능하도록 하기 위해서, 일정한 규칙을 정해두어야 한다. 즉, 확장성은 상호운용성을 위한 규칙 위에 성립하는 것이다. 즉 규칙 - 역할과 역할간 통신이라는 타협점을 필수적으로 설정해야 하며, 강제력은 수용 가능한 확장을 결정짓는다.
강제성이 많을수록 예측하기 쉽고, 버그가 적을 확률이 높으며, 빨리 개발할 확률이 높아진다. 구현의 안정성만 생각하면 최대한 단순한 하드 코딩이 가장 유리하겠으나, 그것이 시장 적응력이나 고객 만족을 달성할 수 있다고 볼 수는 없다. 따라서 항상 소프트웨어는 강제성과 변동성 사이에서 균형을 잡기 위한 타협이 필요하며, 모든 프로젝트는 저마다의 색깔이 있으므로 저마다 고유한 타협점을 정해야 한다.
디자인 패턴이든, 아키텍처든, API든, 어떤 요소를 결정할 때, 그로 인해 생기게 되는 강제력을 염두에 두어야 한다. 이러한 관점으로 대상을 볼 수 있다면, 각 디자인 패턴이 어떤 강제력을 가지는지 파악하고 응용할 수 있다.
결론¶
디자인 패턴은 흔히 발견되는 정보처리 흐름에서 어느 부분까지 고정하고, 그 경계에 어떤 약속을 집어 넣어, 어떤 강제력을 발휘하게 할 수 있는지에 대한 모범 사례를 보여준다. 다만, 각 패턴은 정말 사소한 차이에도 상당히 다른 강제력을 보여준다. 때문에 특정 패턴을 도입함에 있어, 자신이 무엇을 고정요소로 정하고 있으며, 어떤 약속이 생겼는지 정확히 이해할 필요가 있다. 그 이해를 바탕으로 패턴을 선택해야 소프트웨어의 안정성과 확장성을 동시에 확보하기 유리하다.
한편 각 프로젝트는 서로 다른 방향을 추구함으로 단순화된 모범사례인 디자인 패턴이 그대로 들어맞지 않을 수 있다. 또한 디자인 패턴은 모형을 제시하는 것으로 객체의 책임에 따라서는 하나의 클래스가 2~3가지 패턴이 동시에 적용된 상태가 적절할 수도 있다. 따라서 각 디자인 패턴을 정확히 이해하여 핵심 골격을 유지하면서도 객체 책임에 따라서 변형된 강제력을 행사하도록 개발하는 것이 훨씬 유리하다.
SeeAlso¶
- 소프트웨어 아키텍처는 의사결정이지, 모방이 아니다 - 디자인 패턴이든, 아키텍처든, API든, 모방이 아니라 파악하고 응용하는 것이다.
- 소프트웨어 아키텍처는 확장보다 불변에 의해 결정된다 - 변동성, 확장성은 고정된 불변 사항에 기인한다.
- 직관을 따라야 읽기 좋고, 읽기 좋아야 고치기 좋다 - 지식은 가독성에 영향을 미친다.
- 아키텍처는 구조적 해결책이다 - 구조적 해결책이란
Author¶
HJ, Ph.D. / Software Architect
(js.seth.h@gmail.com)
https://js-seth-h.github.io/website/Biography/
Over 20 years in software development, focusing on design reasoning and system structure - as foundations for long-term productivity and structural clarity.
Researched semantic web and meta-browser architecture in graduate studies,
with an emphasis on structural separation between data and presentation.
Ph.D. in Software, Korea University
M.S. in Computer Science Education, Korea University
B.S. in Computer Science Education, Korea University
저작자표시-비영리-변경금지(CC BY-NC-ND 4.0)