패키지 개발
소개
패키지는 Laravel에 기능을 추가하는 주요 방법입니다. 패키지는 Carbon과 같이 날짜를 다루는 훌륭한 방법이 될 수도 있고, Spatie의 Laravel Media Library처럼 Eloquent 모델과 파일을 연결할 수 있도록 해주는 패키지가 될 수도 있습니다.
패키지에는 여러 종류가 있습니다. 일부 패키지는 독립형으로, 모든 PHP 프레임워크에서 작동합니다. Carbon과 Pest는 독립형 패키지의 예입니다. 이러한 패키지는 composer.json 파일에 요구사항으로 추가하여 Laravel과 함께 사용할 수 있습니다.
반면에 다른 패키지는 Laravel과 함께 사용하도록 특별히 고안되었습니다. 이러한 패키지에는 라우트, 컨트롤러, 뷰 및 Laravel 애플리케이션을 향상시키기 위한 특정 구성이 있을 수 있습니다. 이 가이드에서는 주로 Laravel 전용 패키지 개발에 대해 다룹니다.
파사드에 대한 참고 사항
Laravel 애플리케이션을 작성할 때, 계약(contracts)을 사용하든 파사드(facades)를 사용하든 테스트 가능성 수준이 거의 동일하므로 일반적으로 중요하지 않습니다. 그러나 패키지를 작성할 때 패키지는 일반적으로 모든 Laravel의 테스트 도우미에 액세스할 수 없습니다. 패키지가 일반적인 Laravel 애플리케이션 내부에 설치된 것처럼 패키지 테스트를 작성하고 싶다면 Orchestral Testbench 패키지를 사용할 수 있습니다.
패키지 디스커버리
Laravel 애플리케이션의 bootstrap/providers.php 파일에는 Laravel에서 로드해야 하는 서비스 제공자 목록이 포함되어 있습니다. 그러나 사용자가 수동으로 서비스 제공자를 목록에 추가하도록 요구하는 대신 패키지의 composer.json 파일의 extra 섹션에 제공자를 정의하여 Laravel에서 자동으로 로드되도록 할 수 있습니다. 서비스 제공자 외에도 등록하려는 파사드를 나열할 수도 있습니다.
"extra": { "laravel": { "providers": [ "Barryvdh\\Debugbar\\ServiceProvider" ], "aliases": { "Debugbar": "Barryvdh\\Debugbar\\Facade" } }},
패키지가 디스커버리를 위해 구성되면, Laravel은 해당 패키지가 설치될 때 자동으로 서비스 제공자 및 파사드를 등록하여 패키지 사용자에게 편리한 설치 경험을 제공합니다.
패키지 디스커버리 옵트 아웃
만약 당신이 패키지의 소비자이고 특정 패키지에 대한 패키지 디스커버리를 비활성화하고 싶다면, 당신의 애플리케이션의 composer.json 파일의 extra 섹션에 패키지 이름을 나열할 수 있습니다:
"extra": { "laravel": { "dont-discover": [ "barryvdh/laravel-debugbar" ] }},
애플리케이션의 dont-discover 지시어 내에 * 문자를 사용하여 모든 패키지에 대한 패키지 디스커버리를 비활성화할 수 있습니다:
"extra": { "laravel": { "dont-discover": [ "*" ] }},
서비스 제공자
서비스 제공자는 패키지와 Laravel 간의 연결점입니다. 서비스 제공자는 Laravel의 서비스 컨테이너에 항목을 바인딩하고 Laravel에 뷰, 구성, 언어 파일과 같은 패키지 리소스를 로드할 위치를 알려주는 역할을 담당합니다.
서비스 제공자는 Illuminate\Support\ServiceProvider 클래스를 확장하며 register와 boot 두 가지 메서드를 포함합니다. 기본 ServiceProvider 클래스는 illuminate/support Composer 패키지에 있으며, 이 패키지를 사용자 패키지의 종속성에 추가해야 합니다. 서비스 제공자의 구조와 목적에 대한 자세한 내용은 해당 문서를 확인하세요.
리소스
구성
일반적으로 패키지 구성 파일을 애플리케이션의 config 디렉토리에 게시해야 합니다. 이렇게 하면 패키지 사용자가 기본 구성 옵션을 쉽게 재정의할 수 있습니다. 구성 파일을 게시할 수 있도록 하려면 서비스 제공자의 boot 메서드에서 publishes 메서드를 호출하세요.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ $this->publishes([ __DIR__.'/../config/courier.php' => config_path('courier.php'), ]);}
이제 패키지 사용자가 Laravel의 vendor:publish 명령을 실행하면 파일이 지정된 게시 위치에 복사됩니다. 구성이 게시되면 다른 구성 파일과 마찬가지로 해당 값에 접근할 수 있습니다.
$value = config('courier.option');
구성 파일에 클로저를 정의하면 안 됩니다. 사용자가 config:cache Artisan 명령을 실행할 때 올바르게 직렬화할 수 없습니다.
기본 패키지 구성
패키지 구성 파일을 애플리케이션의 게시된 복사본과 병합할 수도 있습니다. 이렇게 하면 사용자가 구성 파일의 게시된 복사본에서 실제로 재정의하려는 옵션만 정의할 수 있습니다. 구성 파일 값을 병합하려면 서비스 제공자의 register 메서드 내에서 mergeConfigFrom 메서드를 사용하세요.
mergeConfigFrom 메서드는 패키지 구성 파일의 경로를 첫 번째 인수로, 애플리케이션의 구성 파일 복사본 이름을 두 번째 인수로 받습니다.
/** * 모든 애플리케이션 서비스 등록. */public function register(): void{ $this->mergeConfigFrom( __DIR__.'/../config/courier.php', 'courier' );}
이 메서드는 구성 배열의 첫 번째 수준만 병합합니다. 사용자가 다차원 구성 배열을 부분적으로 정의하면 누락된 옵션은 병합되지 않습니다.
라우트
패키지에 라우트가 포함되어 있으면 loadRoutesFrom 메서드를 사용하여 로드할 수 있습니다. 이 메서드는 애플리케이션의 라우트가 캐시되었는지 여부를 자동으로 확인하고 라우트가 이미 캐시된 경우 라우트 파일을 로드하지 않습니다.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ $this->loadRoutesFrom(__DIR__.'/../routes/web.php');}
마이그레이션
패키지에 데이터베이스 마이그레이션이 포함되어 있으면 publishesMigrations 메서드를 사용하여 Laravel에 주어진 디렉터리 또는 파일에 마이그레이션이 포함되어 있음을 알릴 수 있습니다. Laravel이 마이그레이션을 게시할 때 파일 이름 내의 타임스탬프를 현재 날짜와 시간을 반영하도록 자동으로 업데이트합니다.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ $this->publishesMigrations([ __DIR__.'/../database/migrations' => database_path('migrations'), ]);}
언어 파일
패키지에 언어 파일이 포함되어 있으면 loadTranslationsFrom 메서드를 사용하여 Laravel에 파일을 로드하는 방법을 알릴 수 있습니다. 예를 들어 패키지 이름이 courier인 경우 서비스 제공자의 boot 메서드에 다음을 추가해야 합니다.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');}
패키지 번역 줄은 package::file.line 구문 규칙을 사용하여 참조됩니다. 따라서 다음과 같이 messages 파일에서 courier 패키지의 welcome 줄을 로드할 수 있습니다.
echo trans('courier::messages.welcome');
loadJsonTranslationsFrom 메서드를 사용하여 패키지에 대한 JSON 번역 파일을 등록할 수 있습니다. 이 메서드는 패키지의 JSON 번역 파일이 포함된 디렉토리의 경로를 허용합니다.
/** * 모든 패키지 서비스를 부트스트랩합니다. */public function boot(): void{ $this->loadJsonTranslationsFrom(__DIR__.'/../lang');}
언어 파일 게시
패키지의 언어 파일을 애플리케이션의 lang/vendor 디렉터리에 게시하려면 서비스 제공자의 publishes 메소드를 사용할 수 있습니다. publishes 메소드는 패키지 경로와 원하는 게시 위치의 배열을 받습니다. 예를 들어 courier 패키지의 언어 파일을 게시하려면 다음과 같이 할 수 있습니다.
/** * 모든 패키지 서비스를 부트스트랩합니다. */public function boot(): void{ $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); $this->publishes([ __DIR__.'/../lang' => $this->app->langPath('vendor/courier'), ]);}
이제 패키지 사용자가 Laravel의 vendor:publish Artisan 명령어를 실행하면 패키지의 언어 파일이 지정된 게시 위치에 게시됩니다.
뷰
패키지의 뷰를 Laravel에 등록하려면 뷰가 있는 위치를 Laravel에 알려야 합니다. 서비스 제공자의 loadViewsFrom 메소드를 사용하여 이 작업을 수행할 수 있습니다. loadViewsFrom 메소드는 뷰 템플릿의 경로와 패키지 이름이라는 두 가지 인수를 받습니다. 예를 들어 패키지 이름이 courier인 경우 서비스 제공자의 boot 메소드에 다음을 추가합니다.
/** * 모든 패키지 서비스를 부트스트랩합니다. */public function boot(): void{ $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');}
패키지 뷰는 package::view 구문 규칙을 사용하여 참조됩니다. 따라서 서비스 제공자에 뷰 경로가 등록되면 다음과 같이 courier 패키지에서 dashboard 뷰를 로드할 수 있습니다.
Route::get('/dashboard', function () { return view('courier::dashboard');});
패키지 뷰 오버라이드
loadViewsFrom 메소드를 사용하면 Laravel은 실제로 뷰에 대해 두 개의 위치를 등록합니다. 애플리케이션의 resources/views/vendor 디렉터리와 지정한 디렉터리입니다. 따라서 courier 패키지를 예로 사용하면 Laravel은 먼저 개발자가 resources/views/vendor/courier 디렉터리에 뷰의 사용자 지정 버전을 배치했는지 확인합니다. 그런 다음 뷰가 사용자 지정되지 않은 경우 Laravel은 loadViewsFrom 호출에서 지정한 패키지 뷰 디렉터리를 검색합니다. 이를 통해 패키지 사용자가 패키지의 뷰를 쉽게 사용자 지정/오버라이드할 수 있습니다.
뷰 게시
애플리케이션의 resources/views/vendor 디렉터리에 게시할 수 있도록 뷰를 제공하려면 서비스 제공자의 publishes 메소드를 사용할 수 있습니다. publishes 메소드는 패키지 뷰 경로와 원하는 게시 위치의 배열을 받습니다.
/** * 패키지 서비스를 부트스트랩합니다. */public function boot(): void{ $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); $this->publishes([ __DIR__.'/../resources/views' => resource_path('views/vendor/courier'), ]);}
이제 패키지 사용자가 Laravel의 vendor:publish Artisan 명령어를 실행하면 패키지의 뷰가 지정된 게시 위치에 복사됩니다.
뷰 컴포넌트
Blade 컴포넌트를 활용하거나 비정형 디렉터리에 컴포넌트를 배치하는 패키지를 빌드하는 경우 Laravel이 컴포넌트를 찾을 수 있도록 컴포넌트 클래스와 해당 HTML 태그 별칭을 수동으로 등록해야 합니다. 일반적으로 패키지 서비스 제공자의 boot 메소드에서 컴포넌트를 등록해야 합니다.
use Illuminate\Support\Facades\Blade;use VendorPackage\View\Components\AlertComponent; /** * 패키지 서비스를 부트스트랩합니다. */public function boot(): void{ Blade::component('package-alert', AlertComponent::class);}
컴포넌트가 등록되면 해당 태그 별칭을 사용하여 렌더링할 수 있습니다.
<x-package-alert/>
패키지 컴포넌트 자동 로드
또는, componentNamespace 메소드를 사용하여 규칙에 따라 컴포넌트 클래스를 자동 로드할 수 있습니다. 예를 들어, Nightshade 패키지에 Nightshade\Views\Components 네임스페이스 내에 있는 Calendar 및 ColorPicker 컴포넌트가 있을 수 있습니다.
use Illuminate\Support\Facades\Blade; /** * 패키지의 서비스 부트스트랩. */public function boot(): void{ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');}
이를 통해 package-name:: 구문을 사용하여 공급업체 네임스페이스로 패키지 컴포넌트를 사용할 수 있습니다.
<x-nightshade::calendar /><x-nightshade::color-picker />
Blade는 컴포넌트 이름을 파스칼 케이스로 변환하여 이 컴포넌트에 연결된 클래스를 자동으로 감지합니다. 하위 디렉토리는 "점" 표기법을 사용하여 지원됩니다.
익명 컴포넌트
패키지에 익명 컴포넌트가 포함된 경우, 패키지의 "views" 디렉토리( loadViewsFrom 메소드에서 지정한 대로)의 components 디렉토리 내에 배치해야 합니다. 그런 다음, 컴포넌트 이름에 패키지 뷰 네임스페이스를 접두사로 붙여서 렌더링할 수 있습니다.
<x-courier::alert />
"About" Artisan 명령어
라라벨에 내장된 about Artisan 명령어는 애플리케이션 환경 및 구성에 대한 요약을 제공합니다. 패키지는 AboutCommand 클래스를 통해 이 명령어의 출력에 추가 정보를 푸시할 수 있습니다. 일반적으로 이 정보는 패키지 서비스 제공자의 boot 메소드에서 추가할 수 있습니다.
use Illuminate\Foundation\Console\AboutCommand; /** * 모든 애플리케이션 서비스 부트스트랩. */public function boot(): void{ AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']);}
명령어
패키지의 Artisan 명령어를 라라벨에 등록하려면 commands 메소드를 사용할 수 있습니다. 이 메소드는 명령어 클래스 이름의 배열을 예상합니다. 명령어가 등록되면 Artisan CLI를 사용하여 실행할 수 있습니다.
use Courier\Console\Commands\InstallCommand;use Courier\Console\Commands\NetworkCommand; /** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ if ($this->app->runningInConsole()) { $this->commands([ InstallCommand::class, NetworkCommand::class, ]); }}
최적화 명령어
라라벨의 optimize 명령어는 애플리케이션 구성, 이벤트, 라우트 및 뷰를 캐시합니다. optimizes 메소드를 사용하면 optimize 및 optimize:clear 명령어가 실행될 때 호출되어야 하는 패키지 자체의 Artisan 명령어를 등록할 수 있습니다.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ if ($this->app->runningInConsole()) { $this->optimizes( optimize: 'package:optimize', clear: 'package:clear-optimizations', ); }}
공개 자산
패키지에는 JavaScript, CSS, 이미지와 같은 자산이 있을 수 있습니다. 이러한 자산을 애플리케이션의 public 디렉토리에 게시하려면 서비스 제공자의 publishes 메소드를 사용하십시오. 이 예에서는 public 자산 그룹 태그도 추가합니다. 이 태그는 관련 자산 그룹을 쉽게 게시하는 데 사용할 수 있습니다.
/** * 모든 패키지 서비스 부트스트랩. */public function boot(): void{ $this->publishes([ __DIR__.'/../public' => public_path('vendor/courier'), ], 'public');}
이제 패키지 사용자가 vendor:publish 명령어를 실행하면 자산이 지정된 게시 위치로 복사됩니다. 사용자는 일반적으로 패키지가 업데이트될 때마다 자산을 덮어써야 하므로 --force 플래그를 사용할 수 있습니다.
php artisan vendor:publish --tag=public --force
파일 그룹 게시
패키지 자산 및 리소스 그룹을 개별적으로 게시하고 싶을 수 있습니다. 예를 들어, 패키지 자산을 강제로 게시하지 않고도 사용자가 패키지의 구성 파일을 게시하도록 허용할 수 있습니다. 패키지 서비스 제공자에서 publishes 메서드를 호출할 때 "태그"를 지정하여 이를 수행할 수 있습니다. 예를 들어, 패키지 서비스 제공자의 boot 메서드에서 courier 패키지(courier-config 및 courier-migrations)에 대한 두 개의 게시 그룹을 정의하기 위해 태그를 사용해 보겠습니다.
/** * Bootstrap any package services. */public function boot(): void{ $this->publishes([ __DIR__.'/../config/package.php' => config_path('package.php') ], 'courier-config'); $this->publishesMigrations([ __DIR__.'/../database/migrations/' => database_path('migrations') ], 'courier-migrations');}
이제 사용자는 vendor:publish 명령을 실행할 때 태그를 참조하여 이러한 그룹을 개별적으로 게시할 수 있습니다.
php artisan vendor:publish --tag=courier-config