메인 콘텐츠로 건너뛰기

Context 란

Laravel의 Context 기능은 요청·큐 잡·명령어의 실행을 넘나들며 정보를 기록·공유하기 위한 구조입니다. Illuminate\Support\Facades\Context 파사드를 통해 정보를 추가하면 그 정보는 애플리케이션이 기록하는 모든 로그 엔트리에 자동으로 부여됩니다. 이에 따라 개별 로그 호출에 전달한 정보와 Context가 유지하는 공유 정보를 명확하게 구별할 수 있습니다. 분산 시스템이나 큐를 사용한 아키텍처에서 트레이싱을 할 때 특히 유용합니다.

컨텍스트의 전파 플로우

기본적인 사용법

가장 전형적인 사용법은 미들웨어에서 trace_id를 설정하는 것입니다. 이후의 모든 로그 엔트리에 자동으로 포함됩니다.
1

미들웨어 작성

php artisan make:middleware AddContext
2

Context 에 트레이스 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);
    }
}
3

미들웨어 등록

bootstrap/app.php에서 글로벌 미들웨어로 등록합니다.
->withMiddleware(function (Middleware $middleware) {
    $middleware->append(\App\Http\Middleware\AddContext::class);
})
이 설정 후, 컨트롤러나 서비스에서 기록하는 로그에는 urltrace_id가 자동으로 부여됩니다.
Log::info('User authenticated.', ['auth_id' => Auth::id()]);
User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}

컨텍스트에의 쓰기

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::addIf('key', 'second');

Context::get('key');
// "first" — 덮어써지지 않음

increment / decrement — 카운터 관리

수치를 증감시키는 전용 메서드입니다. 두 번째 인수로 변화량을 지정할 수 있습니다.
Context::increment('records_added');
Context::increment('records_added', 5);

Context::decrement('records_added');
Context::decrement('records_added', 5);

when — 조건부로 추가

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 — 스택에 추가

Context는 리스트 형식의 데이터를 유지하는 “스택”을 지원합니다. push를 사용하면 추가한 순서로 데이터가 쌓여 갑니다.
Context::push('breadcrumbs', 'first_value');
Context::push('breadcrumbs', 'second_value', 'third_value');

Context::get('breadcrumbs');
// ['first_value', 'second_value', 'third_value']
쿼리의 실행 이력을 스택으로 기록하는 예시입니다.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;

// AppServiceProvider.php 의 boot 메서드에서 등록
DB::listen(function ($event) {
    Context::push('queries', [$event->time, $event->sql]);
});

컨텍스트 취득

get / all

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

// 모두 취득
$data = Context::all();

only / except — 일부만 취득

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

$data = Context::except(['first_key']);

pull / pop — 취득해서 삭제

pull은 키의 값을 취득함과 동시에 컨텍스트에서 삭제합니다.
$value = Context::pull('key');
스택에서 마지막 값을 꺼내려면 pop을 사용합니다.
Context::push('breadcrumbs', 'first_value', 'second_value');

Context::pop('breadcrumbs');
// 'second_value'

Context::get('breadcrumbs');
// ['first_value']

remember — 존재하지 않으면 설정해서 반환

$permissions = Context::remember(
    'user-permissions',
    fn () => $user->permissions,
);

has / missing — 키의 존재 확인

if (Context::has('key')) {
    // ...
}

if (Context::missing('key')) {
    // ...
}
hasnull이 저장되어 있어도 true를 반환합니다. 키가 등록되어 있는지 여부만을 확인합니다.

컨텍스트 삭제

forget으로 키를 삭제합니다.
Context::add(['first_key' => 1, 'second_key' => 2]);

Context::forget('first_key');

Context::all();
// ['second_key' => 2]

// 여러 개를 함께 삭제
Context::forget(['first_key', 'second_key']);

스코프 부착 컨텍스트

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("Adding user [{$userId}] to friends list.");
        // Adding user [987] to friends list.  {"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]
