HTTP Session

[su_heading size=”30″ align=”left” margin=”10″ class=”top”]# Introduction[/su_heading]

HTTP 구동 어플리케이션은 비상태이고, 세션은 사용자의 여러 요청에 대한 정보를 저장하는 방법을 제공한다. 라라벨은 표현적이고 일관성 있는 API를 통하여 엑세스되는 다양한 세션 백엔드를 제공한다. Memcached, Redis와 같은 대중적인 백엔드를 지원하며 데이터베이스는 기본적으로 포함되어 있다.

Configuration

세션 구성 파일은 [su_label type=”black”]config\session.php[/su_label]에 저장된다. 이 파일에서 사용 가능한 옵션을 확인할 수 있다. 기본적으로 라라벨은 많은 어플리케이션에서 잘 동작할 수 있는 [su_label type=”black”]file[/su_label] 세션 드라이버를 사용하도록 구성되어 있다. 실 운영 어플리케이션에는 보다 빠른 세션 성능을 위해 [su_label type=”black”]memcached[/su_label] 또는 [su_label type=”black”]redis[/su_label] 드라이버를 사용하는 것을 고려할 수 있다.

세션 [su_label type=”black”]driver[/su_label] 구성 옵션은 각각의 요청에 대해 세션 데이터가 어디에 저장될지를 정의한다. 라라벨은 훌륭한 몇 개의 드라이버를 기본적으로 제공한다.

  • [su_label type=”black”]file[/su_label] – 세션은 [su_label type=”black”]storage/framework/sessions[/su_label]에 저장된다.
  • [su_label type=”black”]cookie[/su_label] – 세션은 암호화 된 쿠키에 안전하게 저장된다.
  • [su_label type=”black”]database[/su_label] – 세션은 관계형 데이터베이스에 저장된다.
  • [su_label type=”black”]memcached / radis[/su_label] – 세션은 캐시 기반 저장소 중 빠른 하나에 저장된다.
  • [su_label type=”black”]array[/su_label] – 세션은 PHP 배열에 저장되며 유지되지 않는다.

