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

# PHP 어트리뷰트

> Laravel 13에서 도입·강화된 PHP 어트리뷰트를 사용하여 잡이나 모델의 설정을 보다 선언적으로 작성하는 방법을 설명합니다.

## PHP 어트리뷰트란

PHP 어트리뷰트(PHP Attributes)는 PHP 8.0에서 도입된 네이티브 메타데이터 문법입니다. 클래스, 메서드, 프로퍼티, 함수 등에 대해 `#[AttributeName]` 형식으로 메타 정보를 부여할 수 있습니다.

Laravel은 프레임워크 본체에서 PHP 어트리뷰트를 적극적으로 채택하고 있으며, 잡이나 Eloquent 모델의 설정을 선언적으로 작성할 수 있습니다. Laravel 13(v13.2.0)에서는 큐 어트리뷰트가 enum을 받을 수 있게 되었습니다. 기존의 클래스 프로퍼티나 메서드 오버라이드 대신 어트리뷰트를 사용하여 더 읽기 쉽고 간결한 코드를 작성할 수 있습니다.

```php theme={null}
// 기존 방식
class ProcessOrder implements ShouldQueue
{
    public string $queue = 'orders';
    public string $connection = 'redis';
    public int $tries = 3;
    public array $backoff = [30, 60, 120];
}

// 어트리뷰트를 사용한 방식
#[Queue('orders')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
}
```

<Tip>
  어트리뷰트는 PHP 8.0 이상에서 사용할 수 있습니다. Laravel 13은 PHP 8.3 이상을 요구하기 때문에 모든 환경에서 어트리뷰트를 사용할 수 있습니다.
</Tip>

## 큐 관련 어트리뷰트

큐 잡과 관련된 어트리뷰트는 모두 `Illuminate\Queue\Attributes` 네임스페이스에 있습니다.

### `#[Queue]` — 큐 이름 지정

잡이 전송되는 기본 큐 이름을 지정합니다.

```php theme={null}
use Illuminate\Queue\Attributes\Queue;

#[Queue('emails')]
class SendWelcomeEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
```

v13.2.0부터는 문자열 대신 enum을 전달할 수도 있습니다.

```php theme={null}
enum QueueName: string
{
    case Emails = 'emails';
    case Orders = 'orders';
    case Notifications = 'notifications';
}

#[Queue(QueueName::Emails)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
```

<Info>
  `#[Queue]` 어트리뷰트는 `Attribute::TARGET_CLASS`를 대상으로 설정되어 있으므로 클래스에만 적용할 수 있습니다.
</Info>

### `#[Connection]` — 연결 지정

잡이 사용하는 기본 큐 연결을 지정합니다.

```php theme={null}
use Illuminate\Queue\Attributes\Connection;

#[Connection('sqs')]
class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
```

이쪽도 enum을 사용할 수 있습니다.

```php theme={null}
enum QueueConnection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
}

#[Connection(QueueConnection::Sqs)]
class ProcessPayment implements ShouldQueue
{
    // ...
}
```

### `#[Backoff]` — 재시도 백오프 시간 지정

잡이 실패했을 때 재시도까지의 대기 시간(초)을 지정합니다. 여러 값을 전달하면 재시도마다 다른 대기 시간을 설정할 수 있습니다(가변 인자 지원).

```php theme={null}
use Illuminate\Queue\Attributes\Backoff;

// 고정 대기 시간(모든 재시도에 공통으로 60초 대기)
#[Backoff(60)]
class SendEmail implements ShouldQueue
{
    // ...
}

// 재시도마다 다른 대기 시간(지수 백오프)
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
```

`Backoff` 클래스의 구현을 보면 가변 인자를 받도록 설계되어 있습니다.

```php theme={null}
// Illuminate\Queue\Attributes\Backoff의 구현
#[Attribute(Attribute::TARGET_CLASS)]
class Backoff
{
    public array|int $backoff;

    public function __construct(array|int ...$backoff)
    {
        $this->backoff = count($backoff) === 1 ? $backoff[0] : $backoff;
    }
}
```

단일 값인 경우 `int`로, 여러 개인 경우 `array`로 저장됩니다.

### `#[Tries]` — 재시도 횟수 지정

잡이 실패했을 때의 최대 재시도 횟수를 지정합니다.

```php theme={null}
use Illuminate\Queue\Attributes\Tries;

#[Tries(5)]
class ProcessPayment implements ShouldQueue
{
    // ...
}
```

### `#[Timeout]` — 타임아웃 지정

잡의 최대 실행 시간(초)을 지정합니다. 이 시간을 초과하면 잡이 강제 종료됩니다.

```php theme={null}
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(120)]
class GenerateReport implements ShouldQueue
{
    // ...
}
```

