Manager란
Illuminate\Support\Manager는 Laravel 4.0 무렵부터 존재하는 추상 클래스입니다. 캐시·세션·메일 등, 여러 “드라이버”를 전환하여 사용할 수 있는 시스템을 간단히 구축하기 위한 기반을 제공합니다.
“드라이버”란, 같은 인터페이스를 가지면서 내부 구현이 다른 백엔드를 말합니다. 예를 들어 세션이라면 file·cookie·database·redis와 같은 드라이버가 있으며, 설정 파일의 driver 키로 전환합니다.
프레임워크 내에서의 이용 상황
Manager라는 이름이 붙어 있어도 Illuminate\Support\Manager를 상속하고 있지 않은 클래스가 존재합니다.
클래스 Manager를 상속하고 있는가Illuminate\Session\SessionManager✅ 상속하고 있음 Illuminate\Cache\CacheManager❌ 상속하고 있지 않음(독자 구현) Illuminate\Queue\QueueManager❌ 상속하고 있지 않음(독자 구현) Laravel\Socialite\SocialiteManager✅ 상속하고 있음
상속하고 있지 않은 클래스도 extend()로 확장하는 기본적인 사고방식은 같습니다. 그러므로, Manager 패턴을 이해하는 것이 프레임워크 전체의 동작 이해로 이어집니다.
기본적인 구조
driver() — 드라이버를 취득한다
driver()를 호출하면, 다음의 절차로 드라이버가 해결됩니다.
실제 소스 코드를 보면, 드라이버명에서 Str::studly()로 메서드명을 조립하고 있습니다.
// Illuminate\Support\Manager::createDriver() より
protected function createDriver ( $driver )
{
if ( isset ( $this -> customCreators [ $driver ])) {
return $this -> callCustomCreator ( $driver );
}
$method = 'create' . Str :: studly ( $driver ) . 'Driver' ;
if ( method_exists ( $this , $method )) {
return $this -> $method ();
}
throw new InvalidArgumentException ( "Driver [ $driver ] not supported." );
}
즉 file 드라이버라면 createFileDriver()가, my-custom 드라이버라면 createMyCustomDriver()가 호출됩니다.
extend() — 커스텀 드라이버를 등록한다
extend()에 드라이버명과 클로저를 전달함으로써, 독자의 드라이버를 등록할 수 있습니다. 클로저의 인수는 컨테이너 인스턴스입니다.
use Illuminate\Support\Facades\ Session ;
Session :: extend ( 'redis-cluster' , function ( $app ) {
return new RedisClusterSessionHandler (
$app -> make ( 'redis' ),
$app [ 'config' ][ 'session' ],
);
});
extend()는 클로저를 $this에 바인딩하므로, 클로저 내에서 Manager의 프로퍼티나 메서드에 접근할 수 있습니다.
// Illuminate\Support\Manager::extend() より
public function extend ( $driver , Closure $callback )
{
try {
$callback = $callback -> bindTo ( $this , static :: class ) ?? throw new RuntimeException ;
} catch ( Throwable ) {
$callback = $callback -> bindTo ( null , static :: class );
}
$this -> customCreators [ $driver ] = $callback ;
return $this ;
}
__call — 기본 드라이버에의 위임
Manager 클래스에는 __call이 구현되어 있으며, Manager 자신에 존재하지 않는 메서드 호출은 기본 드라이버에 자동적으로 위임됩니다.
public function __call ( $method , $parameters )
{
return $this -> driver () -> $method ( ... $parameters );
}
이에 의해 SessionManager::get('key')와 같이 Manager를 직접 사용하면서, 실제의 처리는 드라이버에 맡긴다는 흐름이 실현되고 있습니다.
SessionManager의 구현 예
Illuminate\Session\SessionManager를 보면, Manager의 사용법이 구체적으로 알 수 있습니다.
namespace Illuminate\Session ;
use Illuminate\Support\ Manager ;
class SessionManager extends Manager
{
// 必須: デフォルトドライバーを返す
public function getDefaultDriver ()
{
return $this -> config -> get ( 'session.driver' );
}
// file ドライバーの生成
protected function createFileDriver ()
{
return $this -> createNativeDriver ();
}
// database ドライバーの生成
protected function createDatabaseDriver ()
{
$table = $this -> config -> get ( 'session.table' );
$lifetime = $this -> config -> get ( 'session.lifetime' );
return $this -> buildSession ( new DatabaseSessionHandler (
$this -> getDatabaseConnection (), $table , $lifetime , $this -> container
));
}
// redis ドライバーの生成
protected function createRedisDriver ()
{
$handler = $this -> createCacheHandler ( 'redis' );
// ...
return $this -> buildSession ( $handler );
}
}
자체 패키지에서 Manager를 사용한다
기본적인 구현
알림 서비스를 예로, Manager를 상속한 커스텀 클래스를 만듭니다.
Manager를 상속한 클래스를 만든다
namespace App\Notifications ;
use Illuminate\Support\ Manager ;
class NotificationManager extends Manager
{
public function getDefaultDriver () : string
{
return $this -> config -> get ( 'notifications.driver' , 'slack' );
}
protected function createSlackDriver () : SlackNotifier
{
return new SlackNotifier (
$this -> config -> get ( 'notifications.slack' ),
);
}
protected function createEmailDriver () : EmailNotifier
{
return new EmailNotifier (
$this -> config -> get ( 'notifications.email' ),
);
}
protected function createLogDriver () : LogNotifier
{
return new LogNotifier (
$this -> container -> make ( 'log' ),
);
}
}
서비스 프로바이더에서 등록한다
namespace App\Providers ;
use App\Notifications\ NotificationManager ;
use Illuminate\Support\ ServiceProvider ;
class NotificationServiceProvider extends ServiceProvider
{
public function register () : void
{
$this -> app -> singleton ( NotificationManager :: class , function ( $app ) {
return new NotificationManager ( $app );
});
}
}
커스텀 드라이버를 추가한다
// AppServiceProvider::boot() などで
$manager = app ( NotificationManager :: class );
$manager -> extend ( 'teams' , function ( $app ) {
return new TeamsNotifier (
$app [ 'config' ][ 'notifications.teams' ],
);
});
사용한다
$manager = app ( NotificationManager :: class );
// デフォルトドライバーを使う
$manager -> send ( 'メッセージ' );
// 特定のドライバーを指定する
$manager -> driver ( 'email' ) -> send ( 'メッセージ' );
// 一度解決したドライバーはキャッシュされる
$manager -> driver ( 'slack' ); // 同じインスタンスが返る
MultipleInstanceManager
Illuminate\Support\MultipleInstanceManager는 Laravel 10에서 추가된 클래스입니다. Manager가 드라이버의 “종류”를 관리하는 것에 비해, MultipleInstanceManager는 이름이 붙은 “인스턴스”를 여러 개 관리할 수 있습니다.
Manager와의 차이
ManagerMultipleInstanceManager관리 단위 드라이버의 종류(file, redis 등) 이름 붙은 인스턴스(mailer1, mailer2 등) 설정의 보유 방법 하나의 기본 드라이버 인스턴스별로 설정을 가짐 주요 용도 세션, 캐시 등 메일, 로그 등 여러 접속이 필요한 것 취득 메서드 driver()instance()
필수 메서드
MultipleInstanceManager를 상속하는 경우에는, 3개의 메서드를 구현해야 합니다.
// ソースコードより
abstract public function getDefaultInstance ();
abstract public function setDefaultInstance ( $name );
abstract public function getInstanceConfig ( $name );
getInstanceConfig()는 이름에 대응하는 설정 배열을 반환합니다. 설정에는 반드시 driver(또는 $driverKey로 지정한 키)가 포함되어 있어야 합니다.
해결 플로우
자체 패키지에서의 구현 예
여러 SMS 게이트웨이에 동시 대응하는 패키지를 예로 합니다.
namespace App\Sms ;
use Illuminate\Support\ MultipleInstanceManager ;
class SmsManager extends MultipleInstanceManager
{
// デフォルトインスタンス名
public function getDefaultInstance () : string
{
return $this -> config -> get ( 'sms.default' , 'primary' );
}
public function setDefaultInstance ( $name ) : void
{
$this -> config -> set ( 'sms.default' , $name );
}
// インスタンス名に対応する設定を返す
public function getInstanceConfig ( $name ) : array
{
return $this -> config -> get ( "sms.gateways.{ $name }" );
}
// twilio ドライバーの生成 (設定配列が渡される)
protected function createTwilioDriver ( array $config ) : TwilioSmsGateway
{
return new TwilioSmsGateway (
$config [ 'account_sid' ],
$config [ 'auth_token' ],
);
}
// vonage ドライバーの生成
protected function createVonageDriver ( array $config ) : VonageSmsGateway
{
return new VonageSmsGateway (
$config [ 'api_key' ],
$config [ 'api_secret' ],
);
}
}
대응하는 설정 파일:
// config/sms.php
return [
'default' => 'primary' ,
'gateways' => [
'primary' => [
'driver' => 'twilio' ,
'account_sid' => env ( 'TWILIO_ACCOUNT_SID' ),
'auth_token' => env ( 'TWILIO_AUTH_TOKEN' ),
],
'backup' => [
'driver' => 'vonage' ,
'api_key' => env ( 'VONAGE_API_KEY' ),
'api_secret' => env ( 'VONAGE_API_SECRET' ),
],
'marketing' => [
'driver' => 'twilio' ,
'account_sid' => env ( 'TWILIO_MARKETING_SID' ),
'auth_token' => env ( 'TWILIO_MARKETING_TOKEN' ),
],
],
];
사용 시에는 instance()로 이름을 지정합니다.
$sms = app ( SmsManager :: class );
// デフォルトインスタンス (primary = twilio) を使う
$sms -> send ( '+81901234xxxx' , '確認コード: 123456' );
// 名前でインスタンスを指定する
$sms -> instance ( 'backup' ) -> send ( '+81901234xxxx' , '確認コード: 123456' );
// マーケティング用のインスタンスを使う
$sms -> instance ( 'marketing' ) -> send ( '+81901234xxxx' , 'キャンペーンのお知らせ' );
MultipleInstanceManager는 Manager와 마찬가지로 extend()로 커스텀 드라이버를 추가할 수 있습니다. 또한 __call도 구현되어 있으므로, 인스턴스 자체에 존재하지 않는 메서드는 기본 인스턴스에 위임됩니다.
드라이버 캐시의 관리
Manager의 캐시 조작
// 全ドライバーを取得
$drivers = $manager -> getDrivers ();
// 全ドライバーのキャッシュをクリア
$manager -> forgetDrivers ();
MultipleInstanceManager의 캐시 조작
// 特定のインスタンスを削除する
$manager -> forgetInstance ( 'backup' );
// デフォルトインスタンスを削除する
$manager -> forgetInstance ();
// 複数のインスタンスを一括削除する
$manager -> forgetInstance ([ 'primary' , 'backup' ]);
// インスタンスを削除してキャッシュも消す
$manager -> purge ( 'backup' );
Manager는 드라이버의 “종류”를 관리한다. createXxxDriver() 메서드를 구현하고, extend()로 확장한다
MultipleInstanceManager는 드라이버의 “인스턴스”를 이름별로 관리한다. 여러 접속 설정이 필요한 패키지에 적합하다
Manager라는 이름의 클래스가 모두 Illuminate\Support\Manager를 상속하고 있는 것은 아니다. CacheManager나 QueueManager는 독자 구현
Macroable 트레이트 기존 클래스에 고유 메서드를 추가하는 Macroable 트레이트의 사용법을 배웁니다.