메인 콘텐츠로 건너뛰기
패키지에서 독자적인 세션 저장소를 제공하려면 저장 처리의 등록과 애플리케이션에 의한 선택을 분리합니다. 설치만으로 로그인 상태의 저장소가 바뀌지 않도록 하고, 사용자가 기존 세션에 미치는 영향을 확인한 뒤 활성화할 수 있게 설계합니다. 이 페이지는 Laravel Framework v13.39.0의 공식 소스를 확인한 내용입니다. 기본 조작은 세션을, 프로바이더의 자동 로드는 패키지 자동 검출을 참조하세요.

extend가 받는 것은 Store가 아니라 핸들러

Session::extend()의 콜백은 PHP의 SessionHandlerInterface를 구현한 저장용 핸들러를 반환합니다. Illuminate\Contracts\Session\Session을 구현한 Store나 캐시 Repository를 직접 반환하는 API가 아닙니다. SessionManager::callCustomCreator()는 콜백의 반환값을 buildSession()에 전달하고, session.encrypt에 따라 Store 또는 EncryptedStore로 감쌉니다. 이때 쿠키 이름과 session.serialization도 적용합니다. 일반적인 HTTP 처리에서는 StartSession 미들웨어가 세션의 시작과 저장을 관리합니다. 핸들러를 제공하기 위해 패키지 측에서 session_start()나 session_set_save_handler()를 호출할 필요는 없습니다.

저장 계약을 나누어 생각하기

독자적인 백엔드를 구현하는 경우 다음 역할을 충족해야 합니다. Store가 직렬화와 복원을 담당하므로 핸들러에서 세션 배열을 다시 serialize()하거나 json_encode()하지 않습니다. 암호화를 활성화한 경우에도 EncryptedStore가 처리한 문자열을 그대로 저장합니다.
데이터가 저장되지 않은 경우와 백엔드 장애는 구분합니다. 확인한 Store::readFromHandler()는 읽기 결과가 비어 있거나 false이면 빈 세션으로 취급합니다. 또한 Store::save()는 핸들러의 write() 반환값을 검사하지 않습니다.장애를 반드시 예외로 처리하려는 패키지에서는 저장소의 예외를 삼키지 말고, false를 반환하는 백엔드도 핸들러 측에서 감지해 예외로 변환하는 등 방침을 명시하고 테스트합니다. 실패 시 Laravel이 자동으로 재시도하거나 저장 실패를 알려 준다고 보장할 수는 없습니다.

boot에서는 생성 처리만 등록하기

다음은 내장 CacheBasedSessionHandler를 재사용하는 최소 예시입니다. 새로운 스토리지 프로토콜을 구현하는 것이 아니라, 이름이 지정된 캐시 스토어를 사용하는 독자 드라이버의 등록 방법을 보여 줍니다. Acme\SessionBridge\가 Composer의 PSR-4로 src/에 대응하는 구성입니다. src/SessionBridgeServiceProvider.php에 프로바이더를 준비합니다.
Session::extend()는 SessionManager를 가져와 생성 처리를 등록하지만, 이 시점에는 콜백 안의 캐시 스토어를 해석하지 않습니다. 저장소는 해당 드라이버가 처음 선택될 때 생성됩니다. 프로바이더의 boot()에서 연결 확인이나 데이터 저장을 실행하지 않는 구성으로 만듭니다. 독자적인 SessionHandlerInterface 구현을 사용하는 경우에는 콜백이 반환하는 핸들러를 교체합니다. new Store(...)까지 수행하면 SessionManager가 기대하는 반환값과 맞지 않게 됩니다.
  • 프로바이더는 일반적인 비지연 프로바이더로 로드합니다.
  • 세션이 처음 사용되기 전에 등록을 완료합니다. 다른 프로바이더가 먼저 독자 드라이버를 해석하는 경우에는 로드 순서도 확인합니다.
  • 드라이버 이름에는 패키지 고유의 이름을 사용합니다. 같은 이름의 extend()는 생성 처리를 덮어쓰며, 내장 드라이버 이름을 등록하면 내장 구현보다 우선합니다.

사용하는 애플리케이션이 명시적으로 선택하기

사용자는 기존 config/session.php의 해당 키를 변경합니다. 다른 설정은 그대로 둡니다.
저장소로는 config/cache.php의 기존 stores 배열에 이름이 지정된 스토어를 추가합니다. 다음은 Redis 연결 default를 사용하는 예입니다. Redis 연결 설정과 필요한 클라이언트는 애플리케이션 측에서 준비합니다.
이 예의 연결과 프리픽스는 캐시 스토어 설정에서 결정됩니다. 내장 Redis 세션 드라이버의 createRedisDriver()를 거치지 않으므로, session.connection이나 session.prefix를 추가해도 이 콜백이 자동으로 반영하지는 않습니다. 이 콜백은 Repository나 저장소 설정을 변경하지 않고 이름이 지정된 스토어를 그대로 사용합니다. 테스트용 array 스토어도 같은 애플리케이션 안에서는 같은 저장 영역을 공유합니다. 패키지에서 session.driver, 쿠키 이름, 애플리케이션 전체의 캐시 프리픽스를 무조건 덮어쓰지 않도록 합니다. 다른 용도와 저장 영역을 공유하지 않는 설계나, 여러 서버에서 같은 저장소를 사용할 수 있는 구성도 사용자와 함께 확인합니다.
일반적인 Redis나 데이터베이스 저장만이 목적이라면 먼저 내장 세션 드라이버를 사용하세요. 독자 드라이버는 새로운 저장소나 추가적인 저장 규약이 필요한 경우를 위한 확장 지점입니다.