### `#[MaxExceptions]` — 허용 예외 수 지정

지정한 횟수 이상의 예외가 발생한 경우 잡을 실패로 간주합니다. `#[Tries]`와 조합하여 사용합니다.

```php theme={null}
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Tries;

#[Tries(10)]
#[MaxExceptions(3)]
class ProcessWebhook implements ShouldQueue
{
    // ...
}
```

### `#[UniqueFor]` — 유니크 기간 지정

잡의 중복 실행을 방지하는 잠금 기간(초)을 지정합니다. `ShouldBeUnique`와 조합하여 사용합니다.

```php theme={null}
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\UniqueFor;

#[UniqueFor(3600)]
class SyncUserData implements ShouldQueue, ShouldBeUnique
{
    // ...
}
```

### `#[DeleteWhenMissingModels]` — 모델이 존재하지 않을 때 삭제

잡이 의존하는 Eloquent 모델이 발견되지 않는 경우, 잡을 실패가 아닌 삭제(스킵)로 처리합니다.

```php theme={null}
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;

#[DeleteWhenMissingModels]
class SendOrderConfirmation implements ShouldQueue
{
    public function __construct(
        protected Order $order,
    ) {}

    public function handle(): void
    {
        // $order가 존재하지 않는 경우 이 잡이 삭제됨
    }
}
```

### `#[WithoutRelations]` — 릴레이션 제외

잡을 직렬화할 때 모델의 릴레이션을 포함하지 않도록 합니다. 큐로 전송되는 데이터를 경량화할 수 있습니다.

```php theme={null}
use Illuminate\Queue\Attributes\WithoutRelations;

#[WithoutRelations]
class ExportUser implements ShouldQueue
{
    public function __construct(
        protected User $user,
    ) {}
}
```

### `#[FailOnTimeout]` — 타임아웃 시 실패로 처리

타임아웃이 발생한 경우 잡을 실패로 기록합니다(기본적으로 타임아웃은 실패로 기록되지 않습니다).

```php theme={null}
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(30)]
#[FailOnTimeout]
class ProcessLongTask implements ShouldQueue
{
    // ...
}
```

## 여러 큐 어트리뷰트 조합하기

이러한 어트리뷰트들을 조합하여 잡의 동작을 선언적으로 설정할 수 있습니다.

```php theme={null}
use Illuminate\Queue\Attributes\Backoff;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\Tries;

#[Queue('payments')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
#[Timeout(60)]
#[MaxExceptions(2)]
#[DeleteWhenMissingModels]
class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        protected Order $order,
    ) {}

    public function handle(PaymentService $payment): void
    {
        $payment->charge($this->order);
    }
}
```

## Eloquent 관련 어트리뷰트

Eloquent 모델의 어트리뷰트는 `Illuminate\Database\Eloquent\Attributes` 네임스페이스에 있습니다. Laravel 13에서는 다수의 어트리뷰트가 추가되었습니다.

### `#[ScopedBy]` — 글로벌 스코프 지정

모델에 자동으로 적용할 글로벌 스코프 클래스를 어트리뷰트로 지정합니다. 상속을 지원하며, `IS_REPEATABLE` 플래그로 여러 스코프를 지정할 수 있습니다.

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

#[ScopedBy(ActiveScope::class)]
class User extends Model
{
    // booted()에서 스코프를 등록할 필요가 없어짐
}
```

여러 스코프를 부여할 경우, 어트리뷰트를 반복하거나 배열로 전달합니다.

```php theme={null}
// 반복 지정(IS_REPEATABLE 대응)
#[ScopedBy(ActiveScope::class)]
#[ScopedBy(VerifiedScope::class)]
class User extends Model
{
}

// 배열로 일괄 지정
#[ScopedBy([ActiveScope::class, VerifiedScope::class])]
class User extends Model
{
}
```

기존 `booted()` 메서드와의 비교입니다.

```php theme={null}
// 기존 방식
class User extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveScope);
        static::addGlobalScope(new VerifiedScope);
    }
}
```

### `#[ObservedBy]` — 옵저버 지정

모델에 연결할 옵저버 클래스를 어트리뷰트로 지정합니다. `ScopedBy`와 마찬가지로 `IS_REPEATABLE`입니다.

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

#[ObservedBy(UserObserver::class)]
class User extends Model
{
}
```

여러 옵저버도 지정할 수 있습니다.

```php theme={null}
#[ObservedBy(UserObserver::class)]
#[ObservedBy(AuditObserver::class)]
class User extends Model
{
}
```

기존의 `AppServiceProvider`에서의 등록이 필요 없어집니다.

```php theme={null}
// 기존 방식(AppServiceProvider)
public function boot(): void
{
    User::observe(UserObserver::class);
}
```

### `#[UseEloquentBuilder]` — 커스텀 쿼리 빌더 지정

