이 페이지에 대해
패키지 개발 기초에서는composer.json의 extra.laravel 섹션을 작성하면 서비스 프로바이더와 파사드가 자동 등록된다는 점을 소개했습니다. 이 페이지에서는 그 이면, 즉 Illuminate\Foundation\PackageManifest 클래스가 어떻게 동작하는지를 소스코드 수준에서 설명합니다.
이 페이지는 패키지 개발 기초의 자매 페이지입니다. 자동 감지의 기본적인 사용법을 먼저 읽어 보시길 권장합니다.
자동 감지의 전체 흐름
PackageManifest 클래스
자동 감지의 중심은 Illuminate\Foundation\PackageManifest입니다. 다음은 프레임워크 13.x 기준의 구현(요약)입니다.
- 매니페스트는 한 번 로드되면 메모리에 캐시된다 (
$this->manifest프로퍼티). 한 요청 내에서providers()를 여러 번 호출해도 파일 I/O는 한 번만 발생합니다. - 매니페스트 파일이 존재하지 않는 경우에만
build()가 실행된다. 일반 운영 시에는 매번 빌드되지 않습니다. - 실체는 소박한 PHP 배열을
return하는 파일(bootstrap/cache/packages.php)로,require만으로 로드할 수 있는 가장 빠른 형식입니다.
매니페스트 빌드 처리
build() 메서드가 실제로 composer.json 정보를 집약하는 부분입니다.
composer.json을 직접 파싱하는 것이 아니라 vendor/composer/installed.json 을 읽는다는 것입니다. 이는 Composer가 composer install / composer update 실행 시 생성하는, 설치된 모든 패키지의 메타데이터 파일입니다. 각 패키지의 composer.json에 작성된 extra 섹션이 여기에 집약되므로 Laravel 쪽은 Composer 관리하의 정보만 신뢰하여 읽습니다.
installed.json은 일반적으로 .gitignore되므로, 초기 설정 시(vendor/가 없는 상태)에는 자동 감지가 작동하지 않습니다. composer install이 완료된 후에야 캐시가 구축됩니다.dont-discover의 두 가지 작성 방식
dont-discover는 패키지 측과 애플리케이션 측 어느 composer.json에도 작성할 수 있지만, 의미가 다릅니다.
패키지 측 composer.json
애플리케이션 측 composer.json
build()의 구현을 보면 $ignore 배열은 각 패키지의 configuration['dont-discover']를 array_merge하여 축적하고 있습니다. 즉 패키지 자신이 “자신의 의존 패키지의 자동 감지를 비활성화”하는 것도 기술적으로는 가능합니다(예: 내부에서 사용하는 서브 패키지의 프로바이더를 중복 등록시키고 싶지 않은 경우). 다만 실무에서 자주 사용되는 것은 애플리케이션 측에서의 비활성화입니다.
dont-discover에 * 지정하기
packagesToIgnore()가 반환하는 배열에 *가 포함되면 $ignoreAll = true가 되어 모든 패키지의 자동 감지가 통째로 비활성화됩니다. CI나 테스트 환경에서 자동 감지의 오버헤드를 피하고 싶은 경우나 bootstrap/providers.php에서 완전히 수동 관리하고 싶은 경우에 사용합니다.
캐시 파일의 실체
getCachedPackagesPath()는 환경 변수 APP_PACKAGES_CACHE가 있으면 그것을, 없으면 bootstrap/cache/packages.php를 반환합니다.
return하고 있음을 알 수 있습니다.
php artisan package:discover의 출력에서 “어떤 패키지가 감지되었는지” 확인할 수 있습니다.
캐시가 재구축되는 시점
Illuminate\Foundation\ComposerScripts는 3개의 Composer 이벤트에 훅을 걸고 있으며, 모두 같은 clearCompiled()를 호출합니다.
composer install / composer update / composer dump-autoload 중 어느 것을 실행해도 설정 캐시·서비스 캐시·패키지 캐시가 모두 삭제됩니다. 다음에 Laravel이 시작되는 시점에 PackageManifest::build()가 실행되어 installed.json의 최신 상태에서 재구축됩니다.
이는 composer.json의 scripts에 등록된, 표준적인 Laravel 프로젝트의 동작입니다.
Laravel 앱의 composer.json (발췌)
php artisan package:discover로 수동 재구축
캐시가 오래된 상태로 남아 있거나, Composer를 거치지 않고 vendor/를 직접 변경한 경우에는 package:discover 명령으로 수동 재구축할 수 있습니다.
$manifest->build()를 호출하여 결과를 출력할 뿐이며, 명령 고유의 로직은 거의 없습니다. CI 파이프라인에서 composer install --no-scripts를 사용하는 등, Composer 이벤트가 발생하지 않는 상황에서는 이 명령을 명시적으로 호출할 필요가 있습니다.
배포 시 주의점
프로덕션 배포에서는composer install --no-dev --optimize-autoloader를 실행하는 것이 일반적이지만, --no-scripts를 지정한 경우에는 패키지 캐시가 업데이트되지 않습니다. 배포 스크립트에 명시적인 재구축 단계를 넣어두면 안전합니다.
package:discover는 config:cache보다 먼저 실행하세요. 설정 캐시가 먼저 만들어지면 나중에 추가된 패키지 설정(mergeConfigFrom으로 등록되는 설정 등)이 반영되지 않을 수 있습니다.
정리
- 자동 감지는
composer.json의 정적 내용이 아니라, Composer가 생성한vendor/composer/installed.json을 읽어 동작한다. - 결과는
bootstrap/cache/packages.php에 순수한 PHP 배열로 캐시되며,require만으로 빠르게 로드된다. - 캐시는
composer install/update/dump-autoload의 각 이벤트에서 자동으로 삭제되고, 다음 시작 시 재구축된다. --no-scripts로 Composer를 실행하는 환경에서는php artisan package:discover를 명시적으로 호출할 필요가 있다.dont-discover에*를 지정하면 자동 감지 전체를 비활성화할 수 있다.
관련 페이지
- 패키지 개발 기초 — 서비스 프로바이더와
extra.laravel의 기본 사용법 - 지연 서비스 프로바이더 — 감지된 프로바이더의 로드 타이밍 최적화
- Orchestra Testbench에서 패키지 테스트 — Testbench 고유의
package:discover명령