UML 다이어그램·디자인 패턴 선택 기준
문제에서 묻는 관계와 변화 축을 찾아 UML과 GoF 패턴을 선택하는 방법입니다.
UML 다이어그램 선택
클래스 다이어그램은 타입과 정적 관계, 시퀀스 다이어그램은 시간 순 메시지, 상태 다이어그램은 한 객체의 상태 변화, 활동 다이어그램은 업무·알고리즘의 제어 흐름을 표현합니다.
- 유스케이스는 외부 액터와 시스템 기능의 관계를 본다.
- 배치 다이어그램은 실행 노드와 산출물 배치를 나타낸다.
- 컴포넌트 다이어그램은 구현 구성요소와 의존성을 표현한다.
생성 패턴 5개: 무엇을 어떻게 만들 것인가
생성 패턴은 객체를 만드는 책임과 사용 책임을 분리합니다. 생성 메서드가 있다는 사실만으로 판단하지 말고, 제품 종류·제품군·조립 과정·복제·인스턴스 수 중 무엇을 제어하는지 확인합니다.
- Factory Method: 생성 인터페이스를 정하고 실제 제품 선택을 하위 클래스에 맡긴다.
- Abstract Factory: 구체 클래스를 지정하지 않고 함께 사용할 관련 제품군을 생성한다.
- Builder: 복잡한 객체의 구성 과정과 표현을 분리해 같은 절차로 다른 표현을 만들 수 있게 한다.
- Prototype: 기존 객체를 복제해 새 객체를 만든다. 내부 참조까지 복제할지는 구현에 따라 확인한다.
- Singleton: 인스턴스 하나와 그 접근점을 제공한다. 전역 상태와 테스트 격리의 어려움은 별도 비용이다.
구조 패턴 7개: 객체를 어떻게 연결할 것인가
구조 패턴은 클래스나 객체를 조합하는 방식을 다룹니다. 객체를 감싼다는 모양만 보고 답을 정하지 말고 연결의 목적을 구분합니다.
- Adapter: 기존 인터페이스를 호출 측이 기대하는 인터페이스로 변환한다.
- Bridge: 추상 계층과 구현 계층을 분리해 양쪽을 독립적으로 확장한다.
- Composite: 부분과 전체를 트리로 묶고 단일 객체와 복합 객체를 같은 방식으로 다룬다.
- Decorator: 같은 인터페이스를 유지하면서 객체에 책임을 동적으로 덧붙인다.
- Facade: 여러 하위 시스템을 사용하기 쉬운 통합 인터페이스로 제공한다.
- Flyweight: 공유 가능한 공통 상태를 나누어 많은 작은 객체의 메모리 비용을 줄인다.
- Proxy: 실제 객체를 대신해 접근·지연 생성·원격 호출 등을 제어한다.
행위 패턴 11개: 책임과 협력을 어떻게 나눌 것인가
행위 패턴은 알고리즘과 객체 사이의 책임 분담을 다룹니다. 다음 목록은 생성·구조 패턴과 구분할 때 필요한 목적을 한 번씩 정리한 것입니다.
- Strategy: 같은 목적의 알고리즘들을 캡슐화해 교체한다.
- State: 내부 상태에 따라 객체의 행동이 달라지도록 상태 객체에 위임한다.
- Observer: 한 객체의 변경을 등록된 여러 관찰자에게 알린다.
- Command: 요청을 객체로 만들어 대기열·기록·실행 취소 처리에 활용한다.
- Template Method: 상위 클래스가 알고리즘의 골격을 정하고 일부 단계를 하위 클래스에 맡긴다.
- Iterator: 내부 표현을 노출하지 않고 원소를 순회한다.
- Memento: 캡슐화를 지키며 내부 상태를 저장하고 복원한다.
- Visitor: 객체 구조를 바꾸지 않고 원소에 적용할 연산을 별도 객체에 모은다.
- Interpreter: 문법 규칙을 표현하고 그 언어의 문장을 해석한다.
- Mediator: 여러 객체의 직접 의존을 중재 객체로 모아 협력을 조정한다.
- Chain of Responsibility: 처리 후보를 연결하고 요청을 넘겨 적절한 처리자를 찾는다.
UML 관계: 선 모양과 끝점 읽기
같은 두 요소를 연결해도 선 종류와 끝점에 따라 의미가 달라집니다. 먼저 관계를 판단한 뒤, 화살표나 마름모가 어느 쪽에 놓이는지 확인합니다.
- 일반화: 실선과 빈 삼각형이 상위 타입을 향한다. 실체화: 점선과 빈 삼각형이 구현할 명세를 향한다.
- 연관: 객체 사이의 구조적 연결을 실선으로 나타낸다. 의존: 사용하는 쪽에서 사용되는 쪽으로 점선 화살표를 그린다.
- 집합: 전체 쪽에 빈 마름모를 둔다. 합성: 전체 쪽에 채운 마름모를 두며 부분은 동시에 둘 이상의 합성 전체에 속하지 않는다.
- include: 포함하는 유스케이스 → 포함되는 유스케이스. extend: 확장 유스케이스 → 기본 유스케이스. 두 관계 모두 점선과 열린 화살표를 사용한다.
- 액터는 사람만 뜻하지 않는다. 시스템 경계 밖에서 상호작용하는 다른 시스템도 액터 역할을 할 수 있다.
조건 하나로 달라지는 패턴 판단
패턴 이름이 아니라 무엇을 바꾸려는지로 답을 고릅니다. 겉으로 비슷한 코드도 아래 조건에 따라 다른 목적을 가집니다.
- 외부에서 정렬 알고리즘을 골라 끼우면 Strategy에 가깝다. 같은 요청의 반응이 객체의 내부 상태 변화에 따라 달라지면 State를 검토한다.
- 감싼 객체의 호출 규격을 맞추는지, 같은 규격으로 기능을 추가하는지, 접근 자체를 통제하는지에 따라 Adapter·Decorator·Proxy를 구분한다.
- 제품 생성 선택을 하위 클래스에 맡기는 것과 서로 호환되는 여러 제품을 함께 생성하는 것은 다르다. 후자에서는 단일 제품이 아니라 제품군의 일관성을 확인한다.
시험에 바로 쓰는 비교표
| 요구 | 적합한 표현·패턴 | 판단 키워드 | 혼동 대상 |
|---|---|---|---|
| 시간 순 메시지 | 시퀀스 다이어그램 | 객체 간 호출 순서 | 활동 다이어그램 |
| 인터페이스 변환 | Adapter | 기존 클래스를 새 규격에 맞춤 | Facade |
| 알고리즘 교체 | Strategy | 동일 목적의 여러 구현 | State |
| 상태 변화 통지 | Observer | 발행자와 구독자 | Mediator |
지문 표현을 판단 기준으로 바꾸기
- 호환되지 않는 인터페이스 변환 → Adapter
- 알고리즘군을 교체 가능하게 캡슐화 → Strategy
- 시간 순서에 따른 메시지 → 시퀀스 다이어그램
직접 풀어보는 예제와 단계별 해설
관계 이름을 외운 뒤 끝내지 않고, 조건과 호출 구조를 근거로 구별합니다. 아래 요구사항과 코드는 정처LAB이 만든 학습 예제이며 실제 출제 문제를 옮긴 것이 아닙니다.
include와 extend는 화살표의 출발점도 다르다
보고서 내보내기는 매번 접근 권한 확인을 포함합니다. 워터마크 추가는 내보내기의 확장 지점에서 조건에 따라 수행하며, 워터마크 없이도 내보내기는 완결됩니다. 사용자는 내보내기에 참여합니다. 세 관계를 구별하세요.
그림이 잘리면 좌우로 움직여 확인하세요.
| 조건 | 관계 | 표현 |
|---|---|---|
| 내보내기가 권한 확인 행동을 포함 | include | 내보내기 → 접근 권한 확인 (점선, «include») |
| 선택 조건에서 워터마크 행동을 삽입 | extend | 워터마크 추가 → 내보내기 (점선, «extend») |
| 사용자가 내보내기에 참여 | association | 사용자 — 내보내기 (실선) |
풀이 순서
- include 화살표는 포함하는 유스케이스에서 포함되는 유스케이스로 향합니다. 여기서는 내보내기가 접근 권한 확인을 사용하므로 권한 확인 쪽이 화살표의 끝입니다.
- extend는 확장 행동에서 기본 유스케이스로 향합니다. 워터마크가 없어도 기본 기능이 성립한다는 조건과 확장 지점이 근거입니다. 단순히 실행 순서가 뒤라는 이유로 extend가 되지는 않습니다.
- 액터와 유스케이스의 참여 관계는 연관으로 나타냅니다. 유스케이스 사이의 include·extend와 구별하며, 이 예제의 실선은 데이터 흐름 방향을 뜻하지 않습니다.
틀리기 쉬운 지점: 선택 기능이라는 단어 하나만 보지 말고 기본 유스케이스의 독립성과 확장 지점을 함께 확인하세요. include와 extend를 모두 기본 기능에서 바깥으로 향하게 그리면 방향이 틀립니다.
호출 이름과 단위가 다른 객체 연결하기
기존 센서는 섭씨의 100배 값을 read_centi()로 반환합니다. 변경할 수 없는 호출 측은 read()에서 섭씨 실수를 받습니다. 아래 연결 객체의 역할과 출력값을 구하세요.
Python 3
class Sensor:
def read_centi(self):
return 2350
class TemperatureAdapter:
def __init__(self, sensor):
self.sensor = sensor
def read(self):
return self.sensor.read_centi() / 100
reader = TemperatureAdapter(Sensor())
print(reader.read())
print(reader.read() >= 23)| 단계 | 호출 또는 연산 | 값 |
|---|---|---|
| 호출 측 | reader.read() | 섭씨 값을 기대 |
| 기존 센서 | read_centi() | 2350 |
| 어댑터 | 2350 / 100 | 23.5 |
| 호출 측 판단 | 23.5 >= 23 | True |
풀이 순서
- TemperatureAdapter는 기존 객체를 감싸고 호출 측이 기대하는 read()를 제공합니다. 메서드 이름만 연결하는 것이 아니라 단위도 바꾸므로 호출 측의 계약을 만족시킵니다.
- 이 구조의 핵심은 호환되지 않는 인터페이스의 연결인 Adapter입니다. 객체를 new 또는 생성자 호출로 만든다는 사실만으로 생성 패턴이 되는 것은 아닙니다. 여러 하위 기능을 단순화하는 Facade나 같은 인터페이스의 책임을 덧붙이는 Decorator와도 목적이 다릅니다.
확인 결과
23.5
True틀리기 쉬운 지점: 2350을 그대로 반환하면 메서드 이름은 맞아도 단위 계약을 어깁니다. 패턴 이름과 별개로 실제 출력과 호출 측이 받는 값까지 확인해야 합니다.
검증 방법과 참고 문서
UML 관계의 참여자·방향을 명세와 대조하고 어댑터 코드는 Python으로 실제 실행해 두 출력값을 검사합니다. 코드 실행 성공 자체가 UML 의미 검증을 대신하지는 않습니다.
예제·데이터·풀이표: 정처LAB 자체 설계 · AI 보조 작성 및 로컬 검증 · 예제 수정일 2026-09-15
자주 틀리는 판단
- 시퀀스와 상태 다이어그램의 변화 축을 혼동
- Adapter와 Decorator를 모두 단순 기능 추가로 판단
- Factory Method와 Abstract Factory의 생성 범위를 구분하지 않음
개념을 확인했다면 실제 문제에서 판단 기준을 적용해보세요.
첫 관련 문제 풀기