> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Illuminate\Support\Manager — 드라이버 시스템의 해부

> Laravel 4.0부터 이어진 드라이버 시스템의 핵심 클래스. 커스텀 드라이버 등록 방법부터 MultipleInstanceManager까지 해설.

## Manager란

`Illuminate\Support\Manager`는 Laravel 4.0 무렵부터 존재하는 추상 클래스입니다. 캐시·세션·메일 등, 여러 "드라이버"를 전환하여 사용할 수 있는 시스템을 간단히 구축하기 위한 기반을 제공합니다.

"드라이버"란, 같은 인터페이스를 가지면서 내부 구현이 다른 백엔드를 말합니다. 예를 들어 세션이라면 `file`·`cookie`·`database`·`redis`와 같은 드라이버가 있으며, 설정 파일의 `driver` 키로 전환합니다.

```mermaid theme={null}
classDiagram
    class Manager {
        <<abstract>>
        #container
        #config
        #customCreators[]
        #drivers[]
        +driver(name) mixed
        +extend(driver, Closure) self
        +getDrivers() array
        +forgetDrivers() self
        +getDefaultDriver()* string
        #createDriver(driver) mixed
        #callCustomCreator(driver) mixed
    }

    class SessionManager {
        +getDefaultDriver() string
        #createFileDriver() Store
        #createDatabaseDriver() Store
        #createRedisDriver() Store
        #createArrayDriver() Store
    }

    class YourCustomManager {
        +getDefaultDriver() string
        #createFooDriver() mixed
        #createBarDriver() mixed
    }

    Manager <|-- SessionManager
    Manager <|-- YourCustomManager
```

## 프레임워크 내에서의 이용 상황

`Manager`라는 이름이 붙어 있어도 `Illuminate\Support\Manager`를 상속하고 **있지 않은** 클래스가 존재합니다.

| 클래스                                  | `Manager`를 상속하고 있는가 |
| ------------------------------------ | ------------------- |
| `Illuminate\Session\SessionManager`  | ✅ 상속하고 있음           |
| `Illuminate\Cache\CacheManager`      | ❌ 상속하고 있지 않음(독자 구현) |
| `Illuminate\Queue\QueueManager`      | ❌ 상속하고 있지 않음(독자 구현) |
| `Laravel\Socialite\SocialiteManager` | ✅ 상속하고 있음           |

상속하고 있지 않은 클래스도 `extend()`로 확장하는 기본적인 사고방식은 같습니다. 그러므로, `Manager` 패턴을 이해하는 것이 프레임워크 전체의 동작 이해로 이어집니다.

## 기본적인 구조

### driver() — 드라이버를 취득한다

`driver()`를 호출하면, 다음의 절차로 드라이버가 해결됩니다.

```mermaid theme={null}
flowchart TD
    A["driver(name) 을 호출"] --> B{drivers[] 에<br>캐시가 있는가?}
    B -- 예 --> C["캐시된<br>인스턴스를 반환"]
    B -- 아니오 --> D{customCreators[]<br>에 등록이 있는가?}
    D -- 예 --> E["callCustomCreator()<br>로 인스턴스를 생성"]
    D -- 아니오 --> F{"createXxxDriver()<br>메서드가 존재하는가?"}
    F -- 예 --> G["createXxxDriver()<br>를 호출"]
    F -- 아니오 --> H["InvalidArgumentException<br>을 스로우"]
    E --> I["drivers[] 에 캐시하여 반환"]
    G --> I
```

실제 소스 코드를 보면, 드라이버명에서 `Str::studly()`로 메서드명을 조립하고 있습니다.

```php theme={null}
// 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()`에 드라이버명과 클로저를 전달함으로써, 독자의 드라이버를 등록할 수 있습니다. 클로저의 인수는 컨테이너 인스턴스입니다.

```php theme={null}
use Illuminate\Support\Facades\Session;

Session::extend('redis-cluster', function ($app) {
    return new RedisClusterSessionHandler(
        $app->make('redis'),
        $app['config']['session'],
    );
});
```

`extend()`는 클로저를 `$this`에 바인딩하므로, 클로저 내에서 Manager의 프로퍼티나 메서드에 접근할 수 있습니다.

