캐시
소개
애플리케이션에서 수행하는 일부 데이터 검색 또는 처리 작업은 CPU 집약적이거나 완료하는 데 몇 초가 걸릴 수 있습니다. 이러한 경우, 동일한 데이터에 대한 후속 요청에서 빠르게 검색할 수 있도록 검색된 데이터를 일정 시간 동안 캐시하는 것이 일반적입니다. 캐시된 데이터는 일반적으로 Memcached 또는 Redis와 같은 매우 빠른 데이터 저장소에 저장됩니다.
다행히도 Laravel은 다양한 캐시 백엔드에 대한 표현적이고 통합된 API를 제공하여 매우 빠른 데이터 검색을 활용하고 웹 애플리케이션의 속도를 향상시킬 수 있습니다.
구성
애플리케이션의 캐시 구성 파일은 config/cache.php에 있습니다. 이 파일에서 애플리케이션 전체에서 기본적으로 사용하려는 캐시 저장소를 지정할 수 있습니다. Laravel은 Memcached, Redis, DynamoDB 및 관계형 데이터베이스와 같은 인기 있는 캐싱 백엔드를 기본적으로 지원합니다. 또한 파일 기반 캐시 드라이버를 사용할 수 있으며, array 및 "null" 캐시 드라이버는 자동화된 테스트에 편리한 캐시 백엔드를 제공합니다.
캐시 구성 파일에는 검토할 수 있는 다양한 기타 옵션도 포함되어 있습니다. 기본적으로 Laravel은 애플리케이션의 데이터베이스에 직렬화된 캐시된 객체를 저장하는 database 캐시 드라이버를 사용하도록 구성되어 있습니다.
드라이버 필수 조건
데이터베이스
database 캐시 드라이버를 사용하는 경우 캐시 데이터를 저장할 데이터베이스 테이블이 필요합니다. 일반적으로 이것은 Laravel의 기본 0001_01_01_000001_create_cache_table.php 데이터베이스 마이그레이션에 포함되어 있습니다. 그러나 애플리케이션에 이 마이그레이션이 없는 경우 make:cache-table Artisan 명령을 사용하여 생성할 수 있습니다.
php artisan make:cache-table php artisan migrate
설명:
-
php artisan make:cache-table: 캐시 테이블을 생성하는 Artisan 명령어입니다. -
php artisan migrate: 데이터베이스 마이그레이션을 실행하는 Artisan 명령어입니다.
Memcached
Memcached 드라이버를 사용하려면 Memcached PECL 패키지를 설치해야 합니다. config/cache.php 설정 파일에서 모든 Memcached 서버를 나열할 수 있습니다. 이 파일에는 이미 시작하는 데 도움이 되는 memcached.servers 항목이 포함되어 있습니다.
'memcached' => [ // ... 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ],],
필요한 경우 host 옵션을 UNIX 소켓 경로로 설정할 수 있습니다. 이렇게 하면 port 옵션을 0으로 설정해야 합니다.
'memcached' => [ // ... 'servers' => [ [ 'host' => '/var/run/memcached/memcached.sock', 'port' => 0, 'weight' => 100 ], ],],
Redis
Laravel에서 Redis 캐시를 사용하기 전에 PECL을 통해 PhpRedis PHP 확장 프로그램을 설치하거나 Composer를 통해 predis/predis 패키지(~2.0)를 설치해야 합니다. Laravel Sail에는 이미 이 확장이 포함되어 있습니다. 또한 Laravel Forge 및 Laravel Vapor와 같은 공식 Laravel 배포 플랫폼에는 기본적으로 PhpRedis 확장이 설치되어 있습니다.
Redis 구성에 대한 자세한 내용은 Laravel 문서 페이지를 참조하세요.
DynamoDB
DynamoDB 캐시 드라이버를 사용하기 전에 캐시된 모든 데이터를 저장할 DynamoDB 테이블을 생성해야 합니다. 일반적으로 이 테이블의 이름은 cache여야 합니다. 그러나 cache 구성 파일 내에서 stores.dynamodb.table 구성 값에 따라 테이블 이름을 지정해야 합니다. 테이블 이름은 DYNAMODB_CACHE_TABLE 환경 변수를 통해 설정할 수도 있습니다.
이 테이블에는 애플리케이션의 cache 구성 파일 내에서 stores.dynamodb.attributes.key 구성 항목 값에 해당하는 이름을 가진 문자열 파티션 키도 있어야 합니다. 기본적으로 파티션 키의 이름은 key여야 합니다.
일반적으로 DynamoDB는 테이블에서 만료된 항목을 적극적으로 제거하지 않습니다. 따라서 테이블에서 TTL(Time to Live)을 활성화해야 합니다. 테이블의 TTL 설정을 구성할 때 TTL 속성 이름을 expires_at로 설정해야 합니다.
다음으로, Laravel 애플리케이션이 DynamoDB와 통신할 수 있도록 AWS SDK를 설치합니다.
composer require aws/aws-sdk-php
또한, DynamoDB 캐시 저장소 구성 옵션에 대한 값이 제공되었는지 확인해야 합니다. 일반적으로 AWS_ACCESS_KEY_ID 및 AWS_SECRET_ACCESS_KEY와 같은 이러한 옵션은 애플리케이션의 .env 구성 파일에 정의되어야 합니다.
'dynamodb' => [ 'driver' => 'dynamodb', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 'endpoint' => env('DYNAMODB_ENDPOINT'),],
MongoDB
MongoDB를 사용하고 있다면, 공식 mongodb/laravel-mongodb 패키지에서 mongodb 캐시 드라이버를 제공하며 mongodb 데이터베이스 연결을 사용하여 구성할 수 있습니다. MongoDB는 만료된 캐시 항목을 자동으로 삭제하는 데 사용할 수 있는 TTL 인덱스를 지원합니다.
MongoDB 구성에 대한 자세한 내용은 MongoDB 캐시 및 잠금 문서를 참조하세요.
캐시 사용법
캐시 인스턴스 가져오기
캐시 저장소 인스턴스를 가져오려면 이 문서 전체에서 사용할 Cache 파사드를 사용하면 됩니다. Cache 파사드는 Laravel 캐시 계약의 기본 구현에 대한 편리하고 간결한 접근 방식을 제공합니다.
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller{ /** * 애플리케이션의 모든 사용자 목록을 표시합니다. */ public function index(): array { $value = Cache::get('key'); return [ // ... ]; }}
여러 캐시 저장소에 접근
Cache 파사드를 사용하여 store 메서드를 통해 다양한 캐시 저장소에 접근할 수 있습니다. store 메서드에 전달된 키는 cache 구성 파일의 stores 구성 배열에 나열된 저장소 중 하나와 일치해야 합니다.
$value = Cache::store('file')->get('foo'); Cache::store('redis')->put('bar', 'baz', 600); // 10분
캐시에서 항목 검색
Cache 파사드의 get 메서드는 캐시에서 항목을 검색하는 데 사용됩니다. 항목이 캐시에 존재하지 않으면 null이 반환됩니다. 원하는 경우 항목이 존재하지 않을 때 반환하려는 기본값을 지정하는 두 번째 인수를 get 메서드에 전달할 수 있습니다.
$value = Cache::get('key'); $value = Cache::get('key', 'default');
기본값으로 클로저를 전달할 수도 있습니다. 지정된 항목이 캐시에 존재하지 않으면 클로저의 결과가 반환됩니다. 클로저를 전달하면 데이터베이스 또는 기타 외부 서비스에서 기본값 검색을 연기할 수 있습니다.
$value = Cache::get('key', function () { return DB::table(/* ... */)->get();});
항목 존재 여부 확인
has 메서드는 항목이 캐시에 존재하는지 확인하는 데 사용할 수 있습니다. 이 메서드는 항목이 존재하지만 값이 null인 경우에도 false를 반환합니다.
if (Cache::has('key')) { // ...}
값 증가/감소
increment 및 decrement 메서드는 캐시에서 정수 항목의 값을 조정하는 데 사용할 수 있습니다. 이러한 메서드 모두 항목의 값을 증가시키거나 감소시킬 양을 나타내는 선택적 두 번째 인수를 허용합니다.
// 값이 존재하지 않으면 초기화합니다...Cache::add('key', 0, now()->addHours(4)); // 값을 증가시키거나 감소시킵니다...Cache::increment('key');Cache::increment('key', $amount);Cache::decrement('key');Cache::decrement('key', $amount);
검색 및 저장
캐시에서 항목을 검색하고 요청된 항목이 존재하지 않는 경우 기본값을 저장하고 싶을 때가 있습니다. 예를 들어 캐시에서 모든 사용자를 검색하거나, 존재하지 않으면 데이터베이스에서 검색하여 캐시에 추가할 수 있습니다. Cache::remember 메서드를 사용하여 이를 수행할 수 있습니다.
$value = Cache::remember('users', $seconds, function () { return DB::table('users')->get();});
캐시에 항목이 존재하지 않으면 remember 메서드에 전달된 클로저가 실행되고 결과가 캐시에 배치됩니다.
rememberForever 메서드를 사용하여 캐시에서 항목을 검색하거나 존재하지 않는 경우 영원히 저장할 수 있습니다.
$value = Cache::rememberForever('users', function () { return DB::table('users')->get();});
오래된 동안 재검증
Cache::remember 메서드를 사용할 때 캐시된 값이 만료된 경우 일부 사용자는 응답 시간이 느려질 수 있습니다. 특정 유형의 데이터의 경우 캐시된 값이 백그라운드에서 재계산되는 동안 부분적으로 오래된 데이터를 제공하여 일부 사용자가 캐시된 값을 계산하는 동안 응답 시간이 느려지는 것을 방지하는 것이 유용할 수 있습니다. 이를 "stale-while-revalidate" 패턴이라고 하며, Cache::flexible 메서드는 이 패턴의 구현을 제공합니다.
flexible 메서드는 캐시된 값이 "신선"하다고 간주되는 시간과 "오래된" 상태가 되는 시간을 지정하는 배열을 허용합니다. 배열의 첫 번째 값은 캐시가 신선하다고 간주되는 시간(초)을 나타내고, 두 번째 값은 재계산이 필요하기 전에 오래된 데이터로 제공될 수 있는 시간을 정의합니다.
신선한 기간(첫 번째 값 이전) 내에 요청이 이루어지면 재계산 없이 캐시가 즉시 반환됩니다. 오래된 기간(두 값 사이) 동안 요청이 이루어지면 오래된 값이 사용자에게 제공되고 응답이 사용자에게 전송된 후 캐시된 값을 새로 고치기 위해 지연된 함수가 등록됩니다. 두 번째 값 이후에 요청이 이루어지면 캐시가 만료된 것으로 간주되고 값이 즉시 재계산되어 사용자에게 더 느린 응답이 발생할 수 있습니다.
$value = Cache::flexible('users', [5, 10], function () { return DB::table('users')->get();});
검색 및 삭제
캐시에서 항목을 검색한 다음 항목을 삭제해야 하는 경우 pull 메서드를 사용할 수 있습니다. get 메서드와 마찬가지로 항목이 캐시에 존재하지 않으면 null이 반환됩니다.
$value = Cache::pull('key'); $value = Cache::pull('key', 'default');
캐시에 항목 저장
Cache 파사드의 put 메서드를 사용하여 캐시에 항목을 저장할 수 있습니다.
Cache::put('key', 'value', $seconds = 10);
저장 시간이 put 메서드에 전달되지 않으면 항목이 무기한 저장됩니다.
Cache::put('key', 'value');
정수 값으로 초 수를 전달하는 대신 캐시된 항목의 원하는 만료 시간을 나타내는 DateTime 인스턴스를 전달할 수도 있습니다.
Cache::put('key', 'value', now()->addMinutes(10));
존재하지 않는 경우 저장
add 메서드는 캐시 저장소에 항목이 아직 존재하지 않는 경우에만 항목을 캐시에 추가합니다. 이 메서드는 항목이 실제로 캐시에 추가된 경우 true를 반환합니다. 그렇지 않으면 메서드는 false를 반환합니다. add 메서드는 원자적 연산입니다.
Cache::add('key', 'value', $seconds);
항목을 영원히 저장
forever 메서드는 항목을 캐시에 영구적으로 저장하는 데 사용할 수 있습니다. 이러한 항목은 만료되지 않으므로 forget 메서드를 사용하여 캐시에서 수동으로 제거해야 합니다.
Cache::forever('key', 'value');
Memcached 드라이버를 사용하는 경우 "영원히" 저장된 항목은 캐시가 크기 제한에 도달하면 제거될 수 있습니다.
캐시에서 항목 제거
forget 메서드를 사용하여 캐시에서 항목을 제거할 수 있습니다.
Cache::forget('key');
만료 초 수를 0 또는 음수로 제공하여 항목을 제거할 수도 있습니다.
Cache::put('key', 'value', 0); Cache::put('key', 'value', -5);
flush 메서드를 사용하여 전체 캐시를 지울 수 있습니다.
Cache::flush();
캐시를 플러시하면 구성된 캐시 "접두사"가 적용되지 않고 캐시에서 모든 항목이 제거됩니다. 다른 애플리케이션에서 공유하는 캐시를 지울 때 이 점을 신중하게 고려하십시오.
캐시 도우미
Cache 파사드를 사용하는 것 외에도 전역 cache 함수를 사용하여 캐시를 통해 데이터를 검색하고 저장할 수 있습니다. 단일 문자열 인수로 cache 함수를 호출하면 주어진 키의 값이 반환됩니다.
$value = cache('key');
키/값 쌍의 배열과 만료 시간을 함수에 제공하면 지정된 시간 동안 캐시에 값을 저장합니다.
cache(['key' => 'value'], $seconds); cache(['key' => 'value'], now()->addMinutes(10));
인수 없이 cache 함수를 호출하면 Illuminate\Contracts\Cache\Factory 구현의 인스턴스가 반환되어 다른 캐싱 메서드를 호출할 수 있습니다.
cache()->remember('users', $seconds, function () { return DB::table('users')->get();});
전역 cache 함수 호출을 테스트할 때 마치 파사드를 테스트하는 것처럼 Cache::shouldReceive 메서드를 사용할 수 있습니다.
원자적 잠금
이 기능을 활용하려면 애플리케이션에서 기본 캐시 드라이버로 memcached, redis, dynamodb, database, file 또는 array 캐시 드라이버를 사용해야 합니다. 또한 모든 서버는 동일한 중앙 캐시 서버와 통신해야 합니다.
잠금 관리
원자적 잠금은 경합 조건에 대한 걱정 없이 분산 잠금을 조작할 수 있도록 합니다. 예를 들어 Laravel Forge는 원자적 잠금을 사용하여 한 번에 하나의 원격 작업만 서버에서 실행되도록 합니다. Cache::lock 메서드를 사용하여 잠금을 만들고 관리할 수 있습니다.
use Illuminate\Support\Facades\Cache; $lock = Cache::lock('foo', 10); if ($lock->get()) { // 10초 동안 잠금 획득... $lock->release();}
get 메서드는 클로저도 허용합니다. 클로저가 실행되면 Laravel은 자동으로 잠금을 해제합니다.
Cache::lock('foo', 10)->get(function () { // 10초 동안 잠금을 획득하고 자동으로 해제됩니다...});
잠금을 요청하는 시점에 잠금을 사용할 수 없는 경우 Laravel에 지정된 초 동안 대기하도록 지시할 수 있습니다. 지정된 시간 제한 내에 잠금을 획득할 수 없으면 Illuminate\Contracts\Cache\LockTimeoutException이 발생합니다.
use Illuminate\Contracts\Cache\LockTimeoutException; $lock = Cache::lock('foo', 10); try { $lock->block(5); // 최대 5초 동안 기다린 후 잠금을 획득했습니다...} catch (LockTimeoutException $e) { // 잠금을 획득할 수 없습니다...} finally { $lock->release();}
위의 예는 클로저를 block 메서드에 전달하여 단순화할 수 있습니다. 클로저가 이 메서드에 전달되면 Laravel은 지정된 초 동안 잠금을 획득하려고 시도하고 클로저가 실행되면 잠금을 자동으로 해제합니다.
Cache::lock('foo', 10)->block(5, function () { // 최대 5초 동안 기다린 후 잠금을 획득했습니다...});
프로세스 간 잠금 관리
경우에 따라 하나의 프로세스에서 잠금을 획득하고 다른 프로세스에서 해제할 수 있습니다. 예를 들어 웹 요청 중에 잠금을 획득하고 해당 요청에 의해 트리거되는 대기열 작업이 끝날 때 잠금을 해제할 수 있습니다. 이 시나리오에서는 주어진 토큰을 사용하여 작업을 다시 잠금으로 인스턴스화할 수 있도록 잠금의 범위가 지정된 "소유자 토큰"을 대기열 작업에 전달해야 합니다.
아래 예에서는 잠금이 성공적으로 획득된 경우 대기열 작업을 디스패치합니다. 또한 잠금의 owner 메서드를 통해 잠금의 소유자 토큰을 대기열 작업에 전달합니다.
$podcast = Podcast::find($id); $lock = Cache::lock('processing', 120); if ($lock->get()) { ProcessPodcast::dispatch($podcast, $lock->owner());}
애플리케이션의 ProcessPodcast 작업 내에서 소유자 토큰을 사용하여 잠금을 복원하고 해제할 수 있습니다.
Cache::restoreLock('processing', $this->owner)->release();
현재 소유자를 고려하지 않고 잠금을 해제하려면 forceRelease 메서드를 사용할 수 있습니다.
Cache::lock('processing')->forceRelease();
사용자 지정 캐시 드라이버 추가
드라이버 작성
사용자 지정 캐시 드라이버를 만들려면 먼저 Illuminate\Contracts\Cache\Store 계약을 구현해야 합니다. 따라서 MongoDB 캐시 구현은 다음과 같이 보일 수 있습니다.
<?php namespace App\Extensions; use Illuminate\Contracts\Cache\Store; class MongoStore implements Store{ public function get($key) {} public function many(array $keys) {} public function put($key, $value, $seconds) {} public function putMany(array $values, $seconds) {} public function increment($key, $value = 1) {} public function decrement($key, $value = 1) {} public function forever($key, $value) {} public function forget($key) {} public function flush() {} public function getPrefix() {}}
MongoDB 연결을 사용하여 이러한 각 메서드를 구현하기만 하면 됩니다. 이러한 각 메서드를 구현하는 방법에 대한 예는 Laravel 프레임워크 소스 코드의 Illuminate\Cache\MemcachedStore를 참조하세요. 구현이 완료되면 Cache 파사드의 extend 메서드를 호출하여 사용자 지정 드라이버 등록을 완료할 수 있습니다.
Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore);});
사용자 지정 캐시 드라이버 코드를 어디에 배치할지 궁금하다면 app 디렉토리 내에 Extensions 네임스페이스를 만들 수 있습니다. 그러나 Laravel에는 엄격한 애플리케이션 구조가 없으며 기본 설정에 따라 애플리케이션을 자유롭게 구성할 수 있습니다.
드라이버 등록
Laravel에 사용자 지정 캐시 드라이버를 등록하려면 Cache 파사드에서 extend 메서드를 사용합니다. 다른 서비스 제공업체가 boot 메서드 내에서 캐시된 값을 읽으려고 할 수 있으므로 booting 콜백 내에서 사용자 지정 드라이버를 등록합니다. booting 콜백을 사용하면 애플리케이션의 서비스 제공업체에서 boot 메서드가 호출되기 직전이지만 모든 서비스 제공업체에서 register 메서드가 호출된 후에 사용자 지정 드라이버가 등록되도록 할 수 있습니다. 애플리케이션의 App\Providers\AppServiceProvider 클래스의 register 메서드 내에서 booting 콜백을 등록합니다.
<?php namespace App\Providers; use App\Extensions\MongoStore;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\Cache;use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * 모든 애플리케이션 서비스를 등록합니다. */ public function register(): void { $this->app->booting(function () { Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); }); } /** * 모든 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { // ... }}
extend 메서드에 전달된 첫 번째 인수는 드라이버의 이름입니다. 이는 config/cache.php 구성 파일의 driver 옵션에 해당합니다. 두 번째 인수는 Illuminate\Cache\Repository 인스턴스를 반환해야 하는 클로저입니다. 클로저에는 서비스 컨테이너의 인스턴스인 $app 인스턴스가 전달됩니다.
확장이 등록되면 애플리케이션의 config/cache.php 구성 파일 내에서 CACHE_STORE 환경 변수 또는 default 옵션을 확장의 이름으로 업데이트합니다.
이벤트
모든 캐시 작업에서 코드를 실행하려면 캐시에서 디스패치한 다양한 이벤트를 수신할 수 있습니다.
| 이벤트 이름 |
|---|
Illuminate\Cache\Events\CacheHit |
Illuminate\Cache\Events\CacheMissed |
Illuminate\Cache\Events\KeyForgotten |
Illuminate\Cache\Events\KeyWritten |
성능을 높이려면 애플리케이션의 config/cache.php 구성 파일에서 지정된 캐시 저장소에 대해 events 구성 옵션을 false로 설정하여 캐시 이벤트를 비활성화할 수 있습니다.
'database' => [ 'driver' => 'database', // ... 'events' => false,],