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

# Eloquent Bootable Traits

> Eloquent 모델의 트레이트 자동 부팅 메커니즘을 자세히 살펴봅니다. `bootXxx()`·`initializeXxx()` 메서드의 네이밍 규약과 PHP Attribute를 사용한 명시적 지정 방법을 학습합니다. 패키지 개발에서 자주 사용되는 패턴입니다.

## 개요

Eloquent 모델은 인스턴스화될 때마다 "부팅 처리(boot)"와 "초기화 처리(initialize)"를 실행합니다. 트레이트에 이러한 메서드를 정의해 두면, 모델이 해당 트레이트를 `use` 하는 것만으로 자동으로 호출됩니다.

이 메커니즘을 활용하면 모델에 기능을 주입하는 트레이트를 깔끔하게 구현할 수 있습니다. Laravel 프레임워크 자체도 `SoftDeletes` 등 많은 트레이트에서 이 패턴을 활용하고 있습니다.

```mermaid theme={null}
sequenceDiagram
    participant App as 애플리케이션
    participant Model as Eloquent Model
    participant Trait as 트레이트

    App->>Model: new Post() 또는 Post::find()
    Model->>Model: bootIfNotBooted()
    Model->>Trait: bootXxx() (클래스당 최초 1회)
    Model->>Model: initializeTraits()
    Model->>Trait: initializeXxx() (인스턴스마다)
    Model-->>App: 모델 인스턴스
```

***

## boot와 initialize의 차이

|            | `bootXxx()`                | `initializeXxx()`  |
| ---------- | -------------------------- | ------------------ |
| **호출 시점**  | 모델 클래스가 최초로 부팅될 때(클래스당 1회) | 인스턴스가 생성될 때마다      |
| **메서드 종류** | `static`                   | 인스턴스 메서드           |
| **용도**     | 이벤트 리스너 등록, 글로벌 스코프 추가 등   | 기본값 설정, 프로퍼티 초기화 등 |

***

## 네이밍 규약

### bootXxx()

트레이트 이름 앞에 `boot` 접두어를 붙인 **`static`** 메서드를 정의하면, 모델 부팅 시 한 번만 호출됩니다.

```php theme={null}
namespace App\Models\Concerns;

trait HasSlug
{
    public static function bootHasSlug(): void
    {
        static::creating(function ($model) {
            if (empty($model->slug)) {
                $model->slug = str($model->name)->slug()->toString();
            }
        });
    }
}
```

모델에서의 사용 예:

```php theme={null}
use App\Models\Concerns\HasSlug;

class Article extends Model
{
    use HasSlug;
    // bootHasSlug() 는 자동으로 호출된다
}
```

### initializeXxx()

트레이트 이름 앞에 `initialize` 접두어를 붙인 인스턴스 메서드를 정의하면, 모델을 `new` 할 때마다 호출됩니다.

```php theme={null}
namespace App\Models\Concerns;

trait HasDefaultStatus
{
    public function initializeHasDefaultStatus(): void
    {
        $this->attributes['status'] ??= 'draft';
    }
}
```

`$casts` 에 동적으로 추가할 때에도 사용할 수 있습니다:

```php theme={null}
trait HasMetadata
{
    public function initializeHasMetadata(): void
    {
        $this->mergeCasts([
            'metadata' => 'array',
        ]);
    }
}
```

***

## PHP Attribute를 통한 명시적 지정

Laravel 11부터는 네이밍 규약에 의존하지 않고 PHP Attribute로 메서드를 명시적으로 지정할 수 있습니다.

| Attribute       | 대상                    |
| --------------- | --------------------- |
| `#[Boot]`       | 클래스 부팅 시 한 번만 실행할 메서드 |
| `#[Initialize]` | 인스턴스 생성 시마다 실행할 메서드   |

