세션
번역일: 2026년 6월 20일
세션
소개
HTTP는 기본적으로 상태를 저장하지 않는(stateless) 프로토콜입니다. 세션은 여러 요청에 걸쳐 사용자 정보를 유지할 수 있도록 해주는 메커니즘입니다. 세션 데이터는 서버 측의 영구 저장소에 보관되며, 이후 요청에서도 동일한 데이터에 접근할 수 있습니다.
Laravel은 통일된 API를 통해 다양한 세션 백엔드를 지원합니다. Memcached, Redis, 관계형 데이터베이스 등 널리 사용되는 저장소를 기본으로 지원합니다.
설정
세션 설정 파일은 config/session.php에 있습니다. 이 파일을 열어 사용 가능한 옵션들을 확인하세요. Laravel의 기본 세션 드라이버는 database입니다.
driver 옵션은 각 요청에서 세션 데이터를 어디에 저장할지를 결정합니다. Laravel이 기본으로 지원하는 드라이버 목록은 다음과 같습니다.
file-storage/framework/sessions디렉터리에 파일로 저장합니다.cookie- 암호화된 쿠키에 저장합니다.database- 관계형 데이터베이스에 저장합니다.memcached/redis- 빠른 캐시 기반 저장소에 저장합니다.dynamodb- AWS DynamoDB에 저장합니다.array- PHP 배열에 저장하며, 요청이 끝나면 사라집니다. 실제로 데이터가 유지되지 않습니다.
NOTE
array 드라이버는 주로 테스트 환경에서 사용됩니다. 데이터가 실제로 저장되지 않기 때문에 프로덕션에서는 사용하지 않습니다.
드라이버 사전 준비
Database
database 드라이버를 사용하려면 세션 데이터를 저장할 테이블이 필요합니다. 보통 Laravel 기본 마이그레이션 파일인 0001_01_01_000000_create_users_table.php에 sessions 테이블 생성 구문이 포함되어 있습니다. 만약 해당 테이블이 없다면 아래 Artisan 명령으로 마이그레이션을 생성하고 실행하세요.
php artisan make:session-tablephp artisan migrateRedis
Laravel에서 Redis 세션을 사용하려면 PECL을 통해 PhpRedis PHP 확장을 설치하거나, Composer로 predis/predis 패키지(~1.0)를 설치해야 합니다. Redis 설정에 대한 자세한 내용은 Redis 문서를 참고하세요.
NOTE
세션 저장에 사용할 Redis 연결은 .env의 SESSION_CONNECTION 환경 변수 또는 config/session.php의 connection 옵션으로 지정할 수 있습니다.
세션 데이터 다루기
데이터 조회
Laravel에서 세션 데이터에 접근하는 방법은 크게 두 가지입니다. 하나는 Request 인스턴스를 통하는 방법이고, 다른 하나는 전역 session 헬퍼를 사용하는 방법입니다.
먼저 Request 인스턴스를 이용하는 방식을 살펴보겠습니다. 컨트롤러 메서드나 라우트 클로저에 Request를 타입힌트로 선언하면 Laravel의 서비스 컨테이너가 자동으로 주입해 줍니다.
<?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 메서드의 두 번째 인수로 기본값을 지정할 수 있습니다. 해당 키가 세션에 없으면 이 기본값이 반환됩니다. 클로저를 기본값으로 전달하면 키가 없을 때 클로저가 실행되고 그 결과가 반환됩니다.
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});전역 Session 헬퍼
전역 session 함수로도 세션 데이터를 읽고 쓸 수 있습니다. 문자열 하나를 인수로 전달하면 해당 키의 값을 반환하고, 배열을 전달하면 세션에 값을 저장합니다.
Route::get('/home', function () {
// 세션에서 값 조회
$value = session('key');
// 기본값 지정
$value = session('key', 'default');
// 세션에 값 저장
session(['key' => 'value']);
});NOTE
Request 인스턴스를 통해 사용하든 전역 session 헬퍼를 사용하든 동작 방식에 실질적인 차이는 없습니다. 두 방식 모두 테스트 케이스에서 assertSessionHas 메서드로 테스트할 수 있습니다.
세션 전체 데이터 조회
all 메서드를 사용하면 세션에 저장된 모든 데이터를 배열로 가져올 수 있습니다.
$data = $request->session()->all();일부 데이터만 조회
only와 except 메서드를 사용하면 세션 데이터 중 원하는 항목만 선택하거나 제외해서 가져올 수 있습니다.
$data = $request->session()->only(['username', 'email']);
$data = $request->session()->except(['username', 'email']);세션 키 존재 여부 확인
has 메서드는 세션에 해당 키가 존재하고 값이 null이 아닐 때 true를 반환합니다.
if ($request->session()->has('users')) {
// ...
}값이 null이더라도 키 자체가 존재하는지 확인하려면 exists 메서드를 사용하세요.
if ($request->session()->exists('users')) {
// ...
}반대로 키가 세션에 없는지 확인하려면 missing 메서드를 사용합니다. 키가 없으면 true를 반환합니다.
if ($request->session()->missing('users')) {
// ...
}데이터 저장
세션에 데이터를 저장할 때는 Request 인스턴스의 put 메서드 또는 전역 session 헬퍼를 사용합니다.
// Request 인스턴스 사용
$request->session()->put('key', 'value');
// 전역 헬퍼 사용
session(['key' => 'value']);배열 세션 값에 항목 추가
세션에 저장된 배열 값에 새 항목을 추가하려면 push 메서드를 사용합니다. 예를 들어 user.teams 키에 팀 이름 배열이 저장되어 있다면 다음과 같이 새 팀을 추가할 수 있습니다.
$request->session()->push('user.teams', 'developers');조회 후 즉시 삭제
pull 메서드는 세션에서 값을 가져오는 동시에 해당 항목을 삭제합니다. 1회성 데이터를 처리할 때 유용합니다.
$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);플래시 데이터
플래시 데이터는 바로 다음 요청까지만 세션에 유지되는 일회성 데이터입니다. 그 다음 요청이 끝나면 자동으로 삭제됩니다. 로그인 성공/실패 메시지처럼 한 번만 보여주면 되는 상태 알림에 주로 활용합니다.
$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를 재생성하는 주요 목적은 세션 고정(Session Fixation) 공격을 방지하기 위함입니다. 공격자가 미리 세션 ID를 심어두고 사용자가 로그인한 뒤에도 동일한 세션을 사용하도록 유도하는 공격 패턴입니다.
Laravel 스타터 킷 또는 Laravel Fortify를 사용할 경우 인증 시 세션 ID가 자동으로 재생성됩니다. 직접 재생성이 필요하다면 regenerate 메서드를 사용하세요.
$request->session()->regenerate();세션 ID를 재생성하는 동시에 세션의 모든 데이터를 삭제하려면 invalidate 메서드를 사용합니다.
$request->session()->invalidate();세션 블로킹
WARNING
세션 블로킹을 사용하려면 원자적 잠금(Atomic Locks)을 지원하는 캐시 드라이버가 필요합니다. 현재 지원되는 드라이버는 memcached, dynamodb, redis, mongodb(공식 mongodb/laravel-mongodb 패키지), database, file, array입니다. cookie 세션 드라이버와는 함께 사용할 수 없습니다.
기본적으로 Laravel은 동일한 세션을 사용하는 여러 요청을 동시에 처리합니다. 예를 들어 JavaScript에서 두 개의 HTTP 요청을 동시에 보내면 두 요청이 함께 실행됩니다. 대부분의 애플리케이션에서는 문제가 없지만, 서로 다른 두 엔드포인트에 동시에 세션 쓰기 요청이 발생할 경우 데이터가 유실될 수 있습니다.
이를 방지하기 위해 Laravel은 특정 세션에 대한 동시 요청을 제한하는 기능을 제공합니다. 라우트 정의에 block 메서드를 체이닝하면 됩니다. 아래 예시에서 /profile 엔드포인트로 들어온 요청은 세션 잠금을 획득합니다. 이 잠금이 유지되는 동안 동일한 세션 ID를 가진 /profile 또는 /order 요청은 첫 번째 요청이 끝날 때까지 대기합니다.
Route::post('/profile', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);
Route::post('/order', function () {
// ...
})->block($lockSeconds = 10, $waitSeconds = 10);block 메서드는 두 개의 선택적 인수를 받습니다.
- 첫 번째 인수 (
$lockSeconds): 세션 잠금을 최대 몇 초 동안 유지할지를 지정합니다. 요청이 그 전에 완료되면 잠금이 조기 해제됩니다. - 두 번째 인수 (
$waitSeconds): 잠금을 획득하기 위해 최대 몇 초 동안 대기할지를 지정합니다. 이 시간 내에 잠금을 획득하지 못하면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) {}
}NOTE
Laravel은 확장 기능을 위한 별도 디렉터리를 강제하지 않습니다. 위 예시처럼 App\Extensions 디렉터리를 만들어 관리하거나 원하는 위치에 자유롭게 배치하세요.
각 메서드의 역할은 다음과 같습니다.
open- 주로 파일 기반 세션 시스템에서 사용합니다. Laravel의file드라이버가 이를 처리하므로 대부분의 경우 비워두면 됩니다.close-open과 마찬가지로 대부분의 드라이버에서 필요하지 않습니다.read- 주어진$sessionId에 해당하는 세션 데이터를 문자열로 반환합니다. 직렬화/역직렬화는 Laravel이 처리하므로 드라이버 내에서 별도로 처리할 필요가 없습니다.write- 주어진$sessionId와$data문자열을 MongoDB 등의 영구 저장소에 저장합니다. 마찬가지로 직렬화는 Laravel이 이미 처리합니다.destroy- 주어진$sessionId에 해당하는 데이터를 영구 저장소에서 삭제합니다.gc- 주어진$lifetime(UNIX 타임스탬프)보다 오래된 세션 데이터를 모두 삭제합니다. Memcached, Redis처럼 자동 만료를 지원하는 시스템에서는 비워두면 됩니다.
드라이버 등록
드라이버 구현이 완료되면 Laravel에 등록해야 합니다. Session 파사드의 extend 메서드를 서비스 프로바이더의 boot 메서드에서 호출하세요. 기존 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;
});
}
}드라이버 등록 후에는 .env 파일의 SESSION_DRIVER 환경 변수를 mongo로 설정하거나, config/session.php의 driver 옵션을 변경하여 새 드라이버를 사용할 수 있습니다.