> ## 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의 구현을 바탕으로 네임스페이스가 지정된 뷰의 검색 순서, 배포된 Blade 템플릿의 유지보수, 뷰 캐시와 검색 결과 캐시의 차이를 해설합니다.

사용자가 화면이나 메일 템플릿을 커스터마이즈할 수 있는 패키지에서는 뷰를 배포하는 것만으로는 부족하며, 배포된 파일을 유지한 채로 업데이트할 수 있는 계약이 필요합니다. 패키지 쪽 Blade 파일을 수정하더라도 사용하는 애플리케이션이 그 파일을 렌더링하고 있다고는 단정할 수 없습니다.

이 페이지에서는 [Laravel 패키지 개발](/ko/advanced/package-development)을 전제로 뷰의 선택, 파일 배포, 캐시를 나누어 생각합니다. 공식 문서는 Laravel 13, 프레임워크 구현은 최신 릴리스인 `v13.34.0`을 참조합니다.

## 등록과 배포는 별개의 처리

`loadViewsFrom()`은 네임스페이스에 검색 경로를 등록합니다. `publishes()`는 복사 원본과 복사 대상을 등록하며, 실제 복사는 `vendor:publish`가 수행합니다. 다음 예시에서는 배포하지 않아도 `courier::deliveries.show`를 사용할 수 있습니다.

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');

        $this->publishes([
            __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
        ], 'courier-views');
    }
}
```

패키지 쪽 파일은 `resources/views/deliveries/show.blade.php`에 둡니다. 뷰 이름의 점은 검색 시 디렉터리 구분자로 변환됩니다.

```php theme={null}
return view('courier::deliveries.show', [
    'trackingCode' => 'TRACK-001',
]);
```

```blade resources/views/deliveries/show.blade.php theme={null}
<p>추적 번호: {{ $trackingCode }}</p>
```

뷰의 네임스페이스는 Composer 패키지 이름이나 PHP 네임스페이스와는 별개입니다. 여기서는 `loadViewsFrom()`의 두 번째 인수로 지정한 `courier`가 뷰 참조와 덮어쓰기 대상 디렉터리의 계약이 됩니다.

## 파일 단위로 덮어쓰기 대상을 찾는다

`ServiceProvider::loadViewsFrom()`은 `view`가 해석될 때 설정의 `view.paths`를 순서대로 확인합니다. 각 경로에 `vendor/courier` 디렉터리가 존재하면 해당 디렉터리를 네임스페이스에 추가하고, 마지막으로 패키지 쪽 경로를 추가합니다.

`FileViewFinder`는 해당 네임스페이스의 경로를 순서대로 검색하여 처음 발견한 파일을 반환합니다. 기본 `resources/views`를 사용하는 구성에서는 다음 순서입니다.

```mermaid theme={null}
flowchart TD
    A["courier::deliveries.show"] --> B["resources/views/vendor/courier/<br>deliveries/show.blade.php를 검색"]
    B --> C{"파일이 있는가?"}
    C -->|예| D["사용하는 애플리케이션의 뷰를 사용"]
    C -->|아니요| E["패키지의 resources/views/<br>deliveries/show.blade.php를 검색"]
    E --> F{"파일이 있는가?"}
    F -->|예| G["패키지의 뷰를 사용"]
    F -->|아니요| H["뷰를 찾을 수 없다는 예외"]
