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

# 패키지 캐시와 optimize 통합

> Laravel 13의 optimizes()를 사용해 패키지 고유의 캐시 생성·삭제를 배포에 통합합니다. 등록 키, 제외 지정, 실행 순서, 실패 시 종료 코드를 구현을 바탕으로 해설합니다.

패키지가 고유한 메타데이터를 미리 생성하는 경우, 사용자에게 배포 절차에 전용 명령어를 추가해 달라고 안내하는 것만으로는 업데이트 시 실행을 빠뜨리기 쉽습니다. `ServiceProvider::optimizes()`를 사용하면 생성과 삭제 명령어를 Laravel의 `optimize`와 `optimize:clear`에 포함시킬 수 있습니다.

이 페이지에서는 [패키지 개발의 기초](/ko/advanced/package-development)를 전제로 Laravel Framework `v13.35.0`의 구현을 살펴봅니다. 캐시 파일의 형식이 아니라 등록과 운영에 관한 계약을 다룹니다.

## 명령어 등록과 태스크 등록을 구분하기

`commands()`는 Artisan에서 호출할 수 있는 명령어 클래스를 등록합니다. `optimizes()`는 이미 실행 가능한 명령어 이름을 최적화 태스크로 등록하는 별개의 처리입니다. 후자만 호출해서는 명령어 클래스가 등록되지 않습니다.

다음 예는 패키지에 `CacheMetadataCommand`와 `ClearMetadataCommand`가 이미 구현되어 있고, 각각의 `$signature`가 `courier:cache`와 `courier:clear-cache`라는 것을 전제로 합니다.

```php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Console\Commands\CacheMetadataCommand;
use Acme\Courier\Console\Commands\ClearMetadataCommand;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                CacheMetadataCommand::class,
                ClearMetadataCommand::class,
            ]);

            $this->optimizes(
                optimize: 'courier:cache',
                clear: 'courier:clear-cache',
                key: 'acme-courier',
            );
        }
    }
}
```

`optimizes()`의 인수는 모두 nullable이므로 생성만, 또는 삭제만 등록할 수도 있습니다. 다만 생성한 캐시를 어떤 절차로 무효화하는지는 반드시 사용자에게 안내하세요.

## 등록 키도 사용자에 대한 계약이 된다

`ServiceProvider`는 생성 명령어를 정적 배열 `$optimizeCommands`에, 삭제 명령어를 `$optimizeClearCommands`에 저장합니다. 두 배열 모두 `key`가 배열 키가 됩니다.

`key`를 생략하면 프로바이더의 클래스 이름에서 이름이 생성됩니다. 예를 들어 `CourierServiceProvider`라면 `courier`가 됩니다. 클래스 이름만 사용하므로, 다른 네임스페이스에 있는 같은 이름의 프로바이더와도 충돌할 수 있습니다.

같은 키로 다시 등록하면 해당 쪽의 명령어가 나중 값으로 덮어써집니다. 여러 태스크를 등록하려면 서로 다른 키를 지정하세요. 또한 `config`나 `routes` 같은 Laravel 표준 태스크의 키는 피합니다. 표준 태스크와 패키지 태스크를 합칠 때도 같은 문자열 키는 덮어써지기 때문입니다.

<Tip>
  `acme-courier`처럼 패키지를 식별할 수 있는 키를 명시하고, 릴리스 간에 유지하세요. 키는 태스크의 표시 이름이 되며, 사용자가 `--except`로 지정하는 값이기도 합니다.
</Tip>

## 표준 태스크 다음에 실행된다

확인한 구현에서는 두 명령어 모두 표준 태스크 배열에 패키지의 등록 배열을 펼쳐 넣은 뒤 순서대로 호출합니다. 충돌하지 않는 키를 사용한 경우, 패키지 태스크는 표준 태스크 뒤에 추가됩니다.

