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

# 패키지 설정 병합과 캐시

> Laravel 13의 ServiceProvider 구현을 바탕으로 얕은 병합과 재귀적 치환의 차이, 숫자 키 배열의 함정, 설정 캐시를 고려한 패키지 유지보수 방법을 해설합니다.

패키지에 설정을 추가해도, 사용자가 이전에 배포한 설정 파일에 새 항목이 자동으로 기록되지는 않습니다. 배포된 설정을 망가뜨리지 않고 기본값을 보완하려면 배열의 병합 방침과 설정 캐시를 모두 설계해야 합니다.

이 페이지에서는 Laravel 13의 `ServiceProvider`를 살펴보고, 설정을 패키지의 공개 API로서 유지보수하는 방법을 정리합니다. [패키지 개발의 기초](/ko/advanced/package-development)를 전제로 하며, 구현 확인에는 `laravel/framework`의 `v13.34.0`을 사용했습니다.

## 배포와 병합은 별개의 처리

`publishes()`는 복사 원본과 복사 대상을 등록합니다. `vendor:publish`가 파일을 복사하기 전까지 사용자의 `config` 디렉터리는 바뀌지 않습니다. 반면 `mergeConfigFrom()`은 시작 시 설정 저장소를 갱신할 뿐, 파일 자체를 다시 쓰지 않습니다.

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

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(
            __DIR__.'/../config/courier.php', 'courier'
        );
    }

    public function boot(): void
    {
        $this->publishes([
            __DIR__.'/../config/courier.php' => config_path('courier.php'),
        ], 'courier-config');
    }
}
```

사용자는 필요할 때만 설정 파일을 배포합니다. 배포하지 않더라도 캐시가 없는 일반적인 시작에서는 `register()`의 병합으로 기본값이 사용됩니다.

```bash theme={null}
php artisan vendor:publish --tag=courier-config
```

<Warning>
  업데이트 절차로서 설정 파일을 `--force`로 다시 배포하면 사용자의 수정 내용을 덮어씁니다. 새 설정 항목을 추가하기만 한다면 기본값 보완과 변경 사항 안내를 우선하세요.
</Warning>

## mergeConfigFrom()은 최상위만 병합한다

`ServiceProvider::mergeConfigFrom()`은 패키지 설정을 먼저, 애플리케이션의 기존 설정을 나중에 전달하여 `array_merge()`를 실행합니다. 같은 문자열 키에서는 애플리케이션 쪽 값이 우선합니다.

다음은 프레임워크의 병합 처리를 배열만으로 재현한 예입니다.

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

$defaults = [
    'enabled' => true,
    'transport' => [
        'timeout' => 10,
        'retries' => 3,
    ],
];

$overrides = [
    'transport' => [
        'timeout' => 30,
    ],
];

$config = array_merge($defaults, $overrides);

var_export($config);
```

```text theme={null}
array (
  'enabled' => true,
  'transport' =>
  array (
    'timeout' => 30,
  ),
)
```

최상위의 `enabled`는 보완되지만, `transport`는 배열 전체가 교체됩니다. `transport.retries`는 남지 않습니다. 사용자가 예전 `transport` 배열을 이미 배포했다면, 같은 배열에 새 키를 추가해도 보완되지 않는다는 점이 중요합니다.

## replaceConfigRecursivelyFrom()으로 중첩된 설정 보완하기

Laravel 13의 `ServiceProvider`에는 protected 메서드인 `replaceConfigRecursivelyFrom()`도 있습니다. 이 메서드는 같은 순서로 `array_replace_recursive()`를 실행합니다.

중첩된 문자열 키를 개별적으로 덮어쓸 수 있는 설정 API로 만들고 싶다면, 프로바이더의 `register()`를 다음과 같이 변경하세요. 같은 설정 키에 대해 앞의 `mergeConfigFrom()`과 함께 사용할 필요는 없습니다.

```php theme={null}
public function register(): void
{
    $this->replaceConfigRecursivelyFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}
```

앞의 `$defaults`와 `$overrides`를 사용하면 결과는 다음과 같습니다.

```php theme={null}
$config = array_replace_recursive($defaults, $overrides);

var_export($config);
```

```text theme={null}
array (
  'enabled' => true,
  'transport' =>
  array (
    'timeout' => 30,
    'retries' => 3,
  ),
)
```

| 설정 계약 | 선택할 처리 | 주의할 점 |
| - | - | - |
| 중첩된 배열은 사용자가 전체를 지정한다 | `mergeConfigFrom()` | 배열 안의 지정되지 않은 키는 보완되지 않는다 |
| 중첩된 항목은 사용자가 일부만 지정한다 | `replaceConfigRecursivelyFrom()` | 숫자 키 리스트도 재귀적 치환의 대상이 된다 |

<Info>
  `replaceConfigRecursivelyFrom()`은 이 페이지에서 확인한 Laravel 13 소스 코드에 존재하는 메서드입니다. 공식 패키지 개발 문서에서 소개하는 `mergeConfigFrom()`과 구분하고, 대응하는 프레임워크 구현을 확인한 뒤 사용하세요.
</Info>

### 숫자 키 리스트는 전체 교체되지 않는다

재귀적 치환은 "배열을 모두 사용자의 값으로 바꾸는" 처리가 아닙니다. 숫자 키라도 같은 키의 값을 교체하고, 사용자가 지정하지 않은 키는 남겨 둡니다.

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