TTL 단위와 저장 형식 유지하기

CacheBasedSessionHandler의 생성자는 보존 기간을 분 단위로 받습니다. write()는 매번 $minutes * 60을 캐시 Repository의 put()에 전달하므로, 위의 예에서는 쓰기가 일어날 때마다 TTL이 갱신됩니다. gc()는 0을 반환하고 만료 처리는 캐시 측에 맡깁니다. 반면 StartSession이 핸들러의 gc($lifetime)에 전달하는 기간은 초 단위입니다. session.lifetime 값을 그대로 초로 저장하는 구현이 되지 않도록 주의합니다. 또한 다음 변경은 설정 이름만 바꾸는 것으로 끝나지 않습니다. 패키지 업데이트에서 이러한 항목을 변경하는 경우에는 재인증 안내, 마이그레이션 처리 여부, 이전 버전과 새 버전이 혼재하는 배포를 검토합니다. 패키지 버전 호환성 관리와 마찬가지로 저장 형식과 설정 키도 유지보수할 공개 사양에 포함합니다.

생성된 Store는 설정 변경만으로 교체되지 않음

SessionManager의 부모 클래스인 Manager는 생성한 드라이버를 이름별로 보관합니다. 또한 SessionServiceProvider는 session.store를 singleton으로 등록합니다. forgetDrivers()는 세션 데이터를 삭제하는 API도 아닙니다. 실행 중인 요청에서 Store를 다시 만들면 이미 읽어 들인 데이터나 다른 참조와의 불일치를 초래합니다. 이 예의 CacheManager도 이름이 지정된 스토어를 보관합니다. 그 연결 설정을 변경한 경우에는 SessionManager만 다시 해석하는 것으로는 충분하지 않습니다. 설정 캐시를 사용하는 배포에서는 캐시를 다시 빌드하고, 장기 실행 워커도 새 코드와 설정을 읽어 들이도록 재시작합니다. 애플리케이션 재시작과 저장된 데이터의 마이그레이션은 별개의 작업입니다.

세션 저장과 요청 락은 별개

커스텀 핸들러를 구현한 것만으로는 같은 세션에 대한 병렬 요청이 직렬화되지 않습니다. StartSession의 블로킹은 session.block_store로 선택하는 캐시 스토어의 락을 이용합니다. 세션 본체의 저장소와 같다고는 할 수 없습니다. Route::post(...)->block() 등으로 제어하는 경우에는 락을 지원하는 캐시 스토어, 여러 서버 간 공유, 대기 시간과 보유 시간을 별도로 검증합니다. 패키지가 애플리케이션 전체의 블로킹 설정을 임의로 활성화하지 않도록 합니다.

저장과 재로드까지 테스트하기

Orchestra Testbench를 사용하는 패키지에서는 프로바이더를 로드하고 독자 드라이버를 실제로 선택한 테스트를 준비합니다. 다음은 tests/SessionDriverTest.php의 예입니다. 테스트용 array 캐시는 외부 연결을 사용하지 않습니다.
이 테스트는 같은 애플리케이션 안에서 Store를 다시 생성하는 격리 테스트입니다. HTTP 미들웨어나 쿠키 왕복, 다른 프로세스로의 영속화, 실제 Redis의 만료나 락을 검증하는 것은 아닙니다. array를 프로덕션의 공유 저장소로 사용하지 않도록 합니다. 독자 핸들러를 공개하기 전에는 다음 항목도 추가합니다.
  • 저장되지 않았거나 만료된 ID가 빈 문자열을 반환하는지
  • 저장한 문자열을 변경 없이 복원할 수 있고, 삭제 후에는 다시 가져올 수 없는지
  • TTL의 단위와 갱신, 실제 백엔드에서의 만료
  • 읽기, 쓰기, 삭제 실패 시 정해 둔 반환값이나 예외가 되는지
  • session.encrypt를 활성화한 왕복과 지원하는 직렬화 형식
  • 프로바이더를 로드하는 것만으로는 저장소에 접근하지 않고, 내장 드라이버의 선택도 빼앗지 않는지
  • 필요하다면 실제 web 미들웨어와 쿠키를 거치는 HTTP 테스트, 그리고 병렬 요청의 락

관련 페이지

세션

세션 읽기와 쓰기, 플래시 데이터, ID 재생성을 확인합니다.

Manager의 내부 구현

커스텀 드라이버의 생성과 보관, 기본 드라이버로의 위임을 확인합니다.

패키지의 커스텀 캐시 스토어

저장소가 되는 캐시 Store와 Repository의 역할 분담, TTL 계약을 확인합니다.

패키지 서비스의 라이프사이클

장기 실행 워커에서 보관되는 의존성과 상태의 분리를 확인합니다.

1차 자료

마지막 수정일 2026년 11월 2일