캐시

번역일: 2026년 6월 25일

캐시

소개

데이터 조회나 복잡한 연산은 CPU를 많이 사용하거나 수 초가 걸리기도 합니다. 이런 경우 결과를 캐시에 저장해두면 동일한 요청이 들어왔을 때 빠르게 응답할 수 있습니다. 캐시 데이터는 보통 MemcachedRedis처럼 매우 빠른 인메모리 저장소에 보관됩니다.

Laravel은 다양한 캐시 백엔드를 일관된 API로 다룰 수 있도록 설계되어 있어, 빠른 데이터 조회를 활용해 웹 애플리케이션의 성능을 손쉽게 향상시킬 수 있습니다.

설정

캐시 설정 파일은 config/cache.php에 위치합니다. 이 파일에서 애플리케이션 전체에서 기본으로 사용할 캐시 스토어를 지정할 수 있습니다. Laravel은 Memcached, Redis, DynamoDB, 관계형 데이터베이스를 기본으로 지원합니다. 파일 기반 드라이버도 사용할 수 있으며, 자동화 테스트에는 arraynull 드라이버가 유용합니다.

기본값으로는 database 캐시 드라이버가 설정되어 있으며, 직렬화된 캐시 객체를 데이터베이스에 저장합니다.

드라이버 사전 준비

Database

database 드라이버를 사용하려면 캐시 데이터를 저장할 테이블이 필요합니다. 일반적으로 Laravel의 기본 마이그레이션 파일인 0001_01_01_000001_create_cache_table.php에 이미 포함되어 있습니다. 만약 이 마이그레이션 파일이 없다면 make:cache-table 명령으로 생성할 수 있습니다.

php artisan make:cache-tablephp artisan migrate

Memcached

Memcached 드라이버를 사용하려면 Memcached PECL 패키지가 설치되어 있어야 합니다. config/cache.php 파일의 memcached.servers 항목에 Memcached 서버 목록을 지정합니다.