스코프 내에서 객체를 변경한 경우, 그 변경은 스코프의 밖에도 반영됩니다. 원시값을 사용하는 경우는 문제 없습니다.

Hidden Context

로그에 출력하고 싶지 않은 데이터(비밀번호·API 키·개인 식별 정보 등)는 Hidden Context에 저장합니다. 일반적인 get 메서드로는 취득할 수 없으며, getHidden 등의 전용 메서드로만 접근할 수 있습니다.
use Illuminate\Support\Facades\Context;

Context::addHidden('key', 'value');

Context::getHidden('key');
// 'value'

Context::get('key');
// null — 일반 get 으로는 취득 불가능
Hidden Context는 일반 컨텍스트와 같은 메서드군을 갖습니다.
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::exceptHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::missingHidden(/* ... */);
Context::forgetHidden(/* ... */);

큐 잡에의 인계

잡을 큐에 디스패치하면 현재의 컨텍스트가 자동으로 직렬화되어 잡의 페이로드에 포함됩니다. 잡 실행 시에 원래의 컨텍스트가 복원되기 때문에, 요청에서 부여한 trace_id가 큐 상의 로그에도 자동으로 인계됩니다.
// 미들웨어에서 설정
Context::add('trace_id', Str::uuid()->toString());

// 컨트롤러에서 잡을 디스패치
ProcessPodcast::dispatch($podcast);
class ProcessPodcast implements ShouldQueue
{
    use Queueable;

    public function handle(): void
    {
        Log::info('Processing podcast.', ['podcast_id' => $this->podcast->id]);
    }
}
Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
요청 시의 trace_id가 큐 상의 로그에도 포함되는 것을 확인할 수 있습니다.

Dehydrating — 잡 전송 시의 커스터마이즈

Context::dehydrating을 사용하면 잡 전송의 직전에 컨텍스트를 가공할 수 있습니다. 예를 들어 Accept-Language 헤더로 정해지는 로케일을 큐에 전달하고 싶은 경우에 사용합니다.
// AppServiceProvider.php

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'));
    });
}
dehydrating 콜백 내에서는 Context 파사드를 사용하지 말고, 콜백에 전달된 $context 리포지토리만 조작해 주십시오. 파사드를 사용하면 현재 프로세스의 컨텍스트를 변경하게 됩니다.

Hydrated — 잡 실행 시의 복원

Context::hydrated를 사용하면 잡 실행의 직전에 컨텍스트가 복원된 타이밍에 처리를 추가할 수 있습니다. 예를 들어 저장해 두었던 로케일을 설정 파일에 반영합니다.
// AppServiceProvider.php

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'));
        }
    });
}
hydrated 콜백 내에서도 Context 파사드는 사용하지 말고, 전달된 $context 리포지토리만 조작해 주십시오.

정리

ContextLog::withContext
대상모든 로그 채널특정 채널만
큐 잡에의 인계자동(Dehydrate/Hydrate)없음
Hidden 데이터지원없음
용도트레이싱·분산 시스템채널 고유의 메타데이터
Hidden Context는 로그에 출력되지 않기 때문에, 다음과 같은 데이터를 안전하게 저장할 수 있습니다.
  • 세션 ID나 사용자 ID(로그에 남기고 싶지 않은 경우)
  • API 키나 인증 토큰
  • 로케일이나 설정값(큐에 인계하고 싶지만 로그에는 불필요)
  • 내부적인 플래그나 상태
  1. 로케일의 인계: dehydrating으로 app.locale을 Hidden Context에 저장하고, hydrated에서 Config::set으로 복원.
  2. 인증 정보의 전파: 요청에서 인증한 사용자의 정보를 큐 잡에서도 참조할 수 있도록 함.
  3. 테넌트 ID: 멀티테넌트 앱에서 테넌트 식별자를 큐를 넘나들며 공유.
마지막 수정일 2026년 7월 13일