메인 콘텐츠로 건너뛰기
패키지에서 독자적인 캐시 백엔드를 제공하려면 저장 처리를 구현하는 Store와 Laravel의 API를 제공하는 Repository를 분리합니다. Cache::extend()에 등록하는 것은 생성 방법이며, 반환한 Store를 Laravel이 자동으로 Repository로 감싸 주는 것은 아닙니다. 이 페이지는 Laravel Framework v13.39.0의 구현을 확인한 내용입니다. 기본 조작은 캐시를, 프로바이더 준비는 패키지 개발을 참조하세요.

드라이버 이름과 스토어 이름 구분하기

CacheManager::build()는 설정의 driver에 대응하는 커스텀 생성 처리가 있으면 내장 드라이버보다 먼저 호출합니다. redis나 array 같은 이름을 재사용하지 말고 패키지 고유의 이름을 사용하세요.

다른 프로바이더의 boot보다 먼저 등록하기

다른 패키지가 boot()에서 캐시를 사용하는 경우, 자신의 boot()에서 드라이버를 등록하면 순서에 따라 제때 등록되지 않을 수 있습니다. 공식 문서에 따라 register()에서 애플리케이션의 booting 콜백을 등록합니다. 다음은 외부 서비스 없이 등록 경로를 확인하기 위해 표준 ArrayStore를 사용하는 최소 예시입니다. Acme\Memo 패키지의 src/MemoServiceProvider.php에 배치합니다.
프로바이더는 패키지 자동 검색 등으로 로드합니다. 확장을 등록하기만 하는 이 프로바이더는 지연 프로바이더로 만들지 말고, HTTP와 Artisan 양쪽에서 로드되도록 합니다.
  • 애플리케이션의 booting은 일반적인 부팅에서 각 프로바이더의 register()가 완료된 후, 각 boot()보다 먼저 실행됩니다. 프로바이더 자신의 $this->booting()과는 별개입니다.
  • extend()의 콜백에는 애플리케이션과 스토어 설정 배열이 전달됩니다. Store 생성이나 외부 연결은 이 생성용 콜백 쪽에 둡니다.
  • 이 등록 시점은 다른 프로바이더의 register() 안에서의 캐시 사용까지 보장하지 않습니다. 서비스 등록과 실제 사용을 분리하세요.
표준 인메모리 캐시만 필요하다면 array 드라이버로 충분합니다. 이 예시는 등록과 계약을 설명하기 위한 것으로, 프로세스 간 공유나 영속화에는 사용할 수 없습니다. 독자적인 백엔드에서는 ArrayStore의 생성 부분을 Illuminate\Contracts\Cache\Store를 구현한 클래스로 교체합니다.
CacheManager::extend()는 익명 클로저의 스코프를 Manager로 다시 바인딩합니다. non-static 클로저의 $this를 프로바이더라고 생각하지 말고, 예시처럼 static 클로저와 인수 $app을 사용하세요.

사용하는 애플리케이션에 스토어 추가하기

config/cache.php의 기존 stores 배열에 추가합니다. 패키지가 사용하는 쪽의 default나 stores 전체를 덮어쓰는 설계는 피합니다.
기본값으로 사용하고 싶을 때 CACHE_STORE에 지정하는 것도 드라이버 이름이 아니라 catalog라는 스토어 이름입니다. 같은 드라이버의 설정을 여러 개 만들면 스토어 이름마다 별도의 Repository가 생성됩니다.

Repository는 직접 구성하기

CacheManager::callCustomCreator()는 콜백의 반환값을 그대로 반환합니다. Store만 반환하면 get()이나 put()이 동작하더라도 Repository의 remember() 등은 사용할 수 없고, TTL 해석도 달라집니다. 예시의 repository($store, $config)는 다음을 담당합니다.
  • Illuminate\Cache\Repository로 Store를 감싼다.
  • Manager가 설정에 추가한 store 이름을 Repository에 전달한다.
  • 이벤트 Dispatcher가 등록되어 있고 events 설정이 비활성화되어 있지 않으면 Repository에 Dispatcher를 설정한다.
이것은 독자적인 설정을 모두 Store에 적용하는 처리가 아닙니다. 연결 대상, prefix, 직렬화 등은 Store 구현과 생성 처리 쪽에서 반영합니다. 예시에서 설정을 읽고 있는 serialize도 마찬가지입니다.

Store 계약을 현재 구현과 대조하기

Laravel 13의 Illuminate\Contracts\Cache\Store가 요구하는 메서드를 구현합니다. 과거 샘플의 빈 메서드 목록을 그대로 동작하는 드라이버로 배포하지 마세요. null은 Repository에서 캐시 미스로 취급됩니다. false나 0까지 미존재로 취급하지 않도록 구현하세요. null을 저장해도 remember()의 재계산을 막는 표시가 되지는 않습니다.