[su_note note_color=”#40749c” text_color=”#ffffff”]배열 드라이버는 테스트 중에 사용하고 세션에 데이터가 유지되지 못하게한다.[/su_note]

Driver Prerequisites

Database

[su_label type=”black”]database[/su_label] 세션 드라이버를 사용하는 경우, 세션 아이템을 포함하는 테이블을 만들어야 한다. 다음은 테이블의 [su_label type=”black”]Schema[/su_label] 선언 예제이다.

Schema::create('sessions', function ($table) {
    $table->string('id')->unique();
    $table->unsignedInteger('user_id')->nullable();
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->text('payload');
    $table->integer('last_activity');
}

[su_label type=”black”]session::table[/su_label]  artisan 명령어를 사용하여 마이그레이션을 생성할 수 있다.

$ php artisan session:table
$ php artisan migrate

Redis

라라벨에서 Redis 세션을 사용하기 전에 컴포저를 통해 [su_label type=”black”]predis/predis[/su_label] 패키지(~1.0)을 설치해야 한다. [su_label type=”black”]database[/su_label] 구성 파일에 Redis 연결을 구성할 수 있다. [su_label type=”black”]session[/su_label] 구성 파일에서 [su_label type=”black”]connection[/su_label] 옵션은 세션에서 사용되는 Redis 연결을 지정하는데 사용할 수 있다.

[su_heading size=”30″ align=”left” margin=”10″]# Using The Session[/su_heading]

Retrieving Data

라라벨에서 세션 데이터를 사용하는 두 가지 기본적인 방법은 [su_label type=”black”]session[/su_label] 도우미와 [su_label type=”black”]request[/su_label] 인스턴스를 통한 방법이다. 우선 컨트롤러 메서드에 타입 힌트를 사용하여 [su_label type=”black”]request[/su_label] 인스턴스를 통해 세션에 엑세스하는 방법을 살펴보자. 컨트롤러 메서드 종속성은 라라벨 서비스 컨테이너를 통해 자동적으로 주입되는 것을 기억하자.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    public function show(Request $request, $id)
    {
        $value = $request->session()->get('key');

        //
    }
}

또한 세션에서 값을 가져올 때 [su_label type=”black”]get[/su_label] 메서드에 두 번째 인수르 기본값을 전달할 수 있다. 세션에 해당 키 값이 없을 경우 이 기본값이 리턴된다. [su_label type=”black”]get[/su_label] 메서드에 기본값으로 [su_label type=”black”]Closure[/su_label]를 전달하고 세션에 해당 키 값이 존재하지 않을 경우 [su_label type=”black”]Closure[/su_label]가 실행되고 결과가 리턴된다.

$value = $request->session()->get('key', 'default');

$value = $request->session()->get('key', function () {
    return 'default';
});

The Global Session Helper

세션에 데이터를 저장하고 가져오기 위해 전역 [su_label type=”black”]session[/su_label] PHP 함수를 사용할 수 있다. [su_label type=”black”]session[/su_label] 도우미가 하나의 문자열 인수로 호출되면 해당 세션 키의 값이 리턴된다. [su_label type=”black”]session[/su_label] 도우미가 키/값 쌍의 배열로 호출되면 해당 값이 세션에 저장된다.

Route::get('home', function () {
    // Retrieve a piece of data from the session...
    $value = session('key');

    // Specifying a default value...
    $value = session('key', 'default');

    // Store a piece of data in the session...
    session(['key' => 'value']);
});

[su_note note_color=”#40749c” text_color=”#ffffff”]HTTP 요청 인스턴스를 통한 세션 사용 방법과 전역 [su_label type=”black”]session[/su_label] 도우미 사용 방법 사이에 실제적인 차이가 거의 없다. 두 가지 메서드는 모든 테스트 케이스에서 사용가능한 [su_label type=”black”]assertSessionHas[/su_label] 메서드를 통해 테스트할 수 있다.[/su_note]

Retrieving All Session Data

세션의 모든 데이터를 가져오고 싶을 경우 [su_label type=”black”]all[/su_label] 메서드를 사용할 수 있다.

$data = $request->session()->all();

Determining If An Item Exists In The Session

세션에 값이 존재하는지 확인하기 위해 [su_label type=”black”]has[/su_label] 메서드를 사용할 수 있다. [su_label type=”black”]has[/su_label] 메서드는 세션에 값이 존재할 경우 [su_label type=”black”]true[/su_label], 존재하지 않을 경우 [su_label type=”black”]null[/su_label] 값을 리턴한다.

if ($request->session()->has('users')) {
    //
}

만약 값이 [su_label type=”black”]null[/su_label]인 세션 값이 존재하는지 확인할 경우 [su_label type=”black”]exists[/su_label] 메서드를 사용할 수 있다. [su_label type=”black”]exists[/su_label] 메서드는 해당 키 값의 값이 [su_label type=”black”]null[/su_label]일 경우 [su_label type=”black”]true[/su_label]를 리턴한다.

if ($request->session()->exists('users')) {
    //
}

Storing Data

세션에 데이터를 저장하기 위해, [su_label type=”black”]put[/su_label] 메서드나 [su_label type=”black”]session[/su_label] 도우미를 사용한다.

// Via a request instance...
$request->session()->put('key', 'value');

// Via the global helper...
session(['key' => 'value']);

Pushing To Array Session Values

[su_label type=”black”]push[/su_label] 메서드는 배열인 세션 값에 새로운 값을 넣는데 사용할 수 있다. 예를 들어, [su_label type=”black”]user.teams[/su_label]키에 팀 이름 배열이 포함되어있을 경우 새 값을 다음과 같이 배열에 추가할 수 있다.

$request->session()->push('user.teams', 'developers');

Retrieving & Deleting An Item

[su_label type=”black”]pull[/su_label] 메서드는 단일문에서 세션으로부터 값을 가져오고 아이템을 삭제한다.

$value = $request->session()->pull('key', 'default');

Flash Data

때로는 다음 요청에만 세션에 아이템을 저장하고 싶을 경우도 있다. 이럴 때 [su_label type=”black”]flash[/su_label] 메서드를 사용한다. 이 메서드를 사용하여 세션에 데이터를 저장하면 후속 HTTP 요청 동안에만 사용할 수 있으며 이후에는 삭제된다. 플래시 데이터는 상태 메시지와 같이 짧게 유지하고 싶을 때 유용하다.

$request->session()->flash('status', 'Task was successful!');

플래시 데이터를 여러 요청에 대해 유지하[su_label type=”black”]reflash[/su_label] 메서드를 사용하여 추가적인 요청에 플래시 데이터를 유지할 수 있다. 특정 플래시 데이터만 유지해야 할 경우 [su_label type=”black”]keep[/su_label] 메서드를 사용할 수 있다.

$request->session()->reflash();

$request->session()->keep(['username', 'email']);

Deleting Data

[su_label type=”black”]forget[/su_label] 메서드는 지정된 세션 데이터를 삭제한다. 세션의 모든 데이터를 삭제하길 원할 경우 [su_label type=”black”]flush[/su_label] 메서드를 사용할 수 있다.

$request->session()->forget('key');

$request->session()->flush();

Regenerating The Session ID

어플리케이션에서 악의적인 사용자의 세션 고정 공격을 막기 위해 세션 ID를 재생성하는 경우가 종종 있다.

라라벨은 내장된 [su_label type=”black”]LoginController[/su_label]를 사용할 경우 인증과정에서 세션 ID를 자동으로 재생성한다. 그러나 세션 ID를 직접 재생성해야 할 경우 [su_label type=”black”]regenerate[/su_label] 메서드를 사용할 수 있다.

$request->session()->regenerate();

[su_heading size=”30″ align=”left” margin=”10″]# Adding Custom Session Drivers[/su_heading]

Implementing The Driver

사용자 정의 세션 드라이버는 [su_label type=”black”]SessionHandlerInterface[/su_label]를 구현해야 한다. 이 인터페이스는 구현해야하는 몇 가지 간단한 메서드를 포함하고 있다. 스텁된 MongoDB 구현은 다은과 같다.

<?php

namespace App\Extensions;

class MongoHandler 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) {}
}

