본문 바로가기
푸닥거리

아키텍처 패턴(Architecture Patterns), 디자인 패턴(Design Pattern)

by [김경민]™ ┌(  ̄∇ ̄)┘™ 2022. 7. 9.
728x90

 

 

 

[출처: NCS학습모듈 2001020105 SW아키텍처 설계 2001020106 SW아키텍처 이행](https://ncs.go.kr/unity/th03/ncsSearchMain.do)

 

 

 

# A. 아키텍처 패턴(Architecture Patterns)

(1) 아키텍처 패턴은 소프트웨어 시스템을 위한 검증된 구조적 도해(a pictorial, an illustrated)이다.

패턴은 서브 시스템과 그들의 역할을 사전 정의해 놓은 하나의 Set이다.

Client-server pattern의 시스템 구조에 의하면 두 개의 서브 시스템이 식별된다.

클라이언트(여러 인스턴스일 수 있음)와 서버(유일함)가 그것들이다.

클라이언트 시스템의 주요 역할은 사용자에게 UI를 보여주는 역할일 수 있으며, 서버의 역할은 여러 질의를 프로세싱하고, 결과를 클라이언트에 제공함에 있을 것이다.

 

(2) 패턴은 시스템들 사이의 관계를 정리하기 위한 규칙과 지침을 설명한다.

클라이언트와 서버 사이의 관계는 클라이언트가 질문을 하고 이를 서버가 응답한다는 것이다.

패턴은 경험이 많은 사람들에 의해 기록되어진다.

패턴은 이러한 경험한 사람들의 머리에 숨겨지고 남아있을 수 있는 지식을 명세화 하는 것이다.

이러한 명세화는 다른 사람에게 그 경험을 배울 수 있게 한다.

패턴은 한 사람에 의해 구성되지 않고, 많은 개발자들의 경험의 산물인 것이다.

이는 소프트웨어 개발에서 기존의 잘 검증된 솔루션을 선택하고, 좋은 디자인 사례를 전파할 수 있게 한다.

아키텍처 패턴은 스타일, 또는 표준 아키텍처라고 불리지만, 아키텍처 스타일은 패턴보다 덜 정제된 컨셉으로서 사용된다.

여러 패턴은 동일한 아키텍처 스타일에 속할 수 있다.

 

## 1. 패턴 사용의 효율성

어떠한 문제가 많은 개발자들에 의해 비슷한 방식으로 해결되고, 그 방법이 일반적으로 해당 문제를 해결하는 방식으로 받아들여질 때, 그 방식은 패턴이 되어 진다.

그래서 패턴은 반복 발생하고, 일반적 해결책이 많은 경험자들 사이에서 알려진 설계 문제를 다루고 있어서 패턴에 대한 문서가 있으며, 이는 잘 검증된 설계 문제의 해결책이 되는 것이다.

 

### (1) 패턴을 기록하여 해결책을 재사용함이 쉬워진다.

패턴은 일반적인 어휘와 설계 솔루션에 대한 이해를 제공한다.

각 패턴의 이름은 널리 퍼진 디자인 언어의 일부가 되어가고 있다.

패턴은 특별한 문제에 대한 해결책에 대한 기나긴 설명의 필요를 제거한다.

그러므로 패턴은 소프트웨어 아키텍처를 문서화하는 방법이며, 이는 아키텍처의 오리지널 비전이 수정되거나 확장되어질 때 또는 코드 수정 등이 이루어질 때 아키텍처의 운영 관리를 지원하기도 한다.

패턴은 정의된 속성을 가지고 소프트웨어의 구축을 지원한다.

예를 들면, 클라이언트-서버 어플리케이션을 설계할 때, 서버는 클라이언트에게 통신을 요청할 수 있게 하면 안된다.

 

### (2) 많은 패턴은 명시적으로 소프트웨어 시스템에 대한 비기능적 요건을 다루고 있다.

예를 들면, MVC(Model-View-Controller) pattern은 사용자 인터페이스의 가변성을 지원한다.

이로 인해 패턴은 보다 복잡한 시스템에서 마치 여러 빌딩블럭 같이 보여질 수 있다.

 

## 2. 국내에서 주로 많이 사용하는 다섯 가지의 아키텍처 패턴

### (1) Layer 패턴

가장 일반적으로 사용하는 아키텍처 패턴으로서 subtask 들을 그룹으로 묶어 사용 허가 관계를 표시하는 패턴이다.

모듈의 재사용성을 높여 유지보수성이나 이식성에 좋은 패턴이다.

 

### (2) Broker 패턴

외부에 분산된 컴포넌트를 호출하려고 할 때 클라이언트 request의 분석하여 서버 컴포넌트에 전달하고 그 결과 값을 전달하는 역할을 하는 패턴을 브로커 패턴이라고 한다.

보안이나, 안정성을 높일 수 있는 패턴이다.

 

### (3) MVC 패턴

모델, 뷰, 컨트롤 세 개의 컴포넌트로 어플리케이션을 구분한 패턴으로 사용자 인터페이스를 가지고 있는 많은 어플리케이션에 사용된다.

모델은 기능과 데이터를 가지고 있고 뷰는 사용자의 화면 표시를 지원한다.

컨트롤러는 이들과의 관계를 가지고 사용자 이벤트나 모델의 변화를 감지하여 모델과 뷰에 전달하는 역할을 한다.

뷰와 모델 사의 사이의 일관성을 갖게 하여 변경용이성, 기능 확장성을 지원한다.

 

### (4) State-Logic-Display(3-tier) 패턴

비즈니스 애플리케이션을 개발할 때 가장 일반적으로 사용되는 패턴으로 사용자 인터페이스(UI)와 비즈니스 로직, 데이터를 구분하여 변경 용이성이 좋다.

게임, 웹 어플리케이션 등 많은 분야에서 사용되고 있다.

 

### (5) Sense-Compute-Control 패턴

일정한 시간 별로 센서의 값을 읽어 들이는 Sense와 센서의 값을 계산하여 해야 할 행위를 정의한 compute, actuator에 해야 할 기능이나 행위를 전달하는 control로 모듈을 구분하는 패턴을 말한다.

임베디드 애플리케이션을 개발할 때 주로 사용되는 패턴이다.

 

아키텍처 패턴은 지속적으로 발표되고 있고 웹과 서적을 통해 공유하고 있다.

또한 대부분의 아키텍트나 설계와 구현을 오래한 엔지니어들은 자신들의 경험을 정리한 패턴들을 갖고 있다.

중요한 것은 이러한 경험들을 재사용할 수 있도록 체계적으로 정리하고 관리하는 것이다.

 

# B. 프로그래밍에서의 디자인 패턴

프로그래밍을 하다보면 반드시 라이브러리, 패키지, 모듈, 프레임워크 같은 것들을 사용한다.

이 프로그래밍을 도와주는 도구들을 자유자재로 활용하려면 도구의 정확한 용도와 장단점, 핵심적인 특성, 사용 시 주의사항 등을 제대로 파악하고 있어야 한다.

라이브러리가 없다면 모든 프로그래머들이 시행착오를 반복하면서 클래스나 함수, 메소드 등을 직접 만들어 프로그래밍해야 한다.

하지만 우리가 일상적으로 접하는 문제 중 상당 수는 다른 많은 이들이 접했던 문제이며, 효율적인 해결법이 나와있다.

디자인 패턴은 프로그램을 개발하는 과정에서 빈번하게 발생하는 디자인 상의 문제를 정리해서, 상황에 따라 간편하게 적용해서 쓸 수 있는 패턴 형태로 만든 것이다.

 

 

## 1. 디자인 패턴(Design Pattern)의 종류

### (1) 스트래티지 패턴(Strategy Pattern)

알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다.

알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

일반적으로 서브클래스를 만드는 방법을 대신하여 유연성을 극대화하기 위한 용도로 쓰인다.

예: QuarkBehavior & FlyBehavior

 

### (2) 옵저버 패턴(Observer Pattern)

한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일 대 다(one-to-many) 의존성을 정의한다.

주제(Subject) & 옵저버(Observer) Observable & Observer: Observable에 register, remove, notify가 있고, Observer에 update가 있다. (notify에서 update를 호출)

예: 신문 구독 서비스, 기상관측 시스템

 

### (3) 데코레이터 패턴(Decorator Pattern)

객체에 추가적인 요건을 동적으로 첨가한다.

데코레이터는 서브 클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

예: 스타벅스 커피

 

### (4) 팩토리 패턴(Factory Pattern)

 

(가) 팩토리 메서드 패턴(Factory method pattern)

객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하게 만든다.

클래스의 인스턴스를 만드는 일을 서브 클래스에 맡긴다.

제품을 생산하는 부분과 사용하는 부분을 분리시킬 수 있다.

 

(나) 추상 팩토리 패턴(Abstract Factory Pattern)

인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고 생성한다.

구상 클래스는 서브 클래스에 의해 만들어진다.

 

### (5) 싱글턴 패턴(Singleton Pattern)

해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록(전역 접근) 하기 위한 패턴이다.

 

### (6) 커맨드 패턴(Command Pattern)

요구사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러 가지 다른 요구사항을 집어넣을 수 있다.

또한, 요청 내역을 큐에 저장하거나 로그로 기록할 수도 있으며, 작업 취소기능도 지원 가능하다.

예: 리모콘

서블릿의 doGet(), doPost() 또는 스트럿츠의 Action() 메서드도 커맨드 패턴일 것이다.

 

### (7) 어댑터 패턴(Adapter Pattern)

한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.

어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.

 

### (8) 퍼사드 패턴(Facade Pattern)

어떤 서브 시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.

퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브 시스템을 더 쉽게 사용할 수 있다.

서브 시스템의 호출을 퍼사드에서 처리해 준다.(기본 명령 호출 정도)

일련의 클래스들에 대한 인터페이스를 단순화 시킨다.

각 패턴별 차이점은 다음과 같다.

- 데코레이터 패턴: 인터페이스는 바꾸지 않고 책임(기능)만 추가

- 어댑터 패턴: 한 인터페이스를 다른 인터페이스로 변환

- 퍼사드 패턴: 인터페이스를 간단하게 바꿈

 

### (9) 템플릿 메서드 패턴(Template Method Pattern)

메서드에서 알고리즘의 골격을 정의한다.

알고리즘의 여러 단계 중 일부는 서브 클래스에서 구현할 수 있다.

템플릿 메서드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브 클래스에서 특정 단계를 재정의 할 수 있다.

스트래티지 패턴과 다른 점은 템플릿 메서드 패턴은 알고리즘의 개요를 정의하며 실제 작업 중 일부는 서브 클래스에서 처리한다.

스트래티지 패턴은 객체 구성을 통해서 알고리즘을 캡슐화하고 구현한다.

 

### (10) 이터레이터 패턴(Iterator Pattern)

컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공한다.

컬렉션의 구현을 드러내지 않으면서 컬렉션에 있는 모든 객체들에 대해 반복 작업할 수 있다.

 

### (11) 컴포지트 패턴(Composite Pattern)

객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층 구조로 만들 수 있다.

이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체(composite)를 똑같은 방법으로 다룰 수 있다.

클라이언트에서 객체 컬렉션과 개별 객체를 똑같은 식으로 처리할 수 있다.

예: 트리 구조의 패턴, 디렉토리 구조

XMLObject 객체가 컴포지트 패턴을 구현

 

### (12) 스테이트 패턴(State Pattern)

객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.

상태 전환의 흐름을 결정하는 코드를 상태 객체 또는 Context 객체 중 어느 쪽에 집어 넣을지 잘 고려해야 한다.

각 상태를 클래스로 캡슐화하여 나중에 변경시켜야 하는 내용을 국지화 시킬 수 있다.

- 스트래티지 패턴: 어떤 클래스의 인스턴스를 만들고 그 인스턴스에게 어떤 행동을 구현하는 전략 객체를 제공

- 스테이트 패턴: 컨텍스트 객체를 생성할 때 초기 상태를 지정해 주는 경우, 이후로는 컨텍스트 객체가 알아서 상태를 변경

 

### (13) 프록시 패턴(Proxy Pattern)

어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴이다.

다른 객체를 대변하는 객체를 만들어서 주 객체에 대한 접근을 제어할 수 있다.

(가) 원격프록시(remote proxy): 원격 객체에 대한 접근 제어 클라이언트와 원격 객체 사이에서 데이터 전달을 관리한다.

(나) 가상프록시(virtual proxy): 인스턴스를 만드는 데 많은 비용이 들어 생성하기 힘든 자원에 대한 접근을 제어한다.

(다) 보호프록시(protection proxy): 접근 권한이 필요한 자원에 대한 접근 제어, 호출하는 쪽의 권한에 따라서 객체에 있는 메소드에 대한 접근을 제어한다.

(라) 방화벽 프록시: 일련의 네트워크 자원에 대한 접근을 제어한다.

(마) 스마트 레퍼런스 프록시: 주 객체가 참조될 때마다 객체에 대한 레퍼런스 개수를 세는 등 추가 행동을 제공한다.

(바) 캐싱 프록시: 비용이 많이 드는 작업의 결과를 임시로 저장, 웹 서버 프록시 또는 컨텐츠 관리 및 퍼블리싱 시스템 등에서 사용한다.

(사) 동기화 프록시: 여러 스레드에서 주 객체에 접근하는 경우, 분산 환경 등에서 안전하게 작업을 처리할 목적으로 사용한다.

(아) 복잡도 숨김 프록시: 복잡한 클래스들의 집합에 대한 접근을 제어하고 복잡도를 숨겨주며 퍼사드 프록시라고도 한다.

프록시에서는 접근을 제어하지만 퍼사드 패턴에서는 대체 인터페이스만 제공한다.

(자) 지연 복사 프록시: 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시켜 객체의 복사를 제어한다.

 

## 2. 패턴 사용의 주의점

(1) 디자인 패턴의 과다한 사용은 불필요하게 복잡한 코드를 초래할 수 있다.

항상 가장 간단한 해결책으로 목적을 달성할 수 있도록 하고, 반드시 필요할 때만 디자인 패턴을 적용해야 한다.

 

(2) 코딩할 때 어떤 패턴을 사용하고 있는지 주석으로 적어준다.

클래스와 메서드 이름을 만들 때도 사용 중인 패턴이 분명하게 드러날 수 있도록 해야 한다.

다른 개발자들이 그 코드를 볼 때 무엇을 어떻게 구현했는지 훨씬 빠르게 이해할 수 있다.

 

 

728x90

댓글