Skip to main content
패키지의 JavaScript나 CSS를 업데이트해도 애플리케이션의 public에 이미 복사된 파일은 자동으로 바뀌지 않습니다. PHP 코드만 새 버전이 되고 브라우저는 계속 이전 버전의 에셋을 사용하는 상태를 막으려면, 공개 위치의 소유자와 업데이트 절차를 정해 두어야 합니다. 이 페이지에서는 패키지 개발의 기초를 전제로, Laravel 13의 공개 처리를 바탕으로 배포와 유지보수 설계를 정리합니다. 구현 확인에는 laravel/framework의 v13.35.0을 사용했습니다.

공개는 빌드도 동기화도 아니다

ServiceProvider::publishes()는 복사 원본과 복사 대상을 등록합니다. 실제로 파일을 복사하는 것은 vendor:publish입니다. JavaScript 트랜스파일이나 CSS 빌드, 애플리케이션의 Vite 엔트리에 대한 추가는 수행하지 않습니다. 패키지 측에서 빌드된 파일을 배포하는 경우, 예를 들어 다음과 같이 구성합니다.
처음 공개할 때는 프로바이더와 태그를 명시합니다.
이 예에서는 public/vendor/courier/courier.css와 courier.js가 생성됩니다. 일반적인 CSS와 JavaScript로 배포하는 설계라면 Blade에서 다음과 같이 참조할 수 있습니다.
ES modules로 배포하는 경우 등에는 배포 형식에 맞춰 불러오는 방식을 변경합니다. asset()은 URL을 생성하는 헬퍼일 뿐, 빌드나 공개, 내용에 따른 파일명 생성을 수행하지 않습니다.
복사 원본에는 공개해도 되는 빌드 결과물만 두세요. 이 예의 공개 위치는 웹에서 접근할 수 있는 public입니다. 설정 파일이나 내부 데이터를 같은 공개 그룹에 포함하지 마세요.

태그는 프로바이더 전용 네임스페이스가 아니다

ServiceProvider는 공개 경로를 프로바이더 클래스별 배열과 태그별 배열에 등록합니다. 태그별 배열은 여러 프로바이더가 공유하므로, public과 같은 범용 태그를 사용하면 다른 패키지도 대상이 될 수 있습니다. 둘 다 지정하면 pathsForProviderAndGroup()은 복사 원본 경로를 키로 하여 array_intersect_key()를 사용합니다. 태그에 따라 다른 복사 대상으로 전환하는 구조가 아닙니다. 같은 복사 원본을 여러 번 등록해 용도별 복사 대상을 갖게 하는 설계는 피하세요. --tag는 여러 번 지정할 수 있습니다. 이 경우 각 태그를 순서대로 공개합니다. --all은 선택 처리의 맨 앞에서 반환되므로, --provider나 --tag를 함께 지정해도 범위를 좁히는 데 사용되지 않습니다.
사용자의 설정이나 뷰까지 덮어쓰지 않도록, 업데이트 절차에서는 패키지 고유의 에셋 태그를 사용합니다. 프로바이더만 지정하고 --force를 붙이면 같은 프로바이더의 설정이나 뷰도 대상이 될 수 있습니다.

재공개 옵션을 구분해서 사용하기

VendorPublishCommand의 파일 공개와 디렉터리 공개는 복사 대상 파일의 존재 여부와 옵션을 사용해 복사 여부를 판단합니다. 다음 표는 복사 원본에 존재하는 일반 에셋 파일에 대한 동작입니다. --existing은 편집 내용을 보호하는 옵션이 아닙니다. 기존 파일은 덮어쓰는 반면, 새 버전에서 추가된 파일은 공개하지 않습니다. JavaScript가 새로 추가된 파일을 필요로 하는 업데이트에서는 --existing만으로는 결과물이 모두 갖춰지지 않을 수 있습니다. 패키지가 공개 위치를 관리하고 사용자가 직접 편집하지 않는다는 약속이라면, 업데이트 후 다음을 실행합니다.
--force는 차이를 병합하지 않고 사용자의 편집 내용도 덮어씁니다. 사용자가 커스터마이즈하는 CSS는 별도 파일로 불러오는 등 패키지가 관리하는 결과물과 분리하세요. 설정이나 뷰의 커스터마이즈와는 업데이트 방침을 구분합니다.