$defaults = ['channels' => ['mail', 'database']];
$overrides = ['channels' => ['slack']];

$config = array_replace_recursive($defaults, $overrides);

var_export($config['channels']);
```

```text theme={null}
array (
  0 => 'slack',
  1 => 'database',
)
```

사용자가 `['slack']`만 지정해도 `database`가 남습니다. 또한 `['channels' => []]`를 전달해도 기본 리스트는 비워지지 않습니다. 알림 대상이나 미들웨어처럼 리스트 전체를 지정하는 것이 의미 있는 설정에서는 특히 주의하세요.

이런 설정이 있다면 리스트와 부분적으로 덮어쓸 수 있는 연관 배열을 서로 다른 최상위 키로 나누고 얕은 병합을 사용하는 등, 설정 구조부터 검토하세요. 이미 공개한 패키지에서 병합 방식을 바꾸면 같은 설정 파일이라도 동작이 달라지므로, 단순한 구현 교체로 취급하지 마세요.

## 설정 캐시에는 병합 후의 값이 저장된다

두 메서드 모두 애플리케이션이 `CachesConfiguration`을 구현하고 `configurationIsCached()`가 `true`인 경우에는 병합 처리를 건너뜁니다. 일반적인 Laravel 애플리케이션에서는 설정 캐시가 존재하는 상태의 시작이 여기에 해당합니다.

`ConfigCacheCommand`는 오래된 설정 캐시를 삭제하고, 새 애플리케이션을 시작해 설정 저장소 전체를 가져옵니다. 그 시작 과정에서 프로바이더의 설정이 병합되고, 결과가 캐시 파일에 저장됩니다. 이후의 시작에서는 `LoadConfiguration`이 그 값을 읽어 들입니다.

```mermaid theme={null}
flowchart TD
    A["php artisan config:cache"] --> B["오래된 설정 캐시 삭제"]
    B --> C["새 애플리케이션 시작"]
    C --> D["설정 파일 읽기<br>프로바이더에서 병합"]
    D --> E["설정 저장소 전체 저장"]
    E --> F["이후 시작에서는 저장된 설정 사용<br>두 병합 메서드는 건너뜀"]
```

따라서 패키지를 업데이트해 기본값이나 병합 방식이 바뀌어도, 오래된 캐시를 계속 사용하는 애플리케이션에는 반영되지 않습니다. 설정 캐시를 사용하는 배포 환경에서는 업데이트된 코드로 캐시를 다시 생성하세요.

```bash theme={null}
php artisan config:cache
```

개발 중에 파일에서 다시 읽는 상태로 되돌리고 싶다면 `php artisan config:clear`를 사용하세요. 캐시 생성 위치를 직접 정하지 말고, Laravel 명령어에 관리를 맡기세요.

<Warning>
  설정 파일에 Closure를 정의하지 마세요. `config:cache`에서 올바르게 직렬화할 수 없습니다. 콜백을 전달하고 싶다면 설정에는 클래스 이름 등을 두고, 실제 서비스 등록은 프로바이더에서 수행하세요.
</Warning>

## 설정 변경을 릴리스하기 전 확인 사항

패키지 테스트에서는 배포되지 않은 설정뿐 아니라, 이전 버전에서 남아 있는 설정도 입력으로 다룹니다.

* 설정을 배포하지 않아도 필요한 기본값을 가져올 수 있다.
* 오래된 배포 설정에서 사용자의 값이 우선하고, 새 항목이 설계대로 보완된다.
* 중첩된 배열, 숫자 키 리스트, 빈 배열에 대해 덮어쓰기 계약이 바뀌지 않는다.
* 사용하는 애플리케이션에서 `config:cache`가 성공하고, 캐시를 사용하는 다른 시작에서도 같은 설정이 된다.

새 키의 기본값과 필요한 캐시 재생성은 릴리스 노트에 기재하세요. 키의 삭제·이름 변경이나 병합 방식 변경은 사용자의 배포된 설정과의 호환성까지 포함해 판단하세요.

## 관련 페이지

<Columns cols={2}>
  <Card title="패키지 테스트" icon="flask" href="/ko/advanced/package-testing">
    서비스 프로바이더를 등록하고 설정과 서비스의 동작을 검증합니다.
  </Card>

  <Card title="버전 호환성 관리" icon="code-branch" href="/ko/advanced/package-versioning">
    공개 API의 변경을 릴리스 방침과 지속적인 유지보수에 연결합니다.
  </Card>
</Columns>

## 참고한 1차 자료

* [Laravel 공식 문서: 패키지 설정](https://github.com/laravel/docs/blob/13.x/packages.md#configuration)
* [ServiceProvider: 병합과 배포 구현](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [ConfigCacheCommand: 설정 캐시 생성](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ConfigCacheCommand.php)
* [LoadConfiguration: 설정 읽기](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php)
* [ConfigClearCommand: 설정 캐시 삭제](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ConfigClearCommand.php)


## Related topics

- [Laravel 패키지 개발](/ko/advanced/package-development.md)
- [고급 주제](/ko/advanced/index.md)
- [캐시](/ko/cache.md)
- [패키지 자동 감지의 내부 구조](/ko/advanced/package-discovery.md)
- [지연 서비스 프로바이더](/ko/advanced/deferred-provider.md)


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