TTL은 Repository를 통해 검증하기

Repository::put()은 Store에 전달하기 전에 TTL을 처리합니다.
예시의 ArrayStore는 직접 put()에 0초를 전달하면 무기한으로 저장합니다. 반면 Repository의 put(..., 0)은 삭제입니다. getStore()로 꺼낸 저수준 Store만 테스트해서는 사용자가 호출하는 API의 동작을 확인할 수 없습니다.

add·락·태그는 별도로 지원 여부 확인하기

Store 계약을 구현하는 것만으로 모든 캐시 기능의 지원이 보장되지는 않습니다.
  • 원자적 add(): Store 계약에는 포함되지 않습니다. 양수 TTL과 함께 호출되고 Store에 add()가 있으면 Repository는 그것을 사용합니다. 없으면 get()과 put()에 의한 폴백이므로, Repository로 감싸는 것만으로는 원자성을 보장할 수 없습니다. 공유 백엔드에서는 해당 백엔드의 원자적 연산으로 구현하고 동시 실행도 검증합니다.
  • 락: Illuminate\Contracts\Cache\LockProvider의 lock()과 restoreLock()은 별도의 계약입니다. 획득, 소유자 일치, 만료, 다른 소유자의 락을 해제하지 않는 것까지 검증합니다. 예시의 ArrayStore 락은 별도 프로세스 간의 배타 제어가 되지 않습니다.
  • 태그: Repository는 Store에 tags()가 있는지 확인합니다. 일반적으로 TaggableStore 등을 사용한 구현이 필요하며, Store 계약만으로 자동으로 활성화되지는 않습니다.
flush()를 설정의 prefix를 존중하는 부분 삭제라고 생각하지 마세요. 공유 백엔드에서의 삭제 범위를 확인하고, php artisan cache:clear를 포함해 사용자에게 설명합니다.

인스턴스 보관과 데이터 삭제 구분하기

CacheManager는 singleton이며, store('catalog')의 결과도 스토어 이름별로 보관합니다. 설정 변경이나 extend()의 재등록만으로는 생성된 Repository가 갱신되지 않습니다.
$same은 $before와 같은 객체입니다. forgetDriver() 이후의 조회에서 새 설정으로 $after가 생성됩니다. forgetDriver()는 백엔드의 전체 삭제가 아닙니다. 다만 예시의 ArrayStore는 인스턴스 내에 데이터를 갖고 있으므로 새 Store에서는 이전 데이터가 보이지 않습니다. 또한 사용하는 쪽의 $before나, 해석된 cache.store singleton이 보관하는 이전 Repository 참조는 교체되지 않습니다. 프로덕션에서는 일반적인 배포 절차에 따라 설정 캐시를 갱신하고 장기 실행 워커를 재시작합니다. 서비스의 라이프사이클도 고려해, 재해석을 모든 프로세스의 일괄 갱신으로 취급하지 마세요.

파사드 모킹뿐만 아니라 생성 경로 테스트하기

패키지 테스트 환경에서 실제 Manager와 Repository를 거치게 합니다. 다음 Testbench 예시는 먼저 등록한 다른 프로바이더의 boot()에서도 드라이버를 사용할 수 있는지 확인합니다. 예시에서는 cache.serializable_classes의 복원 제한을 변경하지 않고 배열을 저장하며, ArrayStore::all(false)로 원시 저장값도 확인합니다.
독자적인 백엔드에서는 여기에 더해 만료, many()의 미존재 키, false·0의 왕복, touch(), 연결 장애, 동시 갱신, prefix와 삭제 범위를 검증합니다. 단순한 Cache::shouldReceive('get')의 성공을 드라이버 등록이나 외부 서비스와의 호환성 증거로 삼지 마세요.

장기 유지보수 체크리스트

  • 드라이버 이름, 스토어 설정, 직렬화 형식, 예외와 실패 시 반환값을 공개 사양으로 한다.
  • Store 계약의 메서드와 태그·락 등의 선택 기능을 지원하는 Laravel 버전별로 확인한다.
  • 설정 캐시가 있는 새 부팅에서도 등록할 수 있고, 다른 프로바이더의 boot()에서 사용할 수 있는지 검증한다.
  • 저장 데이터의 호환성 변경은 기존 키의 마이그레이션이나 폐기 방침까지 포함해 공지한다.
  • 프로세스 내 인스턴스 보관, 백엔드 데이터, 설정 캐시를 구분한다.

참조한 1차 자료

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