삭제한 파일은 공개 위치에 남는다

디렉터리 공개의 moveManagedFiles()는 복사 원본에 있는 파일을 순회하며 기록합니다. 공개 위치에만 있는 파일을 찾아 삭제하는 처리는 없습니다. --force도 디렉터리의 완전한 동기화가 되지 않습니다. 예를 들어 새 버전에서 legacy.js를 삭제해도, 이전 버전을 이미 공개했다면 public/vendor/courier/legacy.js는 남아 있습니다. 이름을 바꾼 경우에도 이전 이름의 파일이 남으므로, 릴리스 노트에는 삭제·이름 변경한 파일과 참조 대상의 변경을 기록합니다. 이전 파일을 제거하는 절차를 제공하는 경우에는 패키지가 소유한 파일을 구체적으로 명시하세요. 사용자의 독자적인 파일이 놓여 있을 가능성이 있는 디렉터리를 통째로 삭제하는 절차로 만들지 않습니다.

laravel-assets 참여는 덮어쓰기 약속을 의미한다

Laravel 13의 공식 애플리케이션 스켈레톤에는 composer.json의 post-update-cmd에 다음 스크립트가 있습니다.
이것은 애플리케이션 측의 스크립트입니다. 패키지 자동 감지 자체가 공개 파일을 업데이트하는 것은 아닙니다. 기존 애플리케이션에서는 스크립트가 변경·삭제되었을 수도 있으므로 사용하는 측의 설정을 확인합니다. 이 업데이트 경로에 참여하는 경우에는 앞의 publishes()의 두 번째 인수를 배열로 변경하여 같은 에셋을 두 개의 태그에 등록합니다.
laravel-assets는 특별한 복사 처리를 갖는 태그가 아닙니다. 스켈레톤의 스크립트가 이 태그를 --force와 함께 공개하므로, 참여한 파일은 Composer 업데이트 시 덮어쓰기 대상이 됩니다. 사용자가 편집하는 설정이나 뷰는 등록하지 마세요.
자동 업데이트의 전제는 애플리케이션 측에 스크립트가 있고, 해당 이벤트가 실행되며, 프로바이더가 공개 경로를 등록하고 있다는 것입니다. 이 전제를 충족하지 않는 배포에서도 업데이트할 수 있도록, 패키지 고유의 태그를 사용한 재공개 명령어를 안내합니다.

배포에서 PHP와 에셋의 버전 맞추기

config:cache나 view:cache는 공개된 JavaScript·CSS를 다시 쓰지 않습니다. 공개 후에도 같은 URL로 제공하는 설계라면 브라우저나 CDN의 캐시 때문에 이전 내용이 사용될 수 있습니다. 배포물의 버전을 반영한 URL이나 캐시 무효화 등 애플리케이션의 제공 방침도 업데이트 절차에 포함합니다. 릴리스마다 다음 조합을 확인합니다.
  • 아직 공개하지 않은 애플리케이션에서 필요한 빌드된 파일이 모두 공개된다.
  • 이전 버전을 공개한 상태에서 --force로 기존 파일이 업데이트되고 새 파일이 추가된다.
  • 에셋 업데이트가 사용자의 설정·뷰·독자적인 CSS를 덮어쓰지 않는다.
  • 삭제·이름 변경한 파일의 처리가 명시되어 있고, 이전 버전의 참조가 남아 있지 않다.
  • 실제 제공 URL에서 새 버전의 내용이 전달되고, PHP와 브라우저 측 처리가 연동된다.

관련 페이지

패키지 자동 감지

Composer 업데이트, 프로바이더 감지, 파일 공개의 차이를 확인합니다.

패키지 뷰의 덮어쓰기와 업데이트

사용자가 커스터마이즈하는 템플릿의 유지보수 방침을 확인합니다.

패키지 캐시와 optimize 통합

파일 공개와 별도로 관리하는 패키지 캐시를 해설합니다.

버전 호환성 관리

공개 위치나 배포 형식의 변경을 호환성 약속으로 다룹니다.

참조한 1차 자료

마지막 수정일 2026년 10월 7일