```php theme={null}
use Illuminate\Database\Eloquent\Attributes\Boot;
use Illuminate\Database\Eloquent\Attributes\Initialize;

trait HasAuditLog
{
    #[Boot]
    public static function registerAuditListeners(): void
    {
        static::created(function ($model) {
            AuditLog::record('created', $model);
        });

        static::updated(function ($model) {
            AuditLog::record('updated', $model);
        });
    }

    #[Initialize]
    public function setAuditDefaults(): void
    {
        $this->attributes['audit_enabled'] ??= true;
    }
}
```

<Info>
  PHP Attribute를 사용하면 메서드 이름이 트레이트 이름에 얽매이지 않기 때문에, 의미 있는 이름을 붙일 수 있습니다. 여러 개의 `#[Boot]` 메서드나 `#[Initialize]` 메서드를 같은 트레이트 안에 정의하는 것도 가능합니다.
</Info>

***

## 실전 예제: 패키지 개발에서의 활용

### 글로벌 스코프의 자동 추가

```php theme={null}
trait HasTenant
{
    public static function bootHasTenant(): void
    {
        static::addGlobalScope(new TenantScope);
    }
}
```

### 기본 캐스트 추가

```php theme={null}
trait HasJsonSettings
{
    public function initializeHasJsonSettings(): void
    {
        $this->mergeCasts([
            'settings' => 'array',
        ]);
    }
}
```

### Eloquent 이벤트로 자동 처리

```php theme={null}
trait Archivable
{
    #[Boot]
    public static function bootArchivable(): void
    {
        static::deleting(function ($model) {
            Archive::store($model->toArray());
        });
    }
}
```

***

## Laravel 프레임워크 내의 예시

Laravel 프레임워크 자체도 많은 트레이트에서 이 패턴을 채택하고 있습니다.

| 트레이트          | 메서드                 | 처리 내용                        |
| ------------- | ------------------- | ---------------------------- |
| `SoftDeletes` | `bootSoftDeletes()` | 소프트 삭제용 글로벌 스코프 추가           |
| `HasFactory`  | `bootHasFactory()`  | 특별한 처리 없음(범위 외 메커니즘)         |
| `HasUlids`    | `bootHasUlids()`    | `creating` 이벤트에서 ULID를 자동 설정 |
| `HasUuids`    | `bootHasUuids()`    | `creating` 이벤트에서 UUID를 자동 설정 |

이들을 참고 구현으로 읽어 보면, 패턴에 대한 이해가 더 깊어집니다.

***

## 주의 사항

### 모델 캐시 초기화

`boot*` 는 클래스마다 한 번만 실행되기 때문에, 테스트 환경에서는 주의가 필요합니다. `Model::clearBootedModels()` 로 부팅된 캐시를 리셋할 수 있습니다.

```php theme={null}
// 테스트 종료 후 모델의 부팅 상태를 리셋한다
protected function tearDown(): void
{
    Model::clearBootedModels();
    parent::tearDown();
}
```

### 실행 순서

같은 모델에 여러 트레이트가 `use` 되어 있을 경우, `bootXxx()` / `initializeXxx()` 는 PHP의 트레이트 해결 순서(선언 순서)로 호출됩니다. 의존 관계가 있다면 `use` 순서에 주의하시기 바랍니다.

***

## 정리

| 패턴          | 방법                                   | 적용 상황              |
| ----------- | ------------------------------------ | ------------------ |
| 클래스 부팅 시 처리 | `static bootXxx()` 또는 `#[Boot]`      | 이벤트 등록, 글로벌 스코프 추가 |
| 인스턴스 초기화    | `initializeXxx()` 또는 `#[Initialize]` | 기본값, 캐스트 추가        |

트레이트에 `bootXxx()` / `initializeXxx()` 를 구현해 두면, 모델에 기능을 주입하는 일이 `use` 한 줄로 끝나게 됩니다. 패키지에서 모델에 기능을 추가할 때의 정석 패턴입니다.


## Related topics

- [Conditionable 트레이트](/ko/advanced/conditionable.md)
- [ForwardsCalls 트레이트](/ko/advanced/forwards-calls.md)
- [Eloquent 입문](/ko/eloquent.md)
- [Eloquent Factories](/ko/eloquent-factories.md)
- [Dumpable 트레이트](/ko/advanced/dumpable.md)