| 명령어 | 표준 태스크의 실행 순서 | 그 이후의 처리 |
| - | - | - |
| `optimize` | `config:cache` → `event:cache` → `route:cache` → `view:cache` | 등록된 생성 명령어 |
| `optimize:clear` | `config:clear` → `cache:clear` → `clear-compiled` → `event:clear` → `route:clear` → `view:clear` | 등록된 삭제 명령어 |

```mermaid theme={null}
flowchart TD
    A["프로바이더의 boot()"] --> B["commands()로 Artisan 명령어 등록"]
    A --> C["optimizes()로 키와 명령어 이름 등록"]
    B --> D["php artisan optimize"]
    C --> D
    D --> E["표준 설정·이벤트·라우트·뷰 캐시"]
    E --> F["courier:cache 실행"]
    C --> G["php artisan optimize:clear"]
    G --> H["표준 캐시 삭제"]
    H --> I["courier:clear-cache 실행"]
```

이 순서를 전제로, 생성 명령어는 다른 표준 캐시를 다시 만들지 말고 패키지가 소유한 데이터만 생성합니다. 여러 패키지 간의 의존 순서를 제어하는 API로 `optimizes()`를 사용하지 말고, 엄밀한 순서가 필요하다면 전용 명령어를 명시적으로 나열하세요.

<Warning>
  `optimize:clear`에는 `cache:clear`도 포함되어 있어 기본 캐시 스토어의 데이터도 삭제합니다. 패키지 전용 캐시만 지우고 싶다면 `courier:clear-cache`를 직접 실행하세요. 패키지의 삭제 명령어 자체도 공유 스토어 전체를 flush하지 않고, 소유한 키나 파일만 삭제하도록 설계합니다.
</Warning>

## 키 또는 명령어 이름으로 제외하기

두 명령어의 `--except`는 쉼표로 구분된 값을 받습니다. 각 값의 앞뒤 공백을 제거한 뒤, 태스크의 키 또는 명령어 이름과 일치하는 것을 제외합니다.

```bash theme={null}
php artisan optimize --except=acme-courier
php artisan optimize --except=courier:cache
php artisan optimize:clear --except=acme-courier,cache
```

처음 두 줄은 같은 생성 태스크를 제외합니다. 세 번째 줄은 패키지의 삭제 태스크와 표준 `cache:clear`를 제외합니다. `cache`는 태스크의 키이며, 패키지 전용 이름이 아닙니다.

제외 지정은 해당 실행에만 적용됩니다. 프로바이더의 등록을 비활성화하거나, 이전에 만든 패키지 캐시를 자동으로 삭제하는 설정이 아닙니다.

## 태스크의 FAIL과 상위 명령어의 종료 코드를 구분하기

`OptimizeCommand`와 `OptimizeClearCommand`는 각 태스크를 `callSilently()`로 호출하고, 종료 코드가 `0`인지 여부를 태스크 표시에 전달합니다. 일반적인 하위 명령어의 출력은 표시되지 않으므로, 원인을 조사할 때는 전용 명령어를 직접 실행합니다.

Laravel `v13.35.0`의 두 `handle()`은 하위 명령어가 0이 아닌 값을 반환해도 그 값을 상위 명령어의 반환값으로 돌려주지 않습니다. 화면에 `FAIL`이 표시되어도 루프는 계속되며, 예외가 없으면 상위 명령어의 종료 코드는 `0`이 됩니다. 반면 던져진 예외는 태스크 표시 컴포넌트에서 다시 던져지므로, 같은 방식으로 계속 진행되지 않습니다.

<Warning>
  `php artisan optimize`의 종료 코드가 `0`이라는 것만으로 패키지 캐시 생성이 성공했다고 판단하지 마세요. 이 동작은 확인한 버전의 구현에 근거하므로, 지원하는 Laravel 버전을 업데이트할 때도 다시 확인합니다.
</Warning>

패키지 캐시 생성이 배포의 필수 조건이라면, 하위 명령어의 종료 코드를 직접 확인할 수 있는 절차로 구성합니다. 예를 들어 다음은 패키지 태스크를 일괄 실행에서 제외하고, 표준 태스크 다음에 한 번만 직접 실행하는 예입니다.

