컨텍스트
업데이트됨번역일: 2026년 7월 28일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 7월 28일
- 번역 갱신
- 2026년 7월 28일
컨텍스트
소개
Laravel의 컨텍스트(Context) 기능을 사용하면 요청, Job, 커맨드 실행 전반에 걸쳐 정보를 저장하고, 조회하고, 공유할 수 있습니다. 저장된 정보는 애플리케이션이 기록하는 로그에도 자동으로 포함되므로, 특정 로그 항목이 기록되기 전까지 어떤 흐름으로 코드가 실행되었는지 파악하기 쉬워지고, 분산 시스템 전반의 실행 흐름을 추적하는 데 큰 도움이 됩니다.
동작 원리
컨텍스트 기능을 이해하는 가장 빠른 방법은 내장 로깅 기능과 함께 직접 사용해 보는 것입니다. Context 파사드를 이용해 컨텍스트에 정보를 추가할 수 있습니다. 아래 예시에서는 미들웨어를 사용해 모든 요청마다 요청 URL과 고유한 트레이스 ID를 컨텍스트에 추가합니다.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class AddContext
{
/**
* 요청을 처리합니다.
*/
public function handle(Request $request, Closure $next): Response
{
Context::add('url', $request->url());
Context::add('trace_id', Str::uuid()->toString());
return $next($request);
}
}컨텍스트에 추가된 정보는 요청 처리 중 기록되는 모든 로그 항목에 메타데이터로 자동 첨부됩니다. 메타데이터로 첨부하면, 개별 로그 항목에 직접 전달된 정보와 Context를 통해 공유된 정보를 구분할 수 있습니다. 예를 들어 아래와 같이 로그를 기록한다면:
Log::info('사용자 인증 완료.', ['auth_id' => Auth::id()]);실제로 기록되는 로그에는 직접 전달한 auth_id뿐 아니라, 컨텍스트에 저장된 url과 trace_id도 메타데이터로 함께 포함됩니다.
사용자 인증 완료. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}컨텍스트에 저장된 정보는 큐에 디스패치되는 Job에도 전달됩니다. 예를 들어 컨텍스트에 정보를 추가한 뒤 ProcessPodcast Job을 큐에 디스패치한다고 해봅시다.
// 미들웨어에서...
Context::add('url', $request->url());
Context::add('trace_id', Str::uuid()->toString());
// 컨트롤러에서...
ProcessPodcast::dispatch($podcast);Job이 디스패치될 때 현재 컨텍스트에 저장된 모든 정보가 캡처되어 Job 페이로드와 함께 전달됩니다. Job이 실행될 때는 이 정보가 다시 현재 컨텍스트로 복원(hydrate)됩니다. 따라서 Job의 handle 메서드에서 로그를 기록하면:
class ProcessPodcast implements ShouldQueue
{
use Queueable;
// ...
/**
* Job을 실행합니다.
*/
public function handle(): void
{
Log::info('팟캐스트 처리 중.', [
'podcast_id' => $this->podcast->id,
]);
// ...
}
}원래 요청에서 컨텍스트에 추가했던 정보가 Job의 로그에도 그대로 나타납니다.
팟캐스트 처리 중. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}이 문서에서는 로깅과의 연동 외에도, HTTP 요청과 큐 Job 사이에서 정보를 공유하는 방법, 그리고 로그에 기록되지 않는 숨겨진 컨텍스트 데이터를 활용하는 방법을 설명합니다.
컨텍스트 저장
Context 파사드의 add 메서드로 현재 컨텍스트에 정보를 저장할 수 있습니다.
use Illuminate\Support\Facades\Context;
Context::add('key', 'value');여러 항목을 한 번에 추가하려면 연관 배열을 전달하면 됩니다.
Context::add([
'first_key' => 'value',
'second_key' => 'value',
]);add 메서드는 동일한 키가 이미 존재하면 해당 값을 덮어씁니다. 키가 존재하지 않을 때만 추가하고 싶다면 addIf 메서드를 사용하세요.
Context::add('key', 'first');
Context::get('key');
// "first"
Context::addIf('key', 'second');
Context::get('key');
// "first"특정 키의 값을 증가 또는 감소시키는 편의 메서드도 제공됩니다. 첫 번째 인수로 키를 지정하며, 두 번째 인수로 증감할 양을 지정할 수 있습니다.
Context::increment('records_added');
Context::increment('records_added', 5);
Context::decrement('records_added');
Context::decrement('records_added', 5);조건부 컨텍스트 저장
when 메서드를 사용하면 조건에 따라 컨텍스트에 데이터를 추가할 수 있습니다. 조건이 true이면 첫 번째 클로저가, false이면 두 번째 클로저가 실행됩니다.
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Context;
Context::when(
Auth::user()->isAdmin(),
fn ($context) => $context->add('permissions', Auth::user()->permissions),
fn ($context) => $context->add('permissions', []),
);스코프 컨텍스트
scope 메서드를 사용하면 특정 클로저 실행 동안만 컨텍스트를 임시로 변경하고, 클로저가 완료되면 원래 상태로 자동 복원할 수 있습니다. 두 번째와 세 번째 인수로 클로저 실행 중에만 병합할 추가 데이터를 전달할 수도 있습니다.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Log;
Context::add('trace_id', 'abc-999');
Context::addHidden('user_id', 123);
Context::scope(
function () {
Context::add('action', 'adding_friend');
$userId = Context::getHidden('user_id');
Log::debug("사용자 [{$userId}]를 친구 목록에 추가하는 중.");
// 사용자 [987]를 친구 목록에 추가하는 중. {"trace_id":"abc-999","user_name":"taylor_otwell","action":"adding_friend"}
},
data: ['user_name' => 'taylor_otwell'],
hidden: ['user_id' => 987],
);
Context::all();
// [
// 'trace_id' => 'abc-999',
// ]
Context::allHidden();
// [
// 'user_id' => 123,
// ]WARNING
스코프 클로저 내부에서 컨텍스트에 저장된 객체를 직접 변경(mutate)하면, 그 변경 사항은 스코프 밖에서도 반영됩니다.
스택
컨텍스트는 데이터를 추가된 순서대로 저장하는 "스택"을 지원합니다. push 메서드로 스택에 값을 추가할 수 있습니다.
use Illuminate\Support\Facades\Context;
Context::push('breadcrumbs', 'first_value');
Context::push('breadcrumbs', 'second_value', 'third_value');
Context::get('breadcrumbs');
// [
// 'first_value',
// 'second_value',
// 'third_value',
// ]스택은 요청 처리 중 발생한 이벤트를 시간 순서대로 기록할 때 유용합니다. 예를 들어 DB 쿼리가 실행될 때마다 SQL과 소요 시간을 스택에 쌓을 수 있습니다.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;
// AppServiceProvider.php에서...
DB::listen(function ($event) {
Context::push('queries', [$event->time, $event->sql]);
});stackContains와 hiddenStackContains 메서드로 특정 값이 스택에 존재하는지 확인할 수 있습니다.
if (Context::stackContains('breadcrumbs', 'first_value')) {
//
}
if (Context::hiddenStackContains('secrets', 'first_value')) {
//
}두 번째 인수로 클로저를 전달하면 더 세밀한 비교 로직을 구현할 수 있습니다.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
return Context::stackContains('breadcrumbs', function ($value) {
return Str::startsWith($value, 'query_');
});컨텍스트 조회
Context 파사드의 get 메서드로 컨텍스트에서 값을 가져올 수 있습니다.
use Illuminate\Support\Facades\Context;
$value = Context::get('key');only와 except 메서드를 사용하면 컨텍스트의 일부 항목만 선택적으로 가져올 수 있습니다.
$data = Context::only(['first_key', 'second_key']);
$data = Context::except(['first_key']);pull 메서드는 값을 가져오는 동시에 컨텍스트에서 해당 항목을 제거합니다.
$value = Context::pull('key');스택에 저장된 데이터는 pop 메서드로 마지막 항목부터 꺼낼 수 있습니다.
Context::push('breadcrumbs', 'first_value', 'second_value');
Context::pop('breadcrumbs');
// second_value
Context::get('breadcrumbs');
// ['first_value']remember와 rememberHidden 메서드는 키에 해당하는 값이 없을 경우 클로저의 반환값을 컨텍스트에 저장하고 반환합니다. 값이 이미 있으면 그 값을 그대로 반환합니다.
$permissions = Context::remember(
'user-permissions',
fn () => $user->permissions,
);컨텍스트에 저장된 모든 정보를 가져오려면 all 메서드를 사용하세요.
$data = Context::all();키 존재 여부 확인
has와 missing 메서드로 특정 키에 값이 저장되어 있는지 확인할 수 있습니다.
use Illuminate\Support\Facades\Context;
if (Context::has('key')) {
// ...
}
if (Context::missing('key')) {
// ...
}has 메서드는 저장된 값이 null이더라도 키가 존재하면 true를 반환합니다.
Context::add('key', null);
Context::has('key');
// true컨텍스트 삭제
forget 메서드로 특정 키와 그 값을 현재 컨텍스트에서 제거할 수 있습니다.
use Illuminate\Support\Facades\Context;
Context::add(['first_key' => 1, 'second_key' => 2]);
Context::forget('first_key');
Context::all();
// ['second_key' => 2]배열을 전달하면 여러 키를 한 번에 삭제할 수 있습니다.
Context::forget(['first_key', 'second_key']);숨겨진 컨텍스트
컨텍스트는 "숨겨진(hidden)" 데이터를 별도로 저장하는 기능을 제공합니다. 숨겨진 정보는 로그에 첨부되지 않으며, 앞서 설명한 일반 조회 메서드로는 접근할 수 없습니다. 숨겨진 컨텍스트를 다루기 위한 전용 메서드가 별도로 제공됩니다.
use Illuminate\Support\Facades\Context;
Context::addHidden('key', 'value');
Context::getHidden('key');
// 'value'
Context::get('key');
// null숨겨진 컨텍스트 메서드는 일반 메서드와 동일한 방식으로 동작하며, 아래와 같이 대응됩니다.
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::exceptHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::missingHidden(/* ... */);
Context::forgetHidden(/* ... */);이벤트
컨텍스트는 hydration 및 dehydration 과정에 훅(hook)을 걸 수 있는 두 가지 이벤트를 제공합니다.
이 이벤트가 필요한 상황을 예로 들어보겠습니다. 미들웨어에서 HTTP 요청의 Accept-Language 헤더를 기반으로 app.locale 설정값을 변경한다고 가정합니다. 요청 처리 중에는 이 값이 올바르게 설정되지만, 이후 큐 Job이 실행될 때는 해당 로케일 설정이 유지되지 않습니다. 컨텍스트 이벤트와 숨겨진 컨텍스트를 함께 활용하면 이 문제를 해결할 수 있습니다. 아래에서 그 방법을 설명합니다.
Dehydrating
Job이 큐에 디스패치될 때 컨텍스트 데이터는 "dehydrate(탈수)"되어 Job 페이로드와 함께 저장됩니다. Context::dehydrating 메서드로 이 과정에서 실행될 클로저를 등록할 수 있습니다. 클로저 내부에서는 큐 Job과 공유될 데이터를 수정할 수 있습니다.
일반적으로 dehydrating 콜백은 애플리케이션의 AppServiceProvider 클래스의 boot 메서드 안에서 등록합니다.
use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Context::dehydrating(function (Repository $context) {
$context->addHidden('locale', Config::get('app.locale'));
});
}NOTE
dehydrating 콜백 내부에서는 Context 파사드를 직접 사용하지 마세요. 파사드를 사용하면 현재 프로세스의 컨텍스트가 변경됩니다. 반드시 콜백에 전달된 $context 리포지터리 인스턴스만 사용하세요.
Hydrated
큐에서 Job이 실행되기 시작할 때 Job과 함께 전달된 컨텍스트 데이터가 현재 컨텍스트로 "hydrate(수화)"됩니다. Context::hydrated 메서드로 이 과정에서 실행될 클로저를 등록할 수 있습니다.
마찬가지로 hydrated 콜백도 AppServiceProvider의 boot 메서드에서 등록하는 것이 일반적입니다.
use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Context::hydrated(function (Repository $context) {
if ($context->hasHidden('locale')) {
Config::set('app.locale', $context->getHidden('locale'));
}
});
}NOTE
hydrated 콜백 내부에서도 Context 파사드를 직접 사용하지 말고, 콜백에 전달된 $context 리포지터리 인스턴스만을 통해 변경하세요.