메인 콘텐츠로 건너뛰기
패키지 사용자가 서비스 프로바이더에 설정 키를 나열하지 않고도 필요한 값을 주입할 수 있게 하고 싶을 때가 있습니다. 컨텍스트 속성을 공개하면 인수에 붙인 메타데이터로부터 패키지 고유의 설정이나 서비스를 해석할 수 있습니다. 이 페이지에서는 Laravel Framework v13.39.0의 공식 소스를 확인하고 courier 설정을 읽는 독자적인 속성을 구현합니다. 내장된 #[Config('courier.timeout')]만으로 목적을 달성할 수 있다면 독자적인 속성을 늘릴 필요는 없습니다. 독자적인 키 규칙이나 해석 방침을 패키지의 API로 제공하는 경우에 검토합니다.

속성의 역할은 주입할 값을 결정하는 것

Illuminate\Contracts\Container\ContextualAttribute를 구현한 속성은 Laravel의 컨테이너가 인수를 해석할 때 읽습니다. 속성 객체 자체가 아니라 resolver가 반환한 값이 사용 클래스에 전달됩니다. 일반적인 바인딩과 내장 속성은 서비스 컨테이너를 참조하세요. 컨텍스트 속성은 Bind·Singleton·Scoped처럼 클래스나 인터페이스의 바인딩을 선언하는 속성과는 별개의 구조입니다.

패키지 설정을 읽는 속성 만들기

기본 설정 등록하기

먼저 패키지의 config/courier.php에 기본값을 준비합니다.
src/CourierServiceProvider.php의 register()에서 설정을 병합합니다. 프로바이더 자동 등록은 패키지 자동 감지의 내부 구조를, 공개와 설정 캐시 처리는 패키지 설정 병합과 캐시를 참조하세요.

ContextualAttribute 구현하기

src/Attributes/CourierConfig.php에서 설정 키에 courier.를 붙이는 속성을 정의합니다.
확인한 구현에서는 resolver에 다음 3개의 인수가 전달됩니다.
  1. 인수에 붙은 속성의 인스턴스.
  2. 의존성을 해석하고 있는 컨테이너.
  3. 해석 대상을 나타내는 ReflectionParameter.
이 예에서는 키를 생략하면 인수 이름을 사용합니다. #[CourierConfig(default: 30)] int $timeout은 courier.timeout을 참조합니다. 사용 측의 인수 이름을 바꿔도 같은 설정을 읽고 싶다면 #[CourierConfig('timeout', default: 30)]처럼 키를 명시합니다. ContextualAttribute는 확인 시점에는 메서드를 선언하지 않는 마커 인터페이스입니다. 따라서 implements만으로는 resolver의 존재나 시그니처가 보장되지 않습니다. 위와 같이 resolve()를 준비하면 이 속성을 위해 일반적인 bind()를 등록할 필요가 없습니다.
속성의 생성자는 메타데이터를 보관하는 용도로만 사용합니다. 컨테이너는 해석 시와 속성 후처리 시에 각각 newInstance()를 호출하므로 “한 번만 실행하는 초기화”를 위한 장소가 아닙니다. 외부 통신이나 변경 가능한 상태를 갖게 하지 마세요.

컨테이너에서 사용 클래스 생성하기

src/CourierOptions.php에 속성을 붙입니다.
사용하는 애플리케이션에서 설정을 덮어쓰면 다음 해석 시에 그 값이 주입됩니다.
이 예에서 $options->timeout은 45, $options->retries는 3입니다. 속성은 설정을 가져올 뿐이며, 양의 정수인지, 허용 최댓값 이내인지 같은 업무상의 검증은 하지 않습니다. 타입 선언도 설정값 검증 처리를 대신하지 않습니다.

생성자에서의 해석 순서 읽기

Container::resolveDependencies()는 각 인수에 대해 다음 순서로 값을 결정합니다.
  1. makeWith() 등으로 전달된, 인수 이름과 일치하는 덮어쓰기 값을 사용한다.
  2. Util::getContextualAttributeFromDependency()로 컨텍스트 속성을 찾아 해석한다.
  3. 속성의 결과가 null이면 일반적인 프리미티브 값 또는 클래스 해석으로 진행한다.
  4. 속성 후처리를 거쳐 결정된 값을 생성자에 전달한다.
1의 덮어쓰기 경로는 해당 인수의 속성 해석과 후처리를 건너뜁니다. makeWith()의 키는 설정 키가 아니라 사용 클래스의 인수 이름입니다.
이 경우 timeout은 설정값과 관계없이 5입니다. 한편 속성이 null 이외의 값을 반환하면 일반적인 when()->needs('$timeout')->give(...)보다 속성의 결과가 우선합니다. 패키지에 속성을 추가하는 것이 기존 사용자의 컨텍스트 바인딩을 바꿀 가능성도 테스트합니다. Util은 ContextualAttribute를 구현한 속성을 Reflection으로 가져와 그중 첫 번째 1건을 해석용으로 선택합니다. 서로 다른 컨텍스트 속성을 여러 개 나열해도 resolver가 순서대로 합성되는 것은 아닙니다.

등록된 handler는 resolve()보다 우선한다

