행위 패턴으로 설계하는 객체 협력과 책임 분배
행위 패턴의 책임 연쇄, 명령, 관찰자, 전략, 상태 패턴을 중심으로 객체 상호작용과 책임 분배 방식을 정리한다.
2026-08-14 · 최초 발행 2025-05-23
객체 사이의 책임 흐름을 설계하는 패턴
행위 패턴은 객체지향 설계에서 객체 간 상호작용, 통신 방식, 알고리즘 구현, 책임 할당을 다룬다. 복잡한 제어 흐름을 관리하면서도 유연성과 확장성을 확보하고, 객체 사이의 결합도는 낮추며 응집도는 높이는 데 목적이 있다.
핵심은 어떤 객체가 무엇을 처리하는지뿐 아니라, 요청과 상태 변화가 객체들 사이를 어떤 방식으로 이동하는지 정하는 데 있다.
요청을 전달하거나 작업으로 캡슐화할 때
Chain of Responsibility: 처리자를 연결하는 책임 연쇄
책임 연쇄는 하나의 요청을 처리할 가능성이 있는 여러 객체에 순서대로 전달하는 방식이다. 요청을 보낸 쪽은 최종 처리자가 누구인지 알 필요가 없다.
로그 레벨에 따른 처리, 웹 애플리케이션 필터 체인, 권한별 결재 프로세스에 적용할 수 있다. 발신자와 수신자의 결합도를 낮추고 처리 책임을 동적으로 배정할 수 있으며, 핸들러 추가도 쉽다. 다만 요청이 반드시 처리된다는 보장은 없고, 흐름을 디버깅하거나 추적하기 어려울 수 있다.
Command: 요청 자체를 객체로 다루기
Command는 요청을 객체로 캡슐화해 매개변수화, 큐잉, 로깅 같은 처리를 가능하게 한다.
텍스트 에디터의 작업 실행과 실행 취소, 원격 작업 호출, 트랜잭션 처리에 활용된다. 작업을 요청하는 객체와 실제 수행하는 객체를 분리할 수 있고, 명령 객체를 조합해 복합 명령을 만들기도 쉽다. 실행 취소와 재실행 기능도 구현하기 좋지만, 명령 종류가 늘수록 클래스 수가 많아질 수 있다.
표현식과 컬렉션을 다루는 방식
Interpreter: 문법을 객체 구조로 표현하기
Interpreter는 특정 언어의 문법 또는 표현식을 평가하는 방법을 정의한다.
SQL 파서, 정규 표현식 엔진, 수학 표현식 계산기가 사용 사례다. 문법 규칙을 클래스로 나타내고 확장하기는 쉽지만, 문법이 복잡해지면 클래스 계층도 복잡해지고 성능 이슈가 생길 수 있다.
Iterator: 내부 구조를 숨긴 순회
Iterator는 컬렉션의 구현 세부 사항을 노출하지 않고 요소를 순차적으로 탐색하는 방법을 제공한다.
자바 Collection 프레임워크, 데이터베이스 결과셋 순회, UI 컴포넌트 탐색에서 볼 수 있다. 컬렉션 구현과 순회 로직을 분리하고 여러 순회 방식을 지원할 수 있으며, 클라이언트 코드도 단순해진다. 반대로 단순한 컬렉션에는 과도한 설계가 될 수 있다.
직접 연결을 줄이는 상호작용 모델
Mediator: 상호작용을 한곳에서 조정하기
Mediator는 객체 사이의 복잡한 협력을 캡슐화하고 중앙에서 관리한다.
항공 교통 관제, 채팅 애플리케이션, GUI 컴포넌트 상호작용이 대표 사례다. 객체 간 직접 참조를 제거해 결합도를 낮추고, N:N 관계를 1:N 관계로 단순화할 수 있다. 대신 중재자에 제어 로직이 집중돼 복잡해질 수 있으며 단일 장애점이 될 가능성도 있다.
Observer: 상태 변화를 구독자에게 알리기
Observer는 한 객체의 상태 변화가 여러 종속 객체에 자동으로 전달되도록 일대다 의존 관계를 정의한다.
UI 이벤트 처리, 뉴스 발행-구독 시스템, MVC 아키텍처의 모델-뷰 관계에 적용할 수 있다. 느슨한 결합과 브로드캐스트 통신을 지원하며 개방-폐쇄 원칙에도 맞는다. 그러나 순환 참조, 과도한 업데이트, 업데이트 순서 제어의 어려움은 고려해야 한다.
상태와 알고리즘 변화의 경계를 나누기
State: 상태가 바뀌면 행위도 바뀌는 객체
State는 객체의 내부 상태에 따라 동작을 변경할 수 있게 한다.
문서 승인 워크플로우, 네트워크 연결 상태 관리, 게임 캐릭터 상태를 모델링할 때 적합하다. 상태별 동작을 각각 캡슐화하고 상태 전이 로직을 명확히 할 수 있으며, 새 상태도 추가하기 쉽다. 상태가 많아지면 클래스 수가 증가하고 전이 로직이 분산될 수 있다.
Strategy: 교체 가능한 알고리즘을 분리하기
Strategy는 알고리즘 군을 정의하고 각각을 캡슐화해 교체 가능하게 만든다.
정렬 알고리즘 선택, 결제 방법 처리, 데이터 압축 전략이 사용 사례다. 알고리즘을 바꾸기 쉽고 재사용성을 높이며, 조건문을 줄여 코드 가독성을 높일 수 있다. 다만 전략이 많으면 클래스가 늘어나고, 클라이언트가 적절한 전략을 선택해야 한다.
Template Method: 고정된 절차 안의 변경 지점
Template Method는 알고리즘의 전체 구조를 정의하고 일부 단계를 서브클래스가 구현하도록 한다.
프레임워크의 기본 알고리즘 구조, 데이터베이스 연결 및 쿼리 처리, 문서 생성 프로세스에 적용할 수 있다. 공통 로직을 중앙화하고 재사용성을 높이며 확장 지점을 분명하게 만든다. 그러나 템플릿 구조 자체를 바꾸기 어렵고 상속이 가진 제한을 따른다.
Memento: 내부 상태를 보존하고 되돌리기
Memento는 객체의 내부 상태를 저장해 이전 상태로 복원할 수 있게 한다.
텍스트 에디터의 실행 취소, 게임 저장 상태, 트랜잭션 롤백에 활용된다. 캡슐화를 깨뜨리지 않고 상태를 저장·복원할 수 있고, 상태 이력도 관리하기 쉽다. 다만 상태가 크면 메모리 사용량이 늘고 성능 이슈가 생길 수 있다.
객체 구조에 연산을 추가하는 Visitor
Visitor는 객체 구조를 구성하는 요소와 그 요소에 적용하는 연산을 분리한다. 객체 구조를 바꾸지 않고 새로운 연산을 추가하기 위한 패턴이다.
컴파일러의 AST 처리, 복합 객체 구조의 다양한 연산 수행, 문서 요소별 처리에서 사용할 수 있다. 관련 연산을 하나의 클래스에 모으고 객체 구조와 연산을 분리할 수 있어 새 연산 추가에 유리하다. 반면 새 요소를 추가하기 어렵고, 구현이 복잡해지며 객체 내부 상태 노출이 필요할 수 있다.
도메인별로 보는 패턴 배치
금융 시스템에서는 Observer로 주식 시세 정보 변경을 다양한 모니터링 대시보드에 실시간 반영할 수 있다. Chain of Responsibility는 대출 승인 과정의 금액별 승인 권한 체계를 구현하는 데 쓰이며, State는 거래 처리 상태인 대기, 처리중, 완료, 실패를 관리하는 데 맞는다.
전자상거래 시스템에서는 Strategy로 신용카드, 계좌이체, 포인트 등의 결제 방식을 분리할 수 있다. Command는 주문 처리 로직을 캡슐화해 비동기 처리와 로깅을 지원하고, Mediator는 장바구니·재고 관리·결제 시스템 사이의 상호작용을 조정한다.
게임에서는 State로 캐릭터의 대기, 걷기, 달리기, 공격 상태를 다룰 수 있다. Visitor는 게임 오브젝트별 충돌 처리 로직을 분리하고, Memento는 세이브와 로드 기능을 구현하는 데 활용된다.
문제의 변화 지점에서 패턴을 고르기
객체 사이의 상호작용이 복잡하고 중앙 제어가 필요하다면 Mediator를, 한 객체의 변화를 여러 객체에 알려야 한다면 Observer를 검토할 수 있다.
알고리즘을 런타임에 바꿔야 한다면 Strategy가 맞고, 전체 알고리즘 구조는 유지한 채 단계만 달라진다면 Template Method가 적합하다. 상태에 따라 동작이 크게 달라지는 객체에는 State를, 상태 저장과 복원이 요구되는 경우에는 Memento를 선택한다.
요청을 저장·실행·취소할 필요가 있으면 Command를, 여러 핸들러가 요청을 처리해야 하면 Chain of Responsibility를 고려한다.
패턴을 적용할 때 남겨야 할 경계
인터페이스는 지나치게 세부적이거나 과도하게 일반적이지 않도록 설계하고, 확장 가능성을 함께 고려해야 한다. 패턴을 조합하는 경우 Strategy와 Factory Method는 전략 객체 생성을 자동화하는 데 사용할 수 있으며, Observer와 Mediator는 복잡한 일대다 관계를 관리하는 데 조합할 수 있다. Command와 Memento는 실행 취소 기능 구현에 함께 쓰일 수 있다.
각 패턴 요소는 명확한 책임을 갖도록 하고, 확장에는 열려 있으면서 수정에는 닫힌 설계를 지향한다. 또한 추상화에 의존해 유연성을 확보한다. 단순한 문제에 불필요한 패턴을 겹치면 복잡성만 늘어나므로, 필요한 범위에서만 적용해야 한다.