PHP 어트리뷰트(PHP Attributes)는 PHP 8.0에서 도입된 네이티브 메타데이터 문법입니다. 클래스, 메서드, 프로퍼티, 함수 등에 대해 #[AttributeName] 형식으로 메타 정보를 부여할 수 있습니다.Laravel은 프레임워크 본체에서 PHP 어트리뷰트를 적극적으로 채택하고 있으며, 잡이나 Eloquent 모델의 설정을 선언적으로 작성할 수 있습니다. Laravel 13(v13.2.0)에서는 큐 어트리뷰트가 enum을 받을 수 있게 되었습니다. 기존의 클래스 프로퍼티나 메서드 오버라이드 대신 어트리뷰트를 사용하여 더 읽기 쉽고 간결한 코드를 작성할 수 있습니다.
// 기존 방식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{}
어트리뷰트는 PHP 8.0 이상에서 사용할 수 있습니다. Laravel 13은 PHP 8.3 이상을 요구하기 때문에 모든 환경에서 어트리뷰트를 사용할 수 있습니다.
use Illuminate\Queue\Attributes\Queue;#[Queue('emails')]class SendWelcomeEmail implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle(): void { // ... }}
v13.2.0부터는 문자열 대신 enum을 전달할 수도 있습니다.
enum QueueName: string{ case Emails = 'emails'; case Orders = 'orders'; case Notifications = 'notifications';}#[Queue(QueueName::Emails)]class SendWelcomeEmail implements ShouldQueue{ // ...}
#[Queue] 어트리뷰트는 Attribute::TARGET_CLASS를 대상으로 설정되어 있으므로 클래스에만 적용할 수 있습니다.
use Illuminate\Queue\Attributes\Connection;#[Connection('sqs')]class ProcessPayment implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle(): void { // ... }}
이쪽도 enum을 사용할 수 있습니다.
enum QueueConnection: string{ case Redis = 'redis'; case Sqs = 'sqs'; case Database = 'database';}#[Connection(QueueConnection::Sqs)]class ProcessPayment implements ShouldQueue{ // ...}
잡이 의존하는 Eloquent 모델이 발견되지 않는 경우, 잡을 실패가 아닌 삭제(스킵)로 처리합니다.
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;#[DeleteWhenMissingModels]class SendOrderConfirmation implements ShouldQueue{ public function __construct( protected Order $order, ) {} public function handle(): void { // $order가 존재하지 않는 경우 이 잡이 삭제됨 }}
잡을 직렬화할 때 모델의 릴레이션을 포함하지 않도록 합니다. 큐로 전송되는 데이터를 경량화할 수 있습니다.
use Illuminate\Queue\Attributes\WithoutRelations;#[WithoutRelations]class ExportUser implements ShouldQueue{ public function __construct( protected User $user, ) {}}
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); }}
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'); }}
// 기존에는 메서드 이름에 "scope" 접두사가 필요했음// public function scopeActive(Builder $query): void// 어트리뷰트를 사용하면 메서드 이름이 그대로 스코프 이름이 됨User::query()->active()->verified()->get();
v13.2.0에서는 #[Queue]와 #[Connection]이 enum을 받을 수 있게 되었습니다. 이를 통해 문자열 리터럴 대신 PHP enum을 사용하여 타입 안전하게 큐와 연결을 지정할 수 있습니다.
// 큐 이름 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';}
use Illuminate\Queue\Attributes\Queue;use Illuminate\Queue\Attributes\Connection;// enum을 사용한 타입 안전한 지정#[Queue(Queue::Orders)]#[Connection(Connection::Redis)]class ProcessOrder implements ShouldQueue{ // ...}
enum을 사용하면 큐 이름이나 연결 이름의 오타를 방지하고 IDE 자동 완성도 활용할 수 있습니다. 애플리케이션 전체에서 큐 이름과 연결 이름을 일원화하여 관리하기에 편리합니다.
class ProcessOrder implements ShouldQueue{ // 동적인 백오프는 메서드로 정의 public function backoff(): array { return [ config('queue.backoff.first'), config('queue.backoff.second'), ]; }}
어트리뷰트는 PHP 컴파일 시에 분석됩니다. config()나 env()와 같은 런타임 값을 사용할 수는 없습니다. 동적인 설정이 필요한 경우에는 계속해서 클래스 프로퍼티나 메서드를 사용하세요.
Laravel은 내부적으로 Reflection API를 사용하여 어트리뷰트를 읽어 옵니다. 큐 워커가 잡을 디스패치할 때 ReadsQueueAttributes 트레이트(InteractsWithQueue에 포함되어 있음)가 리플렉션으로 어트리뷰트를 검출하여 해당하는 프로퍼티에 값을 설정합니다.