모델이 사용하는 커스텀 Eloquent 빌더를 어트리뷰트로 지정합니다.

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

#[UseEloquentBuilder(UserQueryBuilder::class)]
class User extends Model
{
}
```

```php theme={null}
namespace App\Builders;

use Illuminate\Database\Eloquent\Builder;

class UserQueryBuilder extends Builder
{
    public function active(): static
    {
        return $this->where('active', true);
    }

    public function verified(): static
    {
        return $this->whereNotNull('email_verified_at');
    }
}
```

```php theme={null}
// 사용 예시(커스텀 메서드를 타입 안전하게 호출 가능)
$users = User::query()->active()->verified()->get();
```

### `#[CollectedBy]` — 커스텀 컬렉션 지정

모델의 컬렉션으로 사용할 커스텀 컬렉션 클래스를 어트리뷰트로 지정합니다.

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

#[CollectedBy(UserCollection::class)]
class User extends Model
{
}
```

```php theme={null}
namespace App\Collections;

use Illuminate\Database\Eloquent\Collection;

class UserCollection extends Collection
{
    public function admins(): static
    {
        return $this->filter(fn (User $user) => $user->is_admin);
    }

    public function active(): static
    {
        return $this->filter(fn (User $user) => $user->active);
    }
}
```

### `#[Table]` — 테이블 설정을 일괄 지정

테이블 이름, 기본 키, 타임스탬프 등 여러 테이블 관련 설정을 하나의 어트리뷰트로 지정할 수 있습니다.

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

#[Table(name: 'system_users', key: 'user_id', timestamps: false)]
class SystemUser extends Model
{
}
```

`Table` 어트리뷰트에서 설정할 수 있는 옵션은 다음과 같습니다.

| 파라미터           | 대응하는 프로퍼티       | 설명                              |
| -------------- | --------------- | ------------------------------- |
| `name`         | `$table`        | 테이블 이름                          |
| `key`          | `$primaryKey`   | 기본 키 컬럼 이름                      |
| `keyType`      | `$keyType`      | 기본 키의 타입(`'int'`, `'string'` 등) |
| `incrementing` | `$incrementing` | 기본 키의 자동 증가                     |
| `timestamps`   | `$timestamps`   | 타임스탬프 활성화/비활성화                  |
| `dateFormat`   | `$dateFormat`   | 날짜 포맷                           |

### `#[Scope]` — 메서드를 로컬 스코프로 정의

`scope` 접두사 없는 메서드를 Eloquent의 로컬 스코프로 정의할 수 있습니다.

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

class User extends Model
{
    #[Scope]
    public function active(Builder $query): void
    {
        $query->where('active', true);
    }

    #[Scope]
    public function verified(Builder $query): void
    {
        $query->whereNotNull('email_verified_at');
    }
}
```

```php theme={null}
// 기존에는 메서드 이름에 "scope" 접두사가 필요했음
// public function scopeActive(Builder $query): void

// 어트리뷰트를 사용하면 메서드 이름이 그대로 스코프 이름이 됨
User::query()->active()->verified()->get();
```

### `#[UseFactory]` — 팩토리 클래스 지정

모델이 사용하는 커스텀 팩토리 클래스를 어트리뷰트로 지정합니다.

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

#[UseFactory(UserFactory::class)]
class User extends Model
{
}
```

### 그 밖의 Eloquent 어트리뷰트

| 어트리뷰트                                                      | 설명                               |
| ---------------------------------------------------------- | -------------------------------- |
| `#[Fillable(...$attributes)]`                              | 매스 어사인먼트에서 허용할 컬럼 지정             |
| `#[Guarded(...$attributes)]`                               | 매스 어사인먼트에서 보호할 컬럼 지정             |
| `#[Unguarded]`                                             | 매스 어사인먼트 보호 비활성화                 |
| `#[Hidden(...$attributes)]`                                | 직렬화 시 제외할 컬럼 지정                  |
| `#[Visible(...$attributes)]`                               | 직렬화 시 포함할 컬럼 지정                  |
| `#[Appends(...$attributes)]`                               | 직렬화 시 추가할 액세서 지정                 |
| `#[Touches(...$relations)]`                                | 업데이트 시 `updated_at`을 갱신할 릴레이션 지정 |
| `#[WithoutTimestamps]`                                     | 타임스탬프 비활성화                       |
| `#[WithoutIncrementing]`                                   | 기본 키 자동 증가 비활성화                  |
| `#[DateFormat(format: '...')]`                             | 날짜 포맷 지정                         |
| `#[UsePolicy(policyClass: '...')]`                         | 관련 폴리시 클래스 지정                    |
| `#[UseResource(resourceClass: '...')]`                     | 관련 API 리소스 클래스 지정                |
| `#[UseResourceCollection(resourceCollectionClass: '...')]` | 관련 리소스 컬렉션 클래스 지정                |