```

이는 디렉터리 전체를 전환하는 것이 아닙니다. 사용자가 `deliveries/show.blade.php`만 덮어써도, 덮어쓰지 않은 다른 뷰는 패키지 쪽에서 로드됩니다.

| 사용하는 애플리케이션의 상태 | 선택되는 뷰 |
| - | - |
| 덮어쓰기 파일이 없다 | 패키지의 파일 |
| 같은 상대 경로의 덮어쓰기 파일이 있다 | 사용하는 애플리케이션의 파일 |
| 덮어쓰기 파일만 제거했다 | 새로 기동하면 패키지의 파일로 돌아간다 |
| 양쪽 모두 대상 파일이 없다 | `View [...] not found.` 예외 |

<Info>
  `view.paths`가 여러 개인 구성에서는 덮어쓰기 대상도 여러 곳이 될 수 있습니다. `resource_path('views/vendor/courier')`는 이 예시의 배포 대상일 뿐이며, 검색 대상을 그곳으로만 고정하는 처리가 아닙니다. 네임스페이스는 패키지 고유의 것으로 하고, 여러 프로바이더에서 같은 이름에 경로를 추가하는 설계는 피하세요.
</Info>

## 필요한 뷰만 커스터마이즈하기

사용자는 다음 명령어로 템플릿을 복사할 수 있습니다. 프로바이더와 태그를 지정하여 다른 리소스까지 함께 복사되지 않도록 합니다.

```bash theme={null}
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider" --tag=courier-views
```

이 등록에서는 뷰 디렉터리 전체가 배포됩니다. 모두 덮어쓸 필요가 없다면 내용을 확인하고 커스터마이즈할 파일만 남기거나, 필요한 파일만 같은 상대 경로로 수동 복사하는 방법도 있습니다. 편집하지 않은 복사본도 존재하는 한 덮어쓰기로 취급되기 때문입니다.

<Warning>
  배포된 템플릿은 패키지 업데이트와 자동으로 동기화되지 않습니다. 오래된 복사본이 우선되는 상태에서 패키지 쪽만 수정해도 해당 뷰의 변경은 반영되지 않습니다. 표시 버그 수정이나 폼 변경 등도 덮어쓰기 파일과의 비교가 필요합니다.
</Warning>

### 재배포는 차이를 병합하지 않는다

`VendorPublishCommand`는 일반적으로 같은 이름의 배포 대상 파일이 존재하면 복사를 건너뜁니다. `--force`는 기존 파일을 덮어씁니다. 또한 `--existing`도 "배포된 파일을 덮어쓰는" 옵션이며, 사용자의 편집을 보존하는 모드가 아닙니다.

| 작업 | 뷰 파일에 미치는 영향 |
| - | - |
| 일반 재배포 | 기존 파일은 유지하고, 존재하지 않는 대상 파일은 복사한다 |
| `--force`를 붙여 배포 | 기존 커스터마이즈도 덮어쓴다 |
| `--existing`을 붙여 배포 | 배포 대상에 존재하는 대상 파일만 덮어쓴다 |

어떤 방법도 구버전, 신버전, 사용자의 편집을 비교한 병합이 아닙니다. 패키지에서 삭제된 뷰를 배포 대상에서 자동으로 제거하는 처리도 아닙니다. 업데이트 절차를 "같은 태그를 재배포하기만 하면 된다"로 만들지 마세요.

## 뷰도 공개 API로서 유지보수하기

뷰 이름뿐 아니라 전달받는 데이터나 참조하는 부품도 사용자의 커스터마이즈에 영향을 줍니다. 예를 들어 신버전에서 `trackingCode`를 다른 변수명으로 변경하면, 구 템플릿을 유지하고 있는 사용자는 새 코드로부터 필요한 값을 받을 수 없게 됩니다.

릴리스 전에 다음 계약을 확인합니다.

* 네임스페이스와 `deliveries.show` 같은 뷰 이름을 부주의하게 변경하지 않는다.
* 전달하는 변수, 타입, 필수 여부를 기록한다.
* `@include`나 `@extends`의 참조 대상, Blade 컴포넌트의 props도 변경 사항에 포함한다.
* 수정한 뷰와, 배포된 구버전에 적용해야 할 변경을 릴리스 노트에 기재한다.

사용자를 위해 구버전의 패키지 뷰와 신버전을 비교하고, 커스터마이즈한 파일에 필요한 변경을 수동으로 반영하는 절차를 마련합니다. 덮어쓰기가 더 이상 필요 없는 파일은 백업이나 버전 관리로 변경 내용을 보전한 뒤 제거하면 패키지 쪽 뷰로 되돌릴 수 있습니다.

## Blade 캐시는 덮어쓰기 파일을 업데이트하지 않는다

`view:cache`는 Blade 템플릿을 PHP로 미리 컴파일합니다. `ViewCacheCommand`는 먼저 `view:clear`를 실행하고, 일반 뷰 경로와 네임스페이스에 등록된 경로를 수집하여 컴파일 대상을 찾습니다.

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

이 처리는 배포된 Blade 파일을 다시 쓰지 않으며, 뷰의 검색 우선순위도 바꾸지 않습니다. 오래된 덮어쓰기 파일이 존재하면 캐시를 재구축해도 그 파일이 계속 선택됩니다. 배포(deploy) 시에는 코드와 덮어쓰기 파일의 업데이트를 마친 뒤 컴파일합니다.

개발 중에 컴파일된 파일을 지우고 다시 렌더링하고 싶다면 다음 명령어를 사용합니다.

```bash theme={null}
php artisan view:clear
```

일반적인 타임스탬프 확인이 활성화되어 있으면 Blade 컴파일러는 원본 파일과 컴파일된 파일의 수정 시각을 비교합니다. 다만 타임스탬프 확인을 비활성화하는 구성도 있으므로, 배포(deploy) 시의 재구축을 자동 판정에만 맡기지 마세요.

### 검색 결과 캐시와 구분하기

`FileViewFinder::find()`는 찾은 경로를 해당 Finder 인스턴스의 `$views` 배열에 저장합니다. 또한 덮어쓰기 디렉터리의 존재 확인은 `loadViewsFrom()`의 콜백에서 이루어집니다. 기동 후에 새 디렉터리를 추가해도 이미 등록된 검색 경로에 자동으로 추가되지는 않습니다.

| 관리 대상 | 역할 | 업데이트 시 고려 사항 |
| - | - | - |
| 배포된 Blade 파일 | 사용자의 커스터마이즈 | 차이를 반영하거나 덮어쓰기를 그만둔다 |
| 컴파일된 PHP | Blade의 컴파일 결과 | `view:cache` / `view:clear`로 관리한다 |
| Finder의 등록 경로와 검색 결과 | 실행 중 인스턴스의 뷰 선택 | 장시간 실행되는 프로세스를 새 코드와 구성으로 재시작한다 |

`view:clear`는 다른 실행 중인 프로세스가 보유한 Finder의 상태를 일괄로 지우는 명령어가 아닙니다. Octane 같은 장시간 실행되는 프로세스에서는 일반적인 배포(deploy) 절차에 따라 다시 로드하세요. Finder의 `flush()`는 검색 결과를 지우지만, 새 덮어쓰기 디렉터리의 등록까지 수행하는 처리는 아닙니다.

## 릴리스 전에 확인할 사항

패키지 테스트에 더해, 사용하는 애플리케이션에서 다음 조합을 확인합니다. 최신 템플릿만 렌더링하는 테스트로는 구버전을 배포한 사용자에 대한 호환성을 검증할 수 없습니다.

* 배포하지 않은 상태에서 패키지의 뷰가 렌더링된다.
* 하나만 덮어쓰면 그 파일만 우선되고, 나머지는 패키지로 폴백된다.
* 구버전의 배포된 템플릿을 남긴 상태에서도 신버전이 전달하는 데이터로 렌더링할 수 있다.
* 일반 재배포에서 커스터마이즈가 유지되고, 배포되지 않은 파일의 추가도 의도한 대로 이루어진다.
* 덮어쓰기 파일 변경 후 `view:cache`가 성공하고, 새로 기동하면 변경 후의 표시가 된다.

## 관련 페이지

<Columns cols={2}>
  <Card title="뷰" icon="eye" href="/ko/views">
    뷰 생성, 데이터 전달, 사전 컴파일의 기본을 확인합니다.
  </Card>

  <Card title="Blade 템플릿" icon="code" href="/ko/blade">
    레이아웃, include, 컴포넌트의 사용법을 확인합니다.
  </Card>

  <Card title="버전 호환성 관리" icon="code-branch" href="/ko/advanced/package-versioning">
    템플릿의 계약 변경을 릴리스 방침과 연결합니다.
  </Card>

  <Card title="Octane" icon="bolt" href="/ko/octane">
    장시간 실행되는 애플리케이션의 라이프사이클과 다시 로드를 확인합니다.
  </Card>
</Columns>

## 참조한 1차 자료

* [Laravel 공식 문서: 패키지의 뷰](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#views)
* [ServiceProvider: 뷰 경로 등록](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [FileViewFinder: 검색 순서와 검색 결과 보관](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/View/FileViewFinder.php)
* [VendorPublishCommand: 파일 배포 조건](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php)
* [ViewCacheCommand: 뷰 경로 수집과 컴파일](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ViewCacheCommand.php)
* [ViewClearCommand: 컴파일된 파일 삭제](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ViewClearCommand.php)
* [Compiler: 수정 시각 확인](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/View/Compilers/Compiler.php)


## Related topics

- [Laravel 패키지 개발](/ko/advanced/package-development.md)
- [고급 주제](/ko/advanced/index.md)
- [Eloquent Factories](/ko/eloquent-factories.md)
- [패키지 마이그레이션의 배포와 업데이트](/ko/advanced/package-migrations.md)
- [2026년 9월 Laravel 업데이트](/ko/blog/changelog/202609.md)


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