행동 패턴: 전략, 옵서버, 커맨드
행동 패턴으로 동작과 통신을 모델링합니다.
행동 패턴: 전략, 옵서버, 커맨드은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
동작 모델링
행동 패턴은 객체가 협력하고 책임을 분배하는 방식을 다룹니다. 실제 PHP 시스템에 끊임없이 등장하는 다음 세 가지에 집중합니다.
- 전략 — 런타임에 알고리즘을 교체합니다.
- 관찰자 — 관심 있는 수신자에게 이벤트를 알립니다.
- 명령 — 요청을 대기열에 넣거나 로그 기록하거나 실행 취소할 수 있는 객체로 바꿉니다.
전략: 개념
전략 패턴은 하나의 인터페이스 뒤에 교환 가능한 알고리즘군을 정의하고 클라이언트가 런타임에 선택할 수 있게 합니다. 아래의 비대해진 switch를 다형성으로 대체하여, 컨텍스트가 자신이 보유한 전략에 작업을 위임합니다.
<?php
// The smell Strategy removes: branching on a type code
function price(string $type, float $base): float {
switch ($type) {
case 'standard': return $base + 5;
case 'express': return $base + 15;
default: throw new InvalidArgumentException($type);
}
}
echo price('express', 10), PHP_EOL; // 25
코드에서의 전략
여기서는 선택한 운송업체에 따라 배송 비용이 달라집니다. 각 전략은 하나의 가격 책정 알고리즘을 캡슐화하므로, 운송업체를 추가해도 컨텍스트는 변경되지 않습니다.
<?php
interface ShippingStrategy { public function cost(float $weight): float; }
final class Standard implements ShippingStrategy {
public function cost(float $w): float { return 5 + $w * 0.5; }
}
final class Express implements ShippingStrategy {
public function cost(float $w): float { return 15 + $w * 1.2; }
}
final class Cart {
public function __construct(private ShippingStrategy $strategy) {}
public function setStrategy(ShippingStrategy $s): void { $this->strategy = $s; }
public function quote(float $weight): float { return $this->strategy->cost($weight); }
}
$cart = new Cart(new Standard());
echo $cart->quote(10), PHP_EOL; // 10
$cart->setStrategy(new Express());
echo $cart->quote(10), PHP_EOL; // 27
클로저를 사용하는 전략
PHP에서 전략에 항상 클래스가 필요한 것은 아닙니다. 간단한 경우에는 callable 또는 일급 호출 가능 구문도 완전히 유효한 전략입니다. 알고리즘에 고유한 상태나 의존성이 있으면 일반 클래스를 사용하고, 단순하고 상태가 없는 선택에는 클로저를 사용하십시오.
<?php
$strategies = [
'asc' => fn(array $a) => sort($a) ? $a : $a,
'desc' => fn(array $a) => rsort($a) ? $a : $a,
];
$data = [3, 1, 2];
print_r($strategies['desc']($data)); // [3,2,1]
관찰자: 개념
관찰자 패턴을 사용하면 주체는 자신의 상태가 변경될 때 누가 관찰하는지 알지 못한 채 여러 관찰자에게 알릴 수 있습니다. 이를 통해 이벤트 출처와 처리기를 분리하며, 이벤트 시스템과 후크 및 반응형 업데이트의 기반이 됩니다.
코드에서의 관찰자
주체는 관찰자 목록을 유지하고 각 관찰자에서 update()를 호출합니다. 새 수신자는 주체의 로직을 변경하지 않고 연결할 수 있습니다.
<?php
interface Observer { public function update(string $event): void; }
final class OrderSubject {
private array $observers = [];
public function subscribe(Observer $o): void { $this->observers[] = $o; }
public function placed(string $id): void {
foreach ($this->observers as $o) { $o->update("order $id placed"); }
}
}
final class EmailObserver implements Observer {
public function update(string $e): void { echo "email: $e" . PHP_EOL; }
}
final class AuditObserver implements Observer {
public function update(string $e): void { echo "audit: $e" . PHP_EOL; }
}
$subject = new OrderSubject();
$subject->subscribe(new EmailObserver());
$subject->subscribe(new AuditObserver());
$subject->placed('42');
관찰자와 PSR-14 비교
현대 PHP에서는 PSR-14 이벤트 전달기를 통해 관찰자를 표현하는 경우가 많습니다. 전달기는 이벤트 객체를 등록된 수신기에게 전달합니다. 개념적 모델은 동일합니다(주체 = 이벤트, 관찰자 = 수신기). 하지만 전달기는 둘을 더욱 분리하고 중단 가능한 이벤트를 지원합니다. SPL의 SplObserver/SplSubject도 존재하지만 오늘날에는 거의 사용되지 않습니다.
명령: 개념
명령 패턴은 요청을 객체로 캡슐화합니다. 여기에는 동작, 수신자, 매개변수가 포함됩니다. 요청이 이제 데이터가 되었으므로 대기열에 넣거나, 로그 기록하거나, 재시도하거나, 실행 취소할 수 있습니다. 가장 단순한 명령은 아래와 같이 의도를 값으로 구체화할 뿐입니다.
<?php
final class SendEmail {
public function __construct(
public readonly string $to,
public readonly string $subject
) {}
}
// The request is now data you can store, serialize, or enqueue
$cmd = new SendEmail('user@example.com', 'Welcome');
echo json_encode($cmd), PHP_EOL;
코드에서의 명령
각 명령은 execute()를 구현합니다. 호출자는 명령을 보유하고 실행하며, 명령이 무엇을 하는지와는 분리됩니다. undo()를 추가하면 완전한 실행 취소 스택이 됩니다.
<?php
interface Command { public function execute(): string; }
final class Light { public bool $on = false; }
final class TurnOn implements Command {
public function __construct(private Light $light) {}
public function execute(): string { $this->light->on = true; return 'light on'; }
}
final class TurnOff implements Command {
public function __construct(private Light $light) {}
public function execute(): string { $this->light->on = false; return 'light off'; }
}
final class Invoker {
private array $queue = [];
public function add(Command $c): void { $this->queue[] = $c; }
public function run(): void {
foreach ($this->queue as $c) { echo $c->execute(), PHP_EOL; }
}
}
$light = new Light();
$inv = new Invoker();
$inv->add(new TurnOn($light));
$inv->add(new TurnOff($light));
$inv->run();
명령 버스
프레임워크는 이 패턴을 기반으로 명령 버스를 구축합니다. 명령 데이터 전송 객체가 정확히 하나의 처리기에게 전달됩니다. 버스 주변의 미들웨어는 트랜잭션, 유효성 검사, 로그 기록을 일관되게 추가할 수 있습니다. 명령은 일반적인 직렬화 가능 객체이므로 동일한 처리기를 동기적으로 실행하거나 대기열 작업자에게 보낼 수 있습니다.
세 패턴 중 선택하기
- 전략: 하나의 연산에 여러 교환 가능한 알고리즘이 있고 호출자가 선택합니다.
- 관찰자: 하나의 이벤트에 여러 독립적인 반응이 있으며, 출처는 처리기를 알지 못합니다.
- 명령: 요청을 구체화하여 대기열에 넣거나 로그 기록하거나 재시도하거나 실행 취소할 수 있게 합니다.
이 패턴들은 잘 결합됩니다. 명령 처리기가 전략을 선택하고 관찰자 이벤트를 발생시킬 수 있습니다.
빠른 확인
요구 사항을 패턴에 대응시키십시오.
복습
세 가지 패턴으로 동작을 모델링했습니다. 전략은 런타임에 알고리즘을 교체하고(클래스 또는 클로저), 관찰자는 결합되지 않은 수신자에게 상태 변경을 알리며(PSR-14 전달기의 기반), 명령은 요청을 대기열에 넣거나 로그 기록하거나 실행 취소할 수 있는 객체로 바꿉니다(명령 버스의 기반). 이들을 함께 사용하면 협력을 유연하게 유지하고 책임을 명확하게 나눌 수 있습니다.
자주 묻는 질문
“행동 패턴: 전략, 옵서버, 커맨드” 강의는 무료인가요?
네 — “행동 패턴: 전략, 옵서버, 커맨드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“행동 패턴: 전략, 옵서버, 커맨드”에서 뭘 배우나요?
행동 패턴으로 동작과 통신을 모델링합니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
PHP Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“행동 패턴: 전략, 옵서버, 커맨드” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- SOLID 원칙 실전
- 생성 패턴: 팩토리, 빌더, 싱글턴
- 구조 패턴: 어댑터, 데코레이터, 퍼사드
- 행동 패턴: 전략, 옵서버, 커맨드