Skip to main content
패키지에서 스토리지 기능을 제공하려면 드라이버 생성 방법을 등록하는 것과 사용자의 디스크를 생성하는 것을 분리합니다. 프로바이더 부팅만으로 외부 연결이나 파일 조작을 시작하지 않도록 설계하면, 해당 기능을 사용하지 않는 환경의 부팅도 망가뜨리기 어려워집니다. 이 페이지는 Laravel Framework v13.37.0의 구현을 확인한 내용입니다. 기본 조작은 파일 스토리지를, 프로바이더 로딩은 패키지 자동 검색을 참조하세요.

드라이버 이름과 디스크 이름을 구분한다

Storage::extend()의 첫 번째 인수는 드라이버 이름입니다. 반면 Storage::disk()의 인수는 filesystems.disks에 정의하는 디스크 이름입니다. 같은 드라이버를 사용하는 디스크를 여러 개 정의할 수 있습니다. FilesystemManager::resolve()는 설정의 driver에 대응하는 커스텀 생성 처리를 찾고, 있으면 내장 드라이버보다 먼저 호출합니다. local이나 s3를 확장 이름으로 사용하면 기존 생성 처리를 대체하므로, 패키지 고유의 이름을 선택합니다.

프로바이더에서는 생성 방법만 등록한다

다음은 등록 경로를 이해하기 위해 Laravel 표준 로컬 드라이버 생성 처리를 재사용하는 최소 예제입니다. 로컬 저장만이 목적이라면 일반 local로 충분합니다. 독자적인 백엔드를 제공하는 경우에는 공식 문서의 예처럼 Flysystem 어댑터를 구축하고 Laravel의 FilesystemAdapter로 감싸는 처리로 바꿉니다.
이 프로바이더는 일반적인 방법으로 패키지에 등록합니다. HTTP, Artisan, 큐 워커에서 사용할 수 있도록 runningInConsole()로 등록을 제한하지 않습니다. 또한 확장 등록만 담당하는 프로바이더는 지연 프로바이더로 만들지 않습니다. 콜백에는 애플리케이션과 해석 대상 디스크의 설정 전체가 전달됩니다. 설정 검증이나 클라이언트 생성은 이 안에서 수행하고, boot()에서는 파일 조작을 하지 않습니다. 예제의 createLocalDriver()는 생성 시 디렉터리를 만들 수 있으므로, 등록 시점과 생성 시점을 분리하는 것이 중요합니다.
FilesystemManager::extend()는 일반 클로저의 $this를 Manager에 바인딩합니다. 프로바이더의 $this를 사용할 수 있다고 전제하지 말고, 이 예제처럼 static 클로저와 인수인 $app을 사용하면 의존 관계가 명확해집니다.
사용자는 config/filesystems.php의 disks 배열에 설정을 추가합니다.
사용하는 쪽에서는 드라이버 이름이 아니라 디스크 이름을 지정합니다.
이 예제는 로컬 저장의 생성 경로만 보여 줍니다. serve에 의한 파일 제공이나 임시 URL 제공은 다루지 않습니다. 독자적인 어댑터에서 URL 생성, 가시성, 읽기 전용 등을 지원하는 경우에도 각각의 구현과 필요한 의존 패키지를 확인하고, 설정 키를 전달하기만 하면 활성화된다고 판단하지 마세요.

이름 있는 디스크는 생성 후 보관된다

disk('reports')는 매번 콜백을 호출하지 않습니다. Manager의 get()이 내부 $disks 배열에 있는 생성된 인스턴스를 우선합니다. 아직 생성되지 않은 경우에만 resolve()를 거치며, 생성 결과는 디스크 이름을 키로 보관됩니다. 따라서 같은 프로세스에서 다음을 수행해도 생성된 디스크는 자동으로 갱신되지 않습니다.
  • config()로 filesystems.disks.reports를 변경한다.
  • 같은 드라이버 이름에 대해 Storage::extend()를 다시 호출한다.
FilesystemServiceProvider는 Manager 자체도 singleton으로 등록합니다. 장시간 동작하는 프로세스에서는 요청이나 잡마다 디스크가 반드시 다시 만들어진다고 가정하지 마세요.

설정 변경 후에는 필요한 디스크만 재해석한다

같은 프로세스에서 설정을 변경하여 검증하는 경우에는 설정 갱신과 보관된 디스크 제거를 분리합니다.
forgetDisk()는 지정한 이름을 Manager의 보관 배열에서 제거합니다. 다음 disk() 호출에서 현재 설정을 다시 읽습니다. 확인한 버전의 purge()도 지정한 이름을 제거하는 구현이며, 독자적인 백엔드의 연결 종료 메서드를 호출하는 것은 아닙니다.
$before처럼 사용하는 쪽에서 보관하고 있는 참조까지 바뀌는 것은 아닙니다. singleton 서비스가 오래된 디스크를 보관하고 있는 경우도 마찬가지입니다. forgetDisk()를 모든 참조나 다른 프로세스를 일괄 갱신하는 수단으로 사용하지 마세요.
프로덕션의 설정 변경이나 패키지 업데이트에서는 일반적인 배포 절차에 따라 설정 캐시를 다시 생성하고, 장시간 동작하는 워커도 재시작합니다. 설정 캐시와 이 프로세스 내의 디스크 보관 배열은 별개입니다.

일시적인 설정에는 build를 사용한다

이름 있는 공유 설정을 변경하고 싶지 않다면 온디맨드 디스크를 사용할 수 있습니다.
build()는 지정한 설정으로 직접 resolve()를 호출하며, 결과를 이름 있는 디스크의 보관 배열에 추가하지 않습니다. 커스텀 드라이버 등록은 필요하지만, 호출할 때마다 생성 처리로 진행합니다. 동적인 저장 위치를 사용하는 경우에도 저장 위치나 연결 대상을 사용자 입력으로 무제한 선택할 수 있도록 설계하지 않습니다.

fake와 별도로 생성 경로를 테스트한다

Storage::fake('reports')는 Manager의 set()으로 해당 디스크를 테스트용 로컬 디스크로 대체합니다. 애플리케이션의 파일 조작 테스트에는 편리하지만, 패키지의 커스텀 생성 처리가 올바르게 등록되고 실행되는지는 검증하지 않습니다. 패키지의 테스트 환경에서 ArchiveServiceProvider를 로드한 뒤 다음을 나누어 검증합니다. 독자적인 외부 백엔드에서는 네트워크를 사용하지 않는 생성 테스트와, 명시적으로 준비한 검증 환경에서의 연결 테스트를 분리합니다. fake의 성공을 실제 서비스와의 호환성 증거로 삼지 않습니다.

장기 유지보수 체크리스트

  • 드라이버 이름, 설정 키, 반환값의 API, 예외나 throw의 처리를 공개 사양으로 문서화한다.
  • 표준 드라이버나 다른 패키지와 충돌하지 않는 확장 이름을 사용한다.
  • 프로바이더 부팅만으로 디스크를 해석하거나 사용하지 않는다.
  • 설정 변경 시에는 설정 캐시, Manager의 보관 배열, 사용하는 쪽의 참조, 다른 프로세스를 구분한다.
  • Laravel의 지원 버전마다 생성, 재해석, 실제 파일 조작을 지속적으로 검증한다.

참조한 1차 자료

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