HTTP 세션
소개
HTTP 기반 애플리케이션은 상태가 없기 때문에, 세션은 여러 요청에 걸쳐 사용자에 대한 정보를 저장하는 방법을 제공합니다. 해당 사용자 정보는 일반적으로 후속 요청에서 접근할 수 있는 지속적인 저장소/백엔드에 배치됩니다.
라라벨은 표현력이 뛰어나고 통합된 API를 통해 접근할 수 있는 다양한 세션 백엔드를 제공합니다. Memcached, Redis 및 데이터베이스와 같은 인기 있는 백엔드에 대한 지원이 포함되어 있습니다.
설정
애플리케이션의 세션 구성 파일은 config/session.php에 저장됩니다. 이 파일에서 사용 가능한 옵션을 검토하십시오. 기본적으로 라라벨은 database 세션 드라이버를 사용하도록 구성되어 있습니다.
세션 driver 구성 옵션은 각 요청에 대해 세션 데이터가 저장될 위치를 정의합니다. 라라벨은 다양한 드라이버를 포함합니다:
-
file- 세션은storage/framework/sessions에 저장됩니다. -
cookie- 세션은 안전하게 암호화된 쿠키에 저장됩니다. -
database- 세션은 관계형 데이터베이스에 저장됩니다. -
memcached/redis- 세션은 이러한 빠른 캐시 기반 저장소 중 하나에 저장됩니다. -
dynamodb- 세션은 AWS DynamoDB에 저장됩니다. -
array- 세션은 PHP 배열에 저장되며 지속되지 않습니다.
배열 드라이버는 주로 테스팅 중에 사용되며 세션에 저장된 데이터가 지속되지 않도록 합니다.
드라이버 선행 조건
데이터베이스
database 세션 드라이버를 사용하는 경우 세션 데이터를 포함할 데이터베이스 테이블이 있는지 확인해야 합니다. 일반적으로 이는 라라벨의 기본 0001_01_01_000000_create_users_table.php 데이터베이스 마이그레이션에 포함되어 있습니다. 그러나 어떤 이유로든 sessions 테이블이 없는 경우 make:session-table Artisan 명령을 사용하여 이 마이그레이션을 생성할 수 있습니다:
php artisan make:session-table php artisan migrate
php artisan make:session-table은 세션 테이블을 생성하는 마이그레이션 파일을 생성합니다. php artisan migrate는 데이터베이스에 마이그레이션을 적용하여 세션 테이블을 실제로 생성합니다.
Redis
라라벨에서 Redis 세션을 사용하기 전에, PECL을 통해 PhpRedis PHP 확장 프로그램을 설치하거나 Composer를 통해 predis/predis 패키지(~1.0)를 설치해야 합니다. Redis 구성에 대한 자세한 내용은 라라벨의 Redis 문서를 참조하십시오.
SESSION_CONNECTION 환경 변수 또는 session.php 구성 파일의 connection 옵션을 사용하여 세션 저장에 사용할 Redis 연결을 지정할 수 있습니다.
세션과의 상호 작용
데이터 검색
라라벨에서 세션 데이터를 다루는 주요 방법에는 전역 session 헬퍼와 Request 인스턴스를 사용하는 두 가지 방법이 있습니다. 먼저, 라우트 클로저나 컨트롤러 메서드에서 타입 힌트 될 수 있는 Request 인스턴스를 통해 세션에 접근하는 방법을 살펴보겠습니다. 컨트롤러 메서드 의존성은 라라벨 서비스 컨테이너를 통해 자동으로 주입되는 것을 기억하십시오.
<?php namespace App\Http\Controllers; use Illuminate\Http\Request;use Illuminate\View\View; class UserController extends Controller{ /** * 주어진 사용자의 프로필을 표시합니다. */ public function show(Request $request, string $id): View { $value = $request->session()->get('key'); // ... $user = $this->users->find($id); return view('user.profile', ['user' => $user]); }}
세션에서 항목을 검색할 때 get 메서드의 두 번째 인수로 기본값을 전달할 수도 있습니다. 지정된 키가 세션에 존재하지 않으면 이 기본값이 반환됩니다. get 메서드에 기본값으로 클로저를 전달하고 요청된 키가 존재하지 않으면 클로저가 실행되고 그 결과가 반환됩니다.
$value = $request->session()->get('key', 'default'); $value = $request->session()->get('key', function () { return 'default';});
전역 세션 헬퍼
전역 session PHP 함수를 사용하여 세션에서 데이터를 검색하고 저장할 수도 있습니다. session 헬퍼가 단일 문자열 인수로 호출되면 해당 세션 키의 값을 반환합니다. 헬퍼가 키/값 쌍의 배열로 호출되면 해당 값들이 세션에 저장됩니다.
Route::get('/home', function () { // 세션에서 데이터 조각을 검색합니다... $value = session('key'); // 기본값 지정... $value = session('key', 'default'); // 세션에 데이터 조각을 저장합니다... session(['key' => 'value']);});
HTTP 요청 인스턴스를 통해 세션을 사용하는 것과 전역 session 헬퍼를 사용하는 것 사이에는 실제적인 차이가 거의 없습니다. 두 메서드 모두 모든 테스트 케이스에서 사용 가능한 assertSessionHas 메서드를 통해 테스트 가능합니다.
모든 세션 데이터 검색
세션의 모든 데이터를 검색하려면 all 메서드를 사용할 수 있습니다.
$data = $request->session()->all();
세션 데이터 일부 검색
only 및 except 메서드를 사용하여 세션 데이터의 하위 집합을 검색할 수 있습니다.
$data = $request->session()->only(['username', 'email']); $data = $request->session()->except(['username', 'email']);
세션에 항목이 있는지 확인
세션에 항목이 있는지 확인하려면 has 메서드를 사용할 수 있습니다. has 메서드는 항목이 있고 null이 아니면 true를 반환합니다.
if ($request->session()->has('users')) { // ...}
항목의 값이 null이더라도 세션에 항목이 있는지 확인하려면 exists 메서드를 사용할 수 있습니다.
if ($request->session()->exists('users')) { // ...}
세션에 항목이 없는지 확인하려면 missing 메서드를 사용할 수 있습니다. missing 메서드는 항목이 없으면 true를 반환합니다.
if ($request->session()->missing('users')) { // ...}
데이터 저장
세션에 데이터를 저장하려면 일반적으로 요청 인스턴스의 put 메서드 또는 전역 session 헬퍼를 사용합니다.
// 요청 인스턴스를 통해...$request->session()->put('key', 'value'); // 전역 "session" 헬퍼를 통해...session(['key' => 'value']);
배열 세션 값에 푸시
push 메서드를 사용하여 배열인 세션 값에 새 값을 푸시할 수 있습니다. 예를 들어 user.teams 키에 팀 이름 배열이 포함되어 있는 경우 다음과 같이 배열에 새 값을 푸시할 수 있습니다.
$request->session()->push('user.teams', 'developers');
항목 검색 및 삭제
pull 메서드는 단일 문장으로 세션에서 항목을 검색하고 삭제합니다.
$value = $request->session()->pull('key', 'default');
세션 값 증가 및 감소
세션 데이터에 증가 또는 감소시키려는 정수가 포함되어 있는 경우 increment 및 decrement 메서드를 사용할 수 있습니다.
$request->session()->increment('count'); $request->session()->increment('count', $incrementBy = 2); $request->session()->decrement('count'); $request->session()->decrement('count', $decrementBy = 2);
플래시 데이터
다음 요청을 위해 세션에 항목을 저장해야 할 때가 있습니다. flash 메서드를 사용하여 그렇게 할 수 있습니다. 이 메서드를 사용하여 세션에 저장된 데이터는 즉시 그리고 후속 HTTP 요청 동안 사용할 수 있습니다. 후속 HTTP 요청 후 플래시된 데이터는 삭제됩니다. 플래시 데이터는 주로 수명이 짧은 상태 메시지에 유용합니다.
$request->session()->flash('status', '작업이 성공했습니다!');
플래시 데이터를 여러 요청에 대해 유지해야 하는 경우 reflash 메서드를 사용할 수 있으며, 이 메서드는 추가 요청에 대해 모든 플래시 데이터를 유지합니다. 특정 플래시 데이터만 유지해야 하는 경우 keep 메서드를 사용할 수 있습니다.
$request->session()->reflash(); $request->session()->keep(['username', 'email']);
현재 요청에 대해서만 플래시 데이터를 유지하려면 now 메서드를 사용할 수 있습니다.
$request->session()->now('status', '작업이 성공했습니다!');
데이터 삭제
forget 메서드는 세션에서 데이터 조각을 제거합니다. 세션에서 모든 데이터를 제거하려면 flush 메서드를 사용할 수 있습니다.
// 단일 키를 삭제합니다...$request->session()->forget('name'); // 여러 키를 삭제합니다...$request->session()->forget(['name', 'status']); $request->session()->flush();
세션 ID 재생성
세션 ID 재생성은 종종 악의적인 사용자가 애플리케이션에서 세션 고정 공격을 악용하는 것을 방지하기 위해 수행됩니다.
라라벨은 라라벨 애플리케이션 스타터 키트 또는 라라벨 포티파이 중 하나를 사용하는 경우 인증 중에 세션 ID를 자동으로 재생성합니다. 그러나 세션 ID를 수동으로 재생성해야 하는 경우 regenerate 메서드를 사용할 수 있습니다.
$request->session()->regenerate();
단일 문장으로 세션 ID를 재생성하고 세션에서 모든 데이터를 제거해야 하는 경우 invalidate 메서드를 사용할 수 있습니다.
$request->session()->invalidate();
세션 차단
세션 차단을 활용하려면 애플리케이션에서 원자 잠금을 지원하는 캐시 드라이버를 사용해야 합니다. 현재 이러한 캐시 드라이버에는 memcached, dynamodb, redis, mongodb (공식 mongodb/laravel-mongodb 패키지에 포함됨), database, file 및 array 드라이버가 포함됩니다. 또한 cookie 세션 드라이버를 사용할 수 없습니다.
기본적으로 라라벨은 동일한 세션을 사용하는 요청이 동시에 실행되도록 허용합니다. 예를 들어 JavaScript HTTP 라이브러리를 사용하여 애플리케이션에 두 개의 HTTP 요청을 보내면 두 요청이 모두 동시에 실행됩니다. 많은 애플리케이션에서 이것은 문제가 되지 않지만, 세션에 데이터를 쓰는 두 개의 다른 애플리케이션 엔드포인트에 동시 요청을 하는 작은 하위 집합의 애플리케이션에서는 세션 데이터 손실이 발생할 수 있습니다.
이를 완화하기 위해 라라벨은 특정 세션에 대한 동시 요청을 제한할 수 있는 기능을 제공합니다. 시작하려면 라우트 정의에 block 메서드를 연결하기만 하면 됩니다. 이 예에서 /profile 엔드포인트에 대한 수신 요청은 세션 잠금을 획득합니다. 이 잠금이 유지되는 동안 동일한 세션 ID를 공유하는 /profile 또는 /order 엔드포인트에 대한 수신 요청은 첫 번째 요청의 실행이 완료될 때까지 기다린 후 실행을 계속합니다.
Route::post('/profile', function () { // ...})->block($lockSeconds = 10, $waitSeconds = 10) Route::post('/order', function () { // ...})->block($lockSeconds = 10, $waitSeconds = 10)
block 메서드는 두 개의 선택적 인수를 허용합니다. block 메서드가 허용하는 첫 번째 인수는 세션 잠금이 해제되기 전에 유지해야 하는 최대 시간(초)입니다. 물론 이 시간 전에 요청 실행이 완료되면 잠금이 더 빨리 해제됩니다.
block 메서드가 허용하는 두 번째 인수는 세션 잠금을 얻으려고 시도하는 동안 요청이 기다려야 하는 시간(초)입니다. 주어진 시간 내에 요청이 세션 잠금을 얻을 수 없으면 Illuminate\Contracts\Cache\LockTimeoutException이 발생합니다.
이러한 인수 중 어느 것도 전달되지 않으면 잠금은 최대 10초 동안 획득되고 요청은 잠금을 얻으려고 시도하는 동안 최대 10초 동안 기다립니다.
Route::post('/profile', function () { // ...})->block()
사용자 정의 세션 드라이버 추가
드라이버 구현
기존 세션 드라이버 중 어느 것도 애플리케이션의 요구 사항에 맞지 않으면 라라벨에서 사용자 정의 세션 핸들러를 작성할 수 있습니다. 사용자 정의 세션 드라이버는 PHP의 내장 SessionHandlerInterface를 구현해야 합니다. 이 인터페이스에는 몇 가지 간단한 메서드만 포함되어 있습니다. 스텁 처리된 MongoDB 구현은 다음과 같습니다.
<?php namespace App\Extensions; class MongoSessionHandler implements \SessionHandlerInterface{ public function open($savePath, $sessionName) {} public function close() {} public function read($sessionId) {} public function write($sessionId, $data) {} public function destroy($sessionId) {} public function gc($lifetime) {}}
라라벨에는 확장을 보관할 기본 디렉터리가 포함되어 있지 않으므로 원하는 위치에 자유롭게 배치할 수 있습니다. 이 예에서는 MongoSessionHandler를 보관하기 위해 Extensions 디렉터리를 만들었습니다.
이러한 메서드의 목적은 쉽게 이해할 수 없으므로 각 메서드의 목적에 대한 개요는 다음과 같습니다.
-
open메서드는 일반적으로 파일 기반 세션 저장 시스템에서 사용됩니다. 라라벨은file세션 드라이버와 함께 제공되므로 이 메서드에 아무것도 넣을 필요가 거의 없습니다. 이 메서드를 비워 두기만 하면 됩니다. -
close메서드도open메서드와 마찬가지로 일반적으로 무시할 수 있습니다. 대부분의 드라이버에서는 필요하지 않습니다. -
read메서드는 주어진$sessionId와 연결된 세션 데이터의 문자열 버전을 반환해야 합니다. 드라이버에서 세션 데이터를 검색하거나 저장할 때 직렬화나 기타 인코딩을 수행할 필요가 없습니다. 라라벨에서 직렬화를 대신 수행하기 때문입니다. -
write메서드는$sessionId와 연결된 주어진$data문자열을 MongoDB 또는 선택한 다른 저장 시스템과 같은 영구 저장 시스템에 써야 합니다. 다시 말하지만 직렬화를 수행해서는 안 됩니다. 라라벨에서 이미 처리했기 때문입니다. -
destroy메서드는$sessionId와 연결된 데이터를 영구 저장소에서 제거해야 합니다. -
gc메서드는 주어진$lifetime(UNIX 타임스탬프)보다 오래된 모든 세션 데이터를 삭제해야 합니다. Memcached 및 Redis와 같이 자동 만료되는 시스템의 경우 이 메서드를 비워 둘 수 있습니다.
드라이버 등록
드라이버가 구현되면 라라벨에 등록할 준비가 된 것입니다. 라라벨의 세션 백엔드에 추가 드라이버를 추가하려면 Session 파사드에서 제공하는 extend 메서드를 사용할 수 있습니다. 서비스 공급자의 boot 메서드에서 extend 메서드를 호출해야 합니다. 기존 App\Providers\AppServiceProvider에서 이 작업을 수행하거나 완전히 새로운 공급자를 만들 수 있습니다.
<?php namespace App\Providers; use App\Extensions\MongoSessionHandler;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\Session;use Illuminate\Support\ServiceProvider; class SessionServiceProvider extends ServiceProvider{ /** * 모든 애플리케이션 서비스를 등록합니다. */ public function register(): void { // ... } /** * 모든 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Session::extend('mongo', function (Application $app) { // SessionHandlerInterface의 구현을 반환합니다... return new MongoSessionHandler; }); }}
세션 드라이버가 등록되면 SESSION_DRIVER 환경 변수 또는 애플리케이션의 config/session.php 구성 파일 내에서 mongo 드라이버를 애플리케이션의 세션 드라이버로 지정할 수 있습니다.