본문 바로가기

컨텍스트

번역일: 2026년 6월 20일

컨텍스트

소개

Laravel의 컨텍스트(Context) 기능을 사용하면 요청, Job, Artisan 명령어 실행 중에 정보를 저장하고, 조회하고, 공유할 수 있습니다. 저장된 정보는 애플리케이션이 기록하는 로그에도 자동으로 포함되어, 특정 로그 항목이 기록되기 전까지 어떤 코드 흐름이 있었는지 파악하기 쉬워집니다. 분산 시스템에서 요청 흐름을 추적하는 데도 유용합니다.

동작 원리

컨텍스트 기능을 가장 쉽게 이해하는 방법은 로그와 함께 직접 사용해보는 것입니다. 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); } }

컨텍스트에 추가된 정보는 이후 요청 처리 흐름에서 작성되는 모든 로그 항목에 메타데이터로 자동 첨부됩니다. 로그를 직접 호출할 때 넘기는 데이터와 컨텍스트를 통해 공유되는 데이터가 구분되므로, 어떤 정보가 어디서 왔는지 명확히 알 수 있습니다. 예를 들어 다음과 같이 로그를 기록한다고 하겠습니다.

Log::info('사용자 인증 완료.', ['auth_id' => Auth::id()]);

실제로 기록되는 로그에는 직접 전달한 auth_id뿐 아니라, 컨텍스트에 저장된 urltrace_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, ]); // ... } }
팟캐스트 처리 중. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}

아래 흐름을 참고하면 HTTP 요청에서 큐 Job까지 컨텍스트가 어떻게 전파되는지 한눈에 이해할 수 있습니다.

이 문서의 나머지 부분에서는 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"

조건부 컨텍스트 저장

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', []), );

스택

컨텍스트는 "스택" 형태의 데이터 저장도 지원합니다. 스택은 추가된 순서대로 데이터를 보관하는 목록입니다. 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', // ]

스택은 요청 처리 중 발생하는 이벤트들을 순서대로 기록할 때 유용합니다. 예를 들어 쿼리 리스너를 등록하여 실행된 쿼리의 SQL과 소요 시간을 스택에 쌓을 수 있습니다.

use Illuminate\Support\Facades\Context; use Illuminate\Support\Facades\DB; DB::listen(function ($event) { Context::push('queries', [$event->time, $event->sql]); });

stackContainshiddenStackContains 메서드로 특정 값이 스택에 존재하는지 확인할 수 있습니다.

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 메서드를 사용하면 지정한 키들의 값만 선택적으로 가져올 수 있습니다.

$data = Context::only(['first_key', 'second_key']);

pull 메서드는 값을 조회한 뒤 컨텍스트에서 즉시 제거합니다.

$value = Context::pull('key');

스택에 저장된 데이터는 pop 메서드로 꺼낼 수 있습니다. 스택의 마지막(가장 최근에 추가된) 값이 반환되고 제거됩니다.

Context::push('breadcrumbs', 'first_value', 'second_value'); Context::pop('breadcrumbs') // second_value Context::get('breadcrumbs'); // ['first_value']

컨텍스트에 저장된 모든 데이터를 한 번에 조회하려면 all 메서드를 사용합니다.

$data = Context::all();

키 존재 여부 확인

hasmissing 메서드로 특정 키에 값이 저장되어 있는지 확인할 수 있습니다.

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

숨김 메서드는 일반 메서드와 동일한 기능을 제공하며, 이름에 Hidden이 붙는 형태로 구성됩니다.

Context::addHidden(/* ... */); Context::addHiddenIf(/* ... */); Context::pushHidden(/* ... */); Context::getHidden(/* ... */); Context::pullHidden(/* ... */); Context::popHidden(/* ... */); Context::onlyHidden(/* ... */); Context::allHidden(/* ... */); Context::hasHidden(/* ... */); Context::forgetHidden(/* ... */);

이벤트

컨텍스트는 두 가지 이벤트를 제공하여 컨텍스트의 직렬화(dehydration) 및 복원(hydration) 과정에 개입할 수 있게 합니다.

활용 예시를 들자면, 미들웨어에서 HTTP 요청의 Accept-Language 헤더를 읽어 app.locale 설정값을 동적으로 지정하는 경우를 생각해 볼 수 있습니다. 이 locale 값이 큐 Job 실행 시에도 올바르게 적용되어야 알림 메시지 등의 언어가 맞게 처리됩니다. 컨텍스트 이벤트와 숨김 데이터를 조합하면 이 문제를 깔끔하게 해결할 수 있습니다.

Dehydrating

Job이 큐에 디스패치될 때 컨텍스트 데이터는 "직렬화(dehydrate)"되어 Job 페이로드에 함께 저장됩니다. Context::dehydrating 메서드로 이 시점에 실행될 클로저를 등록할 수 있습니다. 클로저 내에서 큐 Job과 공유될 데이터를 수정하거나 추가할 수 있습니다.

이 콜백은 보통 AppServiceProviderboot 메서드에서 등록합니다.

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 메서드로 이 시점에 실행될 클로저를 등록할 수 있습니다.

이 콜백 역시 AppServiceProviderboot 메서드에서 등록하는 것이 일반적입니다.

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 리포지터리 인스턴스만을 통해 작업하세요.

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

번역일: 2026년 6월 20일