'memcached' => [ // ... 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ], ],

UNIX 소켓을 사용하려면 host에 소켓 경로를 지정하고 port0으로 설정합니다.

'memcached' => [ // ... 'servers' => [ [ 'host' => '/var/run/memcached/memcached.sock', 'port' => 0, 'weight' => 100 ], ], ],

Redis

Redis를 캐시로 사용하려면 PECL을 통해 PhpRedis PHP 확장 모듈을 설치하거나, Composer로 predis/predis 패키지(~2.0)를 설치해야 합니다. Laravel Sail에는 PhpRedis가 이미 포함되어 있으며, Laravel ForgeLaravel Vapor에도 기본 설치되어 있습니다.

Redis 설정에 대한 자세한 내용은 Laravel Redis 문서를 참고하세요.

DynamoDB

DynamoDB 드라이버를 사용하려면 캐시 데이터를 저장할 DynamoDB 테이블을 먼저 생성해야 합니다. 테이블 이름은 보통 cache로 지정하지만, cache 설정 파일의 stores.dynamodb.table 값이나 DYNAMODB_CACHE_TABLE 환경 변수에 맞게 설정하면 됩니다.

테이블에는 stores.dynamodb.attributes.key 설정값에 해당하는 이름의 문자열 파티션 키가 있어야 합니다. 기본값은 key입니다.

DynamoDB는 만료된 항목을 자동으로 삭제하지 않으므로, 테이블에서 TTL(Time to Live)을 활성화해야 합니다. TTL 속성명은 expires_at으로 설정하세요.

다음으로 AWS SDK를 설치합니다.

composer require aws/aws-sdk-php

그리고 .env 파일에 DynamoDB 관련 설정값을 추가합니다.

'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는 TTL 인덱스를 지원하므로 만료된 캐시 항목을 자동으로 삭제할 수 있습니다.

자세한 설정 방법은 MongoDB의 Cache and Locks 문서를 참고하세요.

캐시 사용법

캐시 인스턴스 가져오기

캐시 스토어 인스턴스를 얻으려면 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 메서드를 사용하면 여러 캐시 스토어에 접근할 수 있습니다. 전달하는 키는 cache 설정 파일의 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')) { // ... }

값 증가 / 감소

정수형 캐시 값을 증가하거나 감소시킬 때는 incrementdecrement 메서드를 사용합니다. 두 번째 인자로 증감량을 지정할 수 있습니다.

// 값이 없으면 초기화... 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(); });

항목이 캐시에 없을 때만 클로저가 실행되어 결과가 저장됩니다.

영구적으로 저장하려면 rememberForever를 사용합니다.

$value = Cache::rememberForever('users', function () { return DB::table('users')->get(); });

Stale While Revalidate (유연한 캐시 갱신)

Cache::remember를 사용할 때 캐시가 만료된 직후 요청이 들어오면 캐시를 다시 계산하는 동안 응답이 느려질 수 있습니다. 이를 해결하기 위해 "stale-while-revalidate" 패턴을 사용할 수 있으며, Laravel에서는 Cache::flexible 메서드로 이를 구현합니다.

flexible은 "신선(fresh)" 기간과 "낡은(stale)" 기간을 배열로 전달받습니다.

  • 첫 번째 값(fresh): 이 시간 안에 요청이 오면 캐시를 즉시 반환합니다.
  • 두 번째 값(stale): fresh 기간이 지난 뒤 이 시간까지는 기존 캐시를 반환하고, 응답 후 백그라운드에서 캐시를 갱신합니다.
  • 두 번째 값 초과: 캐시가 완전히 만료되어 즉시 재계산하므로 응답이 느릴 수 있습니다.
$value = Cache::flexible('users', [5, 10], function () { return DB::table('users')->get(); });

NOTE

백그라운드 갱신은 deferred function을 이용해 응답이 완료된 뒤 실행됩니다.

조회 후 삭제 (pull)

캐시에서 항목을 가져온 뒤 바로 삭제하려면 pull 메서드를 사용합니다. 항목이 없으면 null을 반환합니다.

$value = Cache::pull('key'); $value = Cache::pull('key', '기본값');

캐시에 항목 저장하기

Cache 파사드의 put 메서드로 항목을 저장합니다.

Cache::put('key', 'value', $seconds = 10);

만료 시간을 지정하지 않으면 영구 저장됩니다.

Cache::put('key', 'value');

정수 초 대신 DateTime 인스턴스로 만료 시각을 지정할 수도 있습니다.

Cache::put('key', 'value', now()->addMinutes(10));

없을 때만 저장 (add)

add 메서드는 캐시에 해당 키가 없을 때만 저장합니다. 저장에 성공하면 true, 이미 존재하면 false를 반환합니다. 이 메서드는 원자적(atomic)으로 동작합니다.

Cache::add('key', 'value', $seconds);

영구 저장 (forever)

forever 메서드는 만료 시간 없이 항목을 영구적으로 저장합니다. 영구 저장된 항목은 forget 메서드로 수동 삭제해야 합니다.

Cache::forever('key', 'value');

NOTE

Memcached 드라이버를 사용하는 경우, 캐시 용량 한도에 도달하면 "영구" 저장된 항목도 삭제될 수 있습니다.

캐시에서 항목 삭제하기

forget 메서드로 특정 항목을 삭제합니다.

Cache::forget('key');

put 메서드에 만료 시간을 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) 걱정 없이 분산 잠금을 관리할 수 있습니다. 예를 들어 Laravel Forge는 원자적 잠금을 이용해 서버에서 하나의 원격 작업만 동시에 실행되도록 보장합니다.

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에 전달하면 됩니다.

$podcast = Podcast::find($id); $lock = Cache::lock('processing', 120); if ($lock->get()) { ProcessPodcast::dispatch($podcast, $lock->owner()); }

큐 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를 살펴보세요.

NOTE

커스텀 드라이버 코드는 app 디렉터리 안에 Extensions 네임스페이스를 만들어 관리하는 것이 일반적입니다. 다만 Laravel은 특정 디렉터리 구조를 강제하지 않으므로, 프로젝트 상황에 맞게 자유롭게 구성하세요.

드라이버 등록

드라이버를 Laravel에 등록하려면 Cache 파사드의 extend 메서드를 사용합니다. 다른 서비스 프로바이더가 boot 메서드에서 캐시를 읽을 수 있으므로, booting 콜백 안에 등록해야 합니다. 이렇게 하면 모든 서비스 프로바이더의 register가 실행된 뒤, boot가 호출되기 직전에 드라이버가 등록됩니다. App\Providers\AppServiceProviderregister 메서드에 다음과 같이 작성합니다.

<?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.phpdriver 옵션값과 일치해야 합니다. 두 번째 인자는 Illuminate\Cache\Repository 인스턴스를 반환하는 클로저입니다.

드라이버 등록 후 CACHE_STORE 환경 변수 또는 config/cache.phpdefault 옵션값을 등록한 드라이버 이름으로 변경하면 됩니다.

이벤트

모든 캐시 작업에 대해 코드를 실행하고 싶다면 캐시가 발생시키는 이벤트를 리슨하면 됩니다.

이벤트 이름
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, ],

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 25일