```bash theme={null}
php artisan optimize --except=acme-courier && php artisan courier:cache
```

이 예는 `courier:cache`의 실패를 종료 코드에 반영하지만, 표준 태스크의 0이 아닌 종료를 집계하지는 않습니다. 표준 태스크도 엄밀하게 감지해야 하는 배포라면, 필요한 명령어를 개별적으로 실행해 종료 코드를 확인하세요.

## 업데이트에 견디는 캐시 설계와 확인

등록뿐 아니라 명령어와 읽기 측의 책임도 정해 둡니다.

* 생성은 반복 실행해도 같은 입력에서 같은 상태가 되며, 도중에 실패해도 불완전한 데이터를 유효하게 만들지 않는다.
* 삭제는 캐시가 존재하지 않는 경우에도 정상적으로 완료되며, 사용자가 배포한 설정이나 영속 데이터를 삭제하지 않는다.
* 생성에 실패한 명령어는 오류를 보고하고 0이 아닌 값을 반환한다. 읽기 측도 손상된 캐시를 무조건 정상으로 취급하지 않는다.
* 캐시 형식을 변경하면 사용자에게 재생성이 필요하다는 것을 안내하고, 장시간 실행되는 프로세스의 재시작도 검토한다.

패키지 테스트와 더불어, 패키지를 사용하는 애플리케이션에서는 다음을 확인합니다. 등록 배열은 정적이므로, 같은 프로세스 내의 테스트 간에 등록 상태가 이어지지 않는지도 주의합니다.

| 확인할 작업 | 완료 조건 |
| - | - |
| 전용 생성·삭제 명령어를 직접 실행 | 정상 시 `0`, 생성 실패 시 0이 아닌 값. 삭제를 두 번 실행해도 성공한다 |
| `optimize` / `optimize:clear` 실행 | 패키지 태스크가 한 번씩 호출되고, 생성·삭제 후의 상태가 올바르다 |
| 키와 명령어 이름으로 `--except` 지정 | 대상 태스크만 실행되지 않는다 |
| 생성 명령어를 0이 아닌 값으로 종료시키기 | `FAIL` 표시와, 확인 대상 버전에서 상위 명령어의 종료 코드를 구분할 수 있다 |
| 패키지 업데이트 후 재생성 | 새 코드와 설정으로 생성되며, 이전 형식을 계속 읽지 않는다 |

## 관련 페이지

<Columns cols={2}>
  <Card title="패키지 설정 병합과 캐시" icon="sliders" href="/ko/advanced/package-config-merging">
    배포된 설정의 보완과 설정 캐시 재구축의 관계를 확인합니다.
  </Card>

  <Card title="Orchestra Testbench로 Laravel 패키지를 테스트하기" icon="flask" href="/ko/advanced/package-testing">
    프로바이더와 Artisan 명령어를 테스트 환경에 등록합니다.
  </Card>
</Columns>

## 참조한 1차 자료

* [Laravel 공식 문서: Optimize commands](https://github.com/laravel/docs/blob/13.x/packages.md#optimize-commands)
* [ServiceProvider: optimizes()와 등록 키](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [OptimizeCommand: 태스크와 제외 처리](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeCommand.php)
* [OptimizeClearCommand: 삭제 태스크와 실행 순서](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/OptimizeClearCommand.php)
* [Command: handle()의 반환값과 종료 코드](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Task: 결과 표시와 예외 재발생](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/View/Components/Task.php)


## Related topics

- [Laravel 패키지 개발](/ko/advanced/package-development.md)
- [고급 주제](/ko/advanced/index.md)
- [캐시](/ko/cache.md)
- [패키지 뷰의 덮어쓰기와 업데이트](/ko/advanced/package-views.md)
- [패키지 자동 감지의 내부 구조](/ko/advanced/package-discovery.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.