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

# ForwardsCalls 트레이트

> Illuminate\Support\Traits\ForwardsCalls를 사용해 프록시/데코레이터 클래스의 메서드 전달을 안전하게 구현하는 방법을 해설합니다.

## ForwardsCalls 트레이트란

`Illuminate\Support\Traits\ForwardsCalls`는 어떤 오브젝트로의 메서드 위임을 공통화하는 트레이트입니다. Laravel 본체에서는 Eloquent나 Mail 등의 "래퍼 오브젝트"에서 사용되고 있습니다.

<Info>
  구현은 `src/Illuminate/Support/Traits/ForwardsCalls.php`에 있습니다. 존재하지 않는 메서드를 전달한 경우, 호출자 클래스명을 포함한 `BadMethodCallException`으로 정형화해 재던집니다.
</Info>

```mermaid theme={null}
flowchart LR
  A["당신의 Proxy 클래스"] -->|__call| B["forwardCallTo()"]
  B --> C["내부의 Driver / Builder"]
  C --> D["결과를 반환"]
  D --> A
```

## 코어 API

### `forwardCallTo($object, $method, $parameters)`

지정된 오브젝트로 메서드를 그대로 전달합니다. 전형적으로는 `__call()`에서 호출합니다.

### `forwardDecoratedCallTo($object, $method, $parameters)`

Builder나 Decorator에서 체인을 유지하고 싶을 때 사용합니다. 전달 대상 메서드의 반환값이 "전달 대상 오브젝트 자신"이라면 "호출자 오브젝트(`$this`)"로 바꿔서 반환합니다.

### `__call()` / `__callStatic()`과의 조합

`ForwardsCalls`는 매직 메서드를 제공하지 않습니다. 여러분의 클래스 측에서 `__call()`(필요하면 `__callStatic()`)을 구현하고, 그 안에서 `forwardCallTo` 계열 메서드를 호출합니다.

## 실제 사용 예(Laravel 내부)

<Steps>
  <Step title="Facade는 static proxy를 제공한다">
    `Illuminate\Support\Facades\Facade`는 `__callStatic()`으로 root instance에 직접 위임합니다. Facade는 static 호출이므로 `ForwardsCalls`가 아니라 직접 전달하는 구현입니다.
  </Step>

  <Step title="Eloquent Builder는 forwardCallTo를 사용한다">
    `Illuminate\Database\Eloquent\Builder::__call()`은 해결되지 않은 메서드를 내부 Query Builder로 `forwardCallTo($this->query, ...)`로 전달하고, 마지막에 `$this`를 반환하여 fluent chain을 유지합니다.
  </Step>

  <Step title="Relation / Mail / Event는 forwardDecoratedCallTo를 사용한다">
    `Illuminate\Database\Eloquent\Relations\Relation`, `Illuminate\Mail\Message`, `Illuminate\Events\NullDispatcher`에서는 `forwardDecoratedCallTo`가 사용되어, 내부 오브젝트에 위임하면서 외부 API의 체인을 유지합니다.
  </Step>
</Steps>

## 기본적인 프록시 구현(`forwardCallTo`)

```php theme={null}
use Illuminate\Support\Traits\ForwardsCalls;

class CourierProxy
{
    use ForwardsCalls;

    public function __construct(
        protected CourierDriver $driver
    ) {}

    public function __call(string $method, array $parameters): mixed
    {
        return $this->forwardCallTo($this->driver, $method, $parameters);
    }
}
```

이러한 형태라면, `CourierProxy`는 `CourierDriver`의 public API를 투명하게 노출할 수 있습니다.

## 메서드 체인 대응 프록시(`forwardDecoratedCallTo`)

```php theme={null}
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Traits\ForwardsCalls;

class QueryProxy
{
    use ForwardsCalls;

    public function __construct(
        protected Builder $query
    ) {}

    public function __call(string $method, array $parameters): static
    {
        $this->forwardDecoratedCallTo($this->query, $method, $parameters);

        return $this;
    }

    public function get(): \Illuminate\Support\Collection
    {
        return $this->query->get();
    }
}
```

```php theme={null}
$users = (new QueryProxy(User::query()))
    ->where('active', true)
    ->orderByDesc('created_at')
    ->limit(10)
    ->get();
```

<Tip>
  수동으로 `return $this`를 쓰는 것보다 `forwardDecoratedCallTo`의 "반환값이 전달 대상 자신이라면 `$this`로 치환한다"는 규칙을 사용하는 편이 안전합니다.
</Tip>

## BadMethodCallException 자동 스로우

존재하지 않는 메서드를 호출하면, `ForwardsCalls`는 호출자 클래스명을 사용해 `BadMethodCallException`을 다시 던집니다.

```php theme={null}
try {
    (new CourierProxy($driver))->missingMethod();
} catch (\BadMethodCallException $e) {
    // 例: Call to undefined method App\Services\CourierProxy::missingMethod()
    report($e);
}
```

이를 통해 사용자는 "어느 외부 API에서 실패했는지"를 즉시 특정할 수 있습니다.

## Macroable과의 비교

| 관점      | `Macroable`          | `ForwardsCalls`                               |
| ------- | -------------------- | --------------------------------------------- |
| 목적      | 기존 클래스에 새 메서드 추가     | 다른 오브젝트로 메서드 위임                               |
| 주요 입구   | `macro()`, `mixin()` | `forwardCallTo()`, `forwardDecoratedCallTo()` |
| 적합한 케이스 | API 확장               | Proxy / Decorator / Adapter                   |

<Warning>
  "메서드를 늘리고 싶다"는데 `ForwardsCalls`를 사용하면, 위임 대상이 갖지 않는 메서드는 항상 실패합니다. API 확장이 목적이라면 `Macroable`을 선택하세요.
</Warning>

## 패키지 개발에서의 활용 예

### 1) Driver를 전환하는 Manager

```php theme={null}
use Illuminate\Support\Traits\ForwardsCalls;

class SmsManager
{
    use ForwardsCalls;

    public function __construct(
        protected SmsDriver $driver
    ) {}

    public function via(string $name): static
    {
        $this->driver = app(SmsDriverFactory::class)->make($name);

        return $this;
    }

    public function __call(string $method, array $parameters): mixed
    {
        return $this->forwardCallTo($this->driver, $method, $parameters);
    }
}
```

### 2) 여러 백엔드를 가진 Adapter

HTTP / Queue / WebSocket 등 다른 백엔드를 하나의 API로 통일하여 위임할 수 있습니다.

### 3) 테스트용 Spy / Stub

실제 드라이버 대신 Spy를 주입하고, 호출은 `forwardCallTo`로 그대로 흘려서 호출 횟수와 인수를 검증할 수 있습니다.

## 관련 페이지

<Columns cols={2}>
  <Card title="Macroable 트레이트" icon="puzzle-piece" href="/ko/advanced/macroable">
    기존 클래스에 새로운 메서드를 추가하는 확장 패턴을 배웁니다.
  </Card>

  <Card title="Conditionable 트레이트" icon="git-branch" href="/ko/advanced/conditionable">
    `when()` / `unless()`를 사용한 조건 분기 체인의 설계를 배웁니다.
  </Card>
</Columns>


## Related topics

- [Macroable 트레이트](/ko/advanced/macroable.md)
- [Dumpable 트레이트](/ko/advanced/dumpable.md)
- [Conditionable 트레이트](/ko/advanced/conditionable.md)
- [InteractsWithData 트레이트](/ko/advanced/interacts-with-data.md)
- [InteractsWithTime 트레이트](/ko/advanced/interacts-with-time.md)