[su_note note_color=”#40749c” text_color=”#ffffff”]라라벨은 확장 디렉토리를 포함하고 있지 않다. 확장 디렉토리는 개발자가 원하는 곳에 자유롭게 배치할 수 있다.[/su_note]

재정의해야 할 메서드들의 목적을 쉽게 이해할 수 없기 때문에 각각의 메서드의 기능을 빠르게 살펴본다.

  • [su_label type=”black”]open[/su_label] 메서드는 파일 기반 세션 저장 시스템에 사용된다. 라라벨은 [su_label type=”black”]file[/su_label] 세션 드라이버와 함께 제공되기 때문에 이 메서드에 아무것도 넣을 필요가 없다. 빈 상태로 남겨둬도 된다. PHP가 이 메소드를 구현해야한다는 것은 인터페이스 설계가 좋지 않다는 것이다.
  • [su_label type=”black”]close[/su_label] 메서드는 [su_label type=”black”]open[/su_label] 메서드와 같이 일반적으로 무시될 수 있다. 대부분의 드라이버는 이 메서드를 필요로 하지 않는다.
  • [su_label type=”black”]read[/su_label] 메서드는 주어진 [su_label type=”black”]$sessionId[/su_label]와 연관된 세션 데이터의 버전 문자열을 리턴해야 한다. 라라벨이 직렬화를 수행하므로 드라이버는 세션 데이터를 저장하거나 가져올 때 직렬화나 인코딩할 필요가 없다.
  • [su_label type=”black”]write[/su_label] 메서드는 MongoDB, Mynamo와 같은 영구 저장 시스템에 [su_label type=”black”]$sessionId[/su_label]와 함께 연관된 주어진 [su_label type=”black”]$data[/su_label] 문자열을 써야한다. 다시 한 번 말하지만 어떠한 직렬화도 수행하지 말아야 한다. 라라벨이 이미 수행했을 것이다.
  • [su_label type=”black”]destroy[/su_label] 메서드는 영구 저장소로부터 [su_label type=”black”]$sessionId[/su_label]와 연관된 데이터를 삭제해야한다.
  • [su_label type=”black”]gc[/su_label] 메서드는 UNIX의 타임스탬프 형식의 주어진 [su_label type=”black”]$lifetime[/su_label] 보다 오래된 모든 세션 데이터를 제거해야 한다. Memcached와 Redis 같은 자체 만료 시스템의 경우에는 이 메서드를 비워둘 수 있다.

Registering The Driver

드라이버가 구현되면 프레임워크에 등록할 준비가 된 것이다. 라라벨 세션 백엔드에 드라이버를 추가하려면 세션 파사드의 [su_label type=”black”]extend[/su_label] 메서드를 사용할 수 있다. 서비스 프로바이더의 [su_label type=”black”]boot[/su_label] 메서드에서 [su_label type=”black”]extend[/su_label] 메서드를 호출해야한다. 기본적으로 제공되는 [su_label type=”black”]AppServiceProvider[/su_label] 또는 새롭게 생성한 프로바이더에서 이 작업을 수행할 수 있다.

<?php

namespace App\Providers;

use App\Extensions\MongoSessionStore;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Session::extend('mongo', function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionStore;
        });
    }

    public function register()
    {
        //
    }
}

세션 드라이버가 등록되면 [su_label type=”black”]config/session.php[/su_label] 구성 파일에서 [su_label type=”black”]mongo[/su_label] 드라이버가 사용될 것이다.