Container::resolveFromAttribute()는 속성 클래스 이름을 키로 하여 whenHasAttribute()로 등록된 handler를 먼저 찾습니다. 없으면 속성의 resolve()를 사용하고, 둘 다 없으면 BindingResolutionException을 던집니다. whenHasAttribute()는 사용 클래스별이 아니라 속성 종류별 설정입니다. 같은 속성 클래스에 다시 등록하면 이전 handler를 덮어씁니다. 패키지가 다른 패키지나 Laravel 내장 속성의 handler를 무조건 변경하면 사용하는 애플리케이션 전체에 영향을 줍니다. 독자적인 속성의 표준 동작은 resolve()에 두고, handler에 의한 변경은 명시적인 확장 지점으로 다룹니다.

make()·call()·직접 호출은 같지 않다

app()->call()도 속성을 해석하지만, 일반적인 BoundMethod 경로는 속성을 찾으면 그 결과를 그대로 인수에 추가합니다. 생성자 해석과 달리 속성의 결과가 null이어도 기본값 해석으로 돌아가지 않습니다. 다른 컨텍스트 바인딩이 없는 상태에서 존재하지 않는 courier.missing을 읽는 예입니다.
new CourierOptions(timeout: 5, retries: 1)도 일반적인 PHP에 의한 생성입니다. 속성을 붙이는 것만으로는 직접적인 new나 메서드 호출에 의존성 주입이 추가되지 않습니다.
생성자와 callable 양쪽에서 같은 기본 동작이 필요하다면 인수의 기본값에만 의존하지 말고 #[CourierConfig('timeout', default: 30)]처럼 속성 측의 기본값을 정의합니다. 이 default는 설정 키가 존재하지 않을 때 사용되며, 존재하는 키의 값이 null인 경우에는 대체하지 않습니다. null 자체를 의미 있는 값으로 다루는 API에서는 두 해석 경로를 각각 검증하세요.

값의 재주입과 서비스 수명 구분하기

속성은 인수를 해석하는 시점의 설정을 읽습니다. 이미 생성된 CourierOptions의 프로퍼티를 감시하거나 갱신하는 기능이 아닙니다. 예를 들어 CourierOptions를 singleton으로 하면 처음 주입된 값이 그 인스턴스에 보관됩니다. 이후 설정을 변경하고 일반적인 make()를 호출해도 같은 인스턴스의 값은 바뀌지 않습니다. 요청별 정보를 읽는 속성을 만드는 경우에도 속성 자체가 singleton이나 scoped의 수명을 결정하는 것은 아닙니다. 주입된 값이나 객체를 보관하는 서비스의 수명을 패키지 서비스의 라이프사이클 설계에 따라 선택합니다. 설정 캐시의 재생성과, 수명이 긴 프로세스나 생성된 서비스의 갱신도 구분해서 생각합니다.

해석 경로 테스트하기

속성 단위 테스트는 공식 테스트와 마찬가지로 새 Container와 Illuminate\Config\Repository를 사용해 작성할 수 있습니다. 다음 PHPUnit 예는 위에서 정의한 CourierConfig·CourierOptions·NullableTimeoutOptions를 불러온 테스트 환경을 전제로 합니다.
handler가 항상 7을 반환하게 하는 예는 우선순위 테스트 전용입니다. 이 컨테이너 안에서는 retries를 포함한 같은 속성의 모든 해석에 영향을 줍니다. 이 단위 테스트만으로는 패키지의 프로바이더 등록이나 mergeConfigFrom(), 설정 캐시와의 통합까지는 확인할 수 없습니다. 패키지 테스트의 애플리케이션 환경에서도 공개 전·공개 후·설정 캐시가 있는 상태를 검증합니다.

릴리스 전 확인 항목

  • 속성의 클래스 이름, 생성자 인수 이름, 설정 키, 반환 타입을 공개 API로 다룬다. 이름 있는 인수로 사용되는 key·default의 변경에도 주의한다.
  • 키를 생략하면 인수 이름을 사용하는 사양에서는 사용 클래스의 인수 이름 변경이 참조하는 설정을 바꾸지 않는지 확인한다.
  • 속성이 없는 기존 바인딩, 속성이 붙은 해석, 인수 이름을 통한 덮어쓰기를 각각 테스트한다.
  • null, 설정 누락, 부적절한 타입을 make()와 call() 양쪽에서 테스트한다.
  • singleton이나 수명이 긴 워커가 오래된 값을 보관하지 않는 설계인지 확인한다.
  • Laravel 지원 버전을 갱신할 때는 resolver의 인수, 우선순위, 속성 후처리를 공식 소스와 테스트로 다시 확인한다.

관련 페이지

패키지 설정 병합과 캐시

기본 설정, 공개된 설정, 캐시의 갱신 경계를 확인합니다.

패키지 서비스의 라이프사이클 설계

주입한 값을 보관하는 서비스의 수명을 선택합니다.

패키지의 컨테이너 태그와 플러그인 집약

내장 Tag 속성과 등록된 구현의 집약을 확인합니다.

PHP Reflection API

속성과 인수의 메타데이터를 읽어 들이는 구조를 확인합니다.

참조한 1차 정보

마지막 수정일 2026년 10월 31일