## Enum 지원(v13.2.0에서 추가)

v13.2.0에서는 `#[Queue]`와 `#[Connection]`이 enum을 받을 수 있게 되었습니다. 이를 통해 문자열 리터럴 대신 PHP enum을 사용하여 타입 안전하게 큐와 연결을 지정할 수 있습니다.

```php theme={null}
// 큐 이름 enum 정의
enum Queue: string
{
    case Default = 'default';
    case High = 'high';
    case Low = 'low';
    case Emails = 'emails';
    case Orders = 'orders';
}

// 연결 enum 정의
enum Connection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
    case Sync = 'sync';
}
```

```php theme={null}
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Connection;

// enum을 사용한 타입 안전한 지정
#[Queue(Queue::Orders)]
#[Connection(Connection::Redis)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
```

<Tip>
  enum을 사용하면 큐 이름이나 연결 이름의 오타를 방지하고 IDE 자동 완성도 활용할 수 있습니다. 애플리케이션 전체에서 큐 이름과 연결 이름을 일원화하여 관리하기에 편리합니다.
</Tip>

## 기존 클래스 프로퍼티와의 비교

### 어트리뷰트의 장점

* **선언적** — 클래스의 상단만 봐도 잡의 동작을 한눈에 파악할 수 있음
* **타입 안전** — enum을 사용하면 IDE 자동 완성과 타입 검사가 동작함
* **상속과의 친화성** — 부모 클래스의 어트리뷰트를 자식 클래스에서 덮어쓸 수 있음
* **코드 감소** — 프로퍼티 선언이나 메서드 오버라이드가 불필요

### 어트리뷰트의 단점

* **동적 값을 설정할 수 없음** — 어트리뷰트의 인자는 컴파일 시 상수만 가능. 변수나 설정 파일의 값은 사용할 수 없음
* **익숙해질 시간이 필요** — 팀에서 PHP 8의 어트리뷰트 문법에 익숙해질 필요가 있는 경우도 있음

### 동적 값이 필요한 경우

실행 시에 값을 결정하고 싶은 경우에는 기존의 메서드 오버라이드를 사용합니다.

```php theme={null}
class ProcessOrder implements ShouldQueue
{
    // 동적인 백오프는 메서드로 정의
    public function backoff(): array
    {
        return [
            config('queue.backoff.first'),
            config('queue.backoff.second'),
        ];
    }
}
```

<Warning>
  어트리뷰트는 PHP 컴파일 시에 분석됩니다. `config()`나 `env()`와 같은 런타임 값을 사용할 수는 없습니다. 동적인 설정이 필요한 경우에는 계속해서 클래스 프로퍼티나 메서드를 사용하세요.
</Warning>

## 구현 방식

Laravel은 내부적으로 Reflection API를 사용하여 어트리뷰트를 읽어 옵니다. 큐 워커가 잡을 디스패치할 때 `ReadsQueueAttributes` 트레이트(`InteractsWithQueue`에 포함되어 있음)가 리플렉션으로 어트리뷰트를 검출하여 해당하는 프로퍼티에 값을 설정합니다.

```php theme={null}
// 내부에서의 읽기 이미지(간략화)
$reflection = new ReflectionClass($job);
$attributes = $reflection->getAttributes(Queue::class);

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    $job->queue = $instance->queue instanceof UnitEnum
        ? $instance->queue->value
        : $instance->queue;
}
```

Eloquent 모델의 어트리뷰트도 마찬가지로 `Model::booted()`에 해당하는 시점에 리플렉션에 의해 읽힙니다.

## 다음 단계

<Columns cols={2}>
  <Card title="중급: 큐와 잡" icon="layer-group" href="/ko/queues">
    Laravel 큐 시스템의 기본적인 사용법을 배웁니다.
  </Card>

  <Card title="PHP Reflection API" icon="magnifying-glass" href="/ko/advanced/php-reflection">
    Laravel이 어트리뷰트를 읽어 오기 위해 사용하는 Reflection API의 구조를 자세히 설명합니다.
  </Card>
</Columns>


## Related topics

- [PHP Reflection API](/ko/advanced/php-reflection.md)
- [Laravel 13 신기능 정리](/ko/blog/laravel-13-new-features.md)
- [2026년 3월 Laravel 업데이트](/ko/blog/changelog/202603.md)
- [Laravel Pennant 실전 유스케이스](/ko/blog/laravel-pennant.md)
- [Eloquent Observers와 모델 이벤트](/ko/advanced/eloquent-observers.md)
