캐시
번역일: 2026년 6월 21일
캐시
소개
애플리케이션에서 수행하는 일부 데이터 조회나 처리 작업은 CPU를 많이 사용하거나 완료하는 데 수 초가 걸릴 수 있습니다. 이런 경우, 조회한 데이터를 일정 시간 동안 캐시에 저장해두면 동일한 요청이 반복될 때 훨씬 빠르게 응답할 수 있습니다. 캐시된 데이터는 보통 Memcached나 Redis처럼 매우 빠른 데이터 저장소에 보관됩니다.
Laravel은 다양한 캐시 백엔드를 위한 일관된 통합 API를 제공합니다. 덕분에 빠른 데이터 조회 성능을 손쉽게 활용하여 웹 애플리케이션의 응답 속도를 높일 수 있습니다.
설정
캐시 설정 파일은 config/cache.php에 위치합니다. 이 파일에서 애플리케이션 전체에서 기본으로 사용할 캐시 드라이버를 지정할 수 있습니다. Laravel은 Memcached, Redis, DynamoDB, 관계형 데이터베이스 등 널리 사용되는 캐시 백엔드를 기본 지원합니다. 파일 기반 캐시 드라이버도 사용 가능하며, array와 null 드라이버는 자동화 테스트 환경에 편리하게 활용할 수 있습니다.
설정 파일 안에는 각 옵션에 대한 설명 주석이 포함되어 있으므로 한번 읽어보시길 권장합니다. 기본값은 file 캐시 드라이버로, 직렬화된 캐시 객체를 서버 파일시스템에 저장합니다. 규모가 큰 애플리케이션이라면 Memcached나 Redis처럼 더 강력한 드라이버 사용을 권장합니다. 동일한 드라이버에 대해 여러 캐시 설정을 구성하는 것도 가능합니다.
드라이버 사전 준비
Database
database 캐시 드라이버를 사용하려면 캐시 항목을 저장할 테이블이 필요합니다. 아래는 해당 테이블을 생성하는 Schema 예시입니다.
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
});NOTE
php artisan cache:table 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' => [
[
'host' => '/var/run/memcached/memcached.sock',
'port' => 0,
'weight' => 100
],
],Redis
Laravel에서 Redis 캐시를 사용하려면 PECL을 통해 PhpRedis PHP 익스텐션을 설치하거나, Composer로 predis/predis 패키지(~1.0)를 설치해야 합니다. Laravel Sail에는 PhpRedis 익스텐션이 기본 포함되어 있으며, Laravel Forge나 Laravel Vapor 같은 공식 배포 플랫폼에도 기본으로 설치되어 있습니다.
Redis 설정에 대한 자세한 내용은 Laravel Redis 문서를 참고하세요.
DynamoDB
DynamoDB 캐시 드라이버를 사용하기 전에, 캐시 데이터를 저장할 DynamoDB 테이블을 생성해야 합니다. 일반적으로 테이블 이름은 cache로 지정하지만, config/cache.php의 stores.dynamodb.table 설정값에 맞춰 이름을 정하면 됩니다.
이 테이블에는 문자열 파티션 키가 필요하며, 키 이름은 설정 파일의 stores.dynamodb.attributes.key 값과 일치해야 합니다. 기본값은 key입니다.
캐시 사용법
캐시 인스턴스 가져오기
캐시 저장소 인스턴스를 얻으려면 Cache 파사드를 사용하면 됩니다. 이 문서에서도 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 메서드에 전달하는 키는 config/cache.php의 stores 배열에 정의된 이름과 일치해야 합니다.
$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 600); // 10분캐시에서 항목 조회하기
Cache 파사드의 get 메서드로 캐시에서 항목을 조회할 수 있습니다. 항목이 존재하지 않으면 null이 반환됩니다. 두 번째 인자로 기본값을 지정하면, 항목이 없을 때 해당 값이 반환됩니다.
$value = Cache::get('key');
$value = Cache::get('key', '기본값');기본값으로 클로저를 전달할 수도 있습니다. 항목이 존재하지 않을 때 클로저가 실행되어 그 결과가 반환됩니다. 클로저를 사용하면 데이터베이스나 외부 서비스에 대한 기본값 조회를 지연시킬 수 있습니다.
$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);조회 후 없으면 저장 (remember)
캐시에서 항목을 조회하되, 없으면 기본값을 저장하고 싶을 때가 있습니다. 예를 들어 캐시에서 전체 사용자 목록을 가져오고, 없으면 데이터베이스에서 조회한 뒤 캐시에 저장하는 패턴입니다. 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();
});조회 후 삭제 (pull)
캐시에서 항목을 조회한 뒤 즉시 삭제하려면 pull 메서드를 사용하세요. get과 마찬가지로 항목이 없으면 null을 반환합니다.
$value = Cache::pull('key');캐시에 항목 저장하기
Cache 파사드의 put 메서드로 캐시에 항목을 저장할 수 있습니다.
Cache::put('key', 'value', $seconds = 10);만료 시간을 지정하지 않으면 항목은 영구적으로 저장됩니다.
Cache::put('key', 'value');초(second) 단위 정수 대신, 만료 시각을 나타내는 DateTime 인스턴스를 전달할 수도 있습니다.
Cache::put('key', 'value', now()->addMinutes(10));없을 때만 저장 (add)
add 메서드는 캐시에 해당 항목이 존재하지 않을 때만 저장합니다. 실제로 저장되면 true, 이미 존재하면 false를 반환합니다. add는 원자적(atomic) 연산으로 처리됩니다.
Cache::add('key', 'value', $seconds);영구 저장 (forever)
forever 메서드를 사용하면 만료 시간 없이 항목을 영구적으로 캐시에 저장합니다. 이렇게 저장된 항목은 forget 메서드로 직접 삭제해야 합니다.
Cache::forever('key', 'value');NOTE
Memcached 드라이버를 사용하는 경우, 캐시 저장소가 용량 한도에 도달하면 forever로 저장한 항목도 삭제될 수 있습니다.
캐시에서 항목 삭제하기
forget 메서드로 캐시에서 항목을 삭제할 수 있습니다.
Cache::forget('key');만료 시간을 0 또는 음수로 지정해도 항목이 즉시 삭제됩니다.
Cache::put('key', 'value', 0);
Cache::put('key', 'value', -5);캐시 전체를 비우려면 flush 메서드를 사용하세요.
Cache::flush();WARNING
flush는 설정된 캐시 "prefix"를 무시하고 캐시 저장소의 모든 항목을 삭제합니다. 여러 애플리케이션이 공유하는 캐시를 비울 때는 이 점을 반드시 주의하세요.
cache 헬퍼 함수
Cache 파사드 외에도 전역 cache 헬퍼 함수로 데이터를 조회하거나 저장할 수 있습니다. 문자열 인자 하나만 전달하면 해당 키의 값을 반환합니다.
$value = cache('key');키/값 쌍의 배열과 만료 시간을 전달하면 해당 기간 동안 캐시에 저장합니다.
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));인자 없이 호출하면 Illuminate\Contracts\Cache\Factory 구현 인스턴스를 반환하므로, 다른 캐시 메서드를 이어서 호출할 수 있습니다.
cache()->remember('users', $seconds, function () {
return DB::table('users')->get();
});NOTE
전역 cache 함수를 테스트할 때는 파사드 테스트와 마찬가지로 Cache::shouldReceive 메서드를 사용할 수 있습니다. 자세한 내용은 파사드 모킹 문서를 참고하세요.
원자적 잠금(Atomic Locks)
WARNING
이 기능을 사용하려면 애플리케이션의 기본 캐시 드라이버가 memcached, redis, dynamodb, database, file, array 중 하나여야 합니다. 또한 모든 서버가 동일한 중앙 캐시 서버와 통신해야 합니다.
원자적 잠금은 경쟁 조건(race condition)을 걱정하지 않고 분산 잠금을 관리할 수 있게 해주는 기능입니다. 예를 들어, 동시에 여러 요청이 들어올 때 특정 작업이 단 하나의 프로세스에서만 실행되도록 보장하고 싶을 때 유용합니다.
드라이버 사전 준비
Database
database 캐시 드라이버로 원자적 잠금을 사용하려면 잠금 정보를 저장할 별도 테이블이 필요합니다.
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration');
});NOTE
cache:table Artisan 명령어로 캐시 테이블 마이그레이션을 생성한 경우, 해당 마이그레이션 파일에 이미 cache_locks 테이블 정의가 포함되어 있습니다.
잠금 관리
Cache::lock 메서드로 잠금을 생성하고 관리할 수 있습니다.
use Illuminate\Support\Facades\Cache;
$lock = Cache::lock('foo', 10);
if ($lock->get()) {
// 10초 동안 잠금 획득...
$lock->release();
}get 메서드에 클로저를 전달하면 클로저 실행 후 잠금이 자동으로 해제됩니다.
Cache::lock('foo', 10)->get(function () {
// 10초 동안 잠금이 획득되고 자동으로 해제됩니다...
});잠금을 즉시 획득할 수 없는 경우, 지정한 초만큼 대기하도록 할 수 있습니다. 지정 시간 내에 잠금을 획득하지 못하면 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 메서드에 클로저를 전달하면 더 간결하게 처리할 수 있습니다. 클로저 실행 후 잠금은 자동으로 해제됩니다.
Cache::lock('foo', 10)->block(5, function () {
// 최대 5초 대기 후 잠금 획득, 실행 완료 후 자동 해제...
});프로세스 간 잠금 관리
한 프로세스에서 잠금을 획득하고 다른 프로세스에서 해제해야 할 때가 있습니다. 예를 들어 웹 요청에서 잠금을 획득하고, 해당 요청으로 디스패치된 큐 Job이 완료될 때 잠금을 해제하는 경우입니다. 이런 시나리오에서는 잠금의 소유자 토큰(owner token)을 큐 Job에 전달하여, Job 내에서 동일한 잠금 인스턴스를 복원할 수 있습니다.
아래 예시에서는 잠금 획득에 성공하면 큐 Job을 디스패치하면서 잠금의 소유자 토큰을 함께 전달합니다.
$podcast = Podcast::find($id);
$lock = Cache::lock('processing', 120);
if ($lock->get()) {
ProcessPodcast::dispatch($podcast, $lock->owner());
}ProcessPodcast Job 내에서는 소유자 토큰으로 잠금을 복원하고 해제합니다.
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);
});NOTE
커스텀 캐시 드라이버 코드의 위치가 고민된다면 app 디렉토리 아래에 Extensions 네임스페이스를 만드는 것도 좋은 방법입니다. 다만 Laravel은 애플리케이션 구조를 강제하지 않으므로 원하는 방식으로 자유롭게 구성해도 됩니다.
드라이버 등록
Laravel에 커스텀 캐시 드라이버를 등록하려면 Cache 파사드의 extend 메서드를 사용합니다. 다른 서비스 프로바이더의 boot 메서드에서 캐시를 읽으려 할 수 있으므로, booting 콜백 안에서 드라이버를 등록해야 합니다. booting 콜백은 모든 서비스 프로바이더의 register 메서드 실행 이후, boot 메서드 실행 직전에 호출됩니다. App\Providers\AppServiceProvider의 register 메서드 안에서 다음과 같이 등록하세요.
<?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의 driver 옵션을 등록한 드라이버 이름으로 변경하세요.
이벤트
캐시 작업마다 특정 코드를 실행하고 싶다면, 캐시가 발생시키는 이벤트를 리스닝하면 됩니다. 일반적으로 이벤트 리스너는 App\Providers\EventServiceProvider 클래스에 등록합니다.
use App\Listeners\LogCacheHit;
use App\Listeners\LogCacheMissed;
use App\Listeners\LogKeyForgotten;
use App\Listeners\LogKeyWritten;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Cache\Events\KeyWritten;
/**
* 애플리케이션의 이벤트 리스너 매핑