```php theme={null}
// 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 자신에 존재하지 않는 메서드 호출은 기본 드라이버에 자동적으로 위임됩니다.

```php theme={null}
public function __call($method, $parameters)
{
    return $this->driver()->$method(...$parameters);
}
```

이에 의해 `SessionManager::get('key')`와 같이 Manager를 직접 사용하면서, 실제의 처리는 드라이버에 맡긴다는 흐름이 실현되고 있습니다.

## SessionManager의 구현 예

`Illuminate\Session\SessionManager`를 보면, `Manager`의 사용법이 구체적으로 알 수 있습니다.

```php theme={null}
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`를 상속한 커스텀 클래스를 만듭니다.

<Steps>
  <Step title="Manager를 상속한 클래스를 만든다">
    ```php theme={null}
    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'),
            );
        }
    }
    ```
  </Step>

  <Step title="서비스 프로바이더에서 등록한다">
    ```php theme={null}
    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);
            });
        }
    }
    ```
  </Step>

  <Step title="커스텀 드라이버를 추가한다">
    ```php theme={null}
    // AppServiceProvider::boot() などで

    $manager = app(NotificationManager::class);

    $manager->extend('teams', function ($app) {
        return new TeamsNotifier(
            $app['config']['notifications.teams'],
        );
    });
    ```
  </Step>

  <Step title="사용한다">
    ```php theme={null}
    $manager = app(NotificationManager::class);

    // デフォルトドライバーを使う
    $manager->send('メッセージ');

    // 特定のドライバーを指定する
    $manager->driver('email')->send('メッセージ');

    // 一度解決したドライバーはキャッシュされる
    $manager->driver('slack'); // 同じインスタンスが返る
    ```
  </Step>
</Steps>

## MultipleInstanceManager

`Illuminate\Support\MultipleInstanceManager`는 Laravel 10에서 추가된 클래스입니다. `Manager`가 드라이버의 "종류"를 관리하는 것에 비해, `MultipleInstanceManager`는 이름이 붙은 "인스턴스"를 여러 개 관리할 수 있습니다.

### Manager와의 차이

|           | `Manager`                   | `MultipleInstanceManager`          |
| --------- | --------------------------- | ---------------------------------- |
| 관리 단위     | 드라이버의 종류(`file`, `redis` 등) | 이름 붙은 인스턴스(`mailer1`, `mailer2` 등) |
| 설정의 보유 방법 | 하나의 기본 드라이버                 | 인스턴스별로 설정을 가짐                      |
| 주요 용도     | 세션, 캐시 등                    | 메일, 로그 등 여러 접속이 필요한 것              |
| 취득 메서드    | `driver()`                  | `instance()`                       |

### 필수 메서드

`MultipleInstanceManager`를 상속하는 경우에는, 3개의 메서드를 구현해야 합니다.

```php theme={null}
// ソースコードより
abstract public function getDefaultInstance();
abstract public function setDefaultInstance($name);
abstract public function getInstanceConfig($name);
```

`getInstanceConfig()`는 이름에 대응하는 설정 배열을 반환합니다. 설정에는 반드시 `driver`(또는 `$driverKey`로 지정한 키)가 포함되어 있어야 합니다.

### 해결 플로우

```mermaid theme={null}
flowchart TD
    A["instance(name) 을 호출"] --> B["name 이 null 이면<br>getDefaultInstance() 를 사용"]
    B --> C{instances[] 에<br>캐시가 있는가?}
    C -- 예 --> D["캐시된<br>인스턴스를 반환"]
    C -- 아니오 --> E["getInstanceConfig(name) 으로 설정을 취득"]
    E --> F{설정에 driver<br>키가 있는가?}
    F -- 아니오 --> G["RuntimeException 을 스로우"]
    F -- 예 --> H{customCreators[]<br>에 등록이 있는가?}
    H -- 예 --> I["callCustomCreator(config) 로 생성"]
    H -- 아니오 --> J["createXxxDriver(config) 를 호출"]
    I --> K["instances[] 에 캐시하여 반환"]
    J --> K
```

### 자체 패키지에서의 구현 예

여러 SMS 게이트웨이에 동시 대응하는 패키지를 예로 합니다.

```php theme={null}
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'],
        );
    }
}
```

대응하는 설정 파일:

```php theme={null}
// 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()`로 이름을 지정합니다.

```php theme={null}
$sms = app(SmsManager::class);

// デフォルトインスタンス (primary = twilio) を使う
$sms->send('+81901234xxxx', '確認コード: 123456');

// 名前でインスタンスを指定する
$sms->instance('backup')->send('+81901234xxxx', '確認コード: 123456');

// マーケティング用のインスタンスを使う
$sms->instance('marketing')->send('+81901234xxxx', 'キャンペーンのお知らせ');
```

<Tip>
  `MultipleInstanceManager`는 `Manager`와 마찬가지로 `extend()`로 커스텀 드라이버를 추가할 수 있습니다. 또한 `__call`도 구현되어 있으므로, 인스턴스 자체에 존재하지 않는 메서드는 기본 인스턴스에 위임됩니다.
</Tip>

## 드라이버 캐시의 관리

### Manager의 캐시 조작

```php theme={null}
// 全ドライバーを取得
$drivers = $manager->getDrivers();

// 全ドライバーのキャッシュをクリア
$manager->forgetDrivers();
```

### MultipleInstanceManager의 캐시 조작

```php theme={null}
// 特定のインスタンスを削除する
$manager->forgetInstance('backup');

// デフォルトインスタンスを削除する
$manager->forgetInstance();

// 複数のインスタンスを一括削除する
$manager->forgetInstance(['primary', 'backup']);

// インスタンスを削除してキャッシュも消す
$manager->purge('backup');
```

## 정리

* `Manager`는 드라이버의 "종류"를 관리한다. `createXxxDriver()` 메서드를 구현하고, `extend()`로 확장한다
* `MultipleInstanceManager`는 드라이버의 "인스턴스"를 이름별로 관리한다. 여러 접속 설정이 필요한 패키지에 적합하다
* `Manager`라는 이름의 클래스가 모두 `Illuminate\Support\Manager`를 상속하고 있는 것은 아니다. `CacheManager`나 `QueueManager`는 독자 구현

<Card title="Macroable 트레이트" icon="puzzle-piece" href="/ko/advanced/macroable">
  기존 클래스에 고유 메서드를 추가하는 Macroable 트레이트의 사용법을 배웁니다.
</Card>


## Related topics

- [파일 스토리지](/ko/filesystem.md)
- [Laravel Socialite (소셜 인증)](/ko/socialite.md)
- [Laravel Sentinel — 라우트 보호 미들웨어 조사](/ko/blog/sentinel-introduction.md)
- [로깅](/ko/logging.md)
- [Laravel AI SDK용 Amazon Bedrock 드라이버](/ko/packages/laravel-amazon-bedrock.md)
