Skip to content

미들웨어

소개

미들웨어는 애플리케이션에 들어오는 HTTP 요청을 검사하고 필터링할 수 있는 편리한 메커니즘을 제공합니다. 예를 들어, Laravel에는 사용자가 인증되었는지 확인하는 미들웨어가 포함되어 있습니다. 사용자가 인증되지 않은 경우, 미들웨어는 사용자를 애플리케이션의 로그인 화면으로 리디렉션합니다. 하지만 사용자가 인증된 경우, 미들웨어는 요청이 애플리케이션 내부로 더 진행될 수 있도록 허용합니다.

인증 외에도 다양한 작업을 수행하는 추가 미들웨어를 작성할 수 있습니다. 예를 들어, 로깅 미들웨어는 애플리케이션에 들어오는 모든 요청을 기록할 수 있습니다. Laravel에는 인증 및 CSRF 보호를 위한 미들웨어 등 다양한 미들웨어가 포함되어 있지만, 사용자가 정의한 모든 미들웨어는 일반적으로 애플리케이션의 app/Http/Middleware 디렉터리에 위치합니다.

미들웨어 정의하기

새로운 미들웨어를 생성하려면 make:middleware 아티즌 명령어를 사용하세요:

shell
php artisan make:middleware EnsureTokenIsValid

이 명령어는 새로운 EnsureTokenIsValid 클래스를 app/Http/Middleware 디렉터리에 생성합니다. 이 미들웨어에서는 제공된 token 입력값이 지정된 값과 일치할 때만 라우트에 접근을 허용합니다. 그렇지 않으면 사용자를 /home URI로 리디렉션합니다:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureTokenIsValid
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('/home');
        }

        return $next($request);
    }
}

보시다시피, 주어진 token이 우리의 비밀 토큰과 일치하지 않으면 미들웨어는 클라이언트에게 HTTP 리디렉션을 반환합니다. 그렇지 않으면 요청이 애플리케이션 내부로 더 전달됩니다. 요청을 애플리케이션 내부로 더 전달하려면(미들웨어가 "통과"하도록 하려면) $next 콜백에 $request를 전달해야 합니다.

미들웨어는 HTTP 요청이 애플리케이션에 도달하기 전에 반드시 통과해야 하는 일련의 "레이어"로 상상하는 것이 가장 좋습니다. 각 레이어는 요청을 검사하고, 심지어 완전히 거부할 수도 있습니다.

NOTE

모든 미들웨어는 서비스 컨테이너를 통해 해석되므로, 미들웨어의 생성자에서 필요한 의존성을 타입힌트로 지정할 수 있습니다.

미들웨어와 응답

물론, 미들웨어는 요청을 애플리케이션 내부로 전달하기 이나 에 작업을 수행할 수 있습니다. 예를 들어, 다음 미들웨어는 요청이 애플리케이션에서 처리되기 에 작업을 수행합니다:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class BeforeMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // 작업 수행

        return $next($request);
    }
}

반면, 이 미들웨어는 요청이 애플리케이션에서 처리된 에 작업을 수행합니다:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class AfterMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        // 작업 수행

        return $response;
    }
}

미들웨어 등록하기

글로벌 미들웨어

모든 HTTP 요청에 대해 미들웨어를 실행하고 싶다면, 애플리케이션의 bootstrap/app.php 파일에서 글로벌 미들웨어 스택에 추가할 수 있습니다:

php
use App\Http\Middleware\EnsureTokenIsValid;

->withMiddleware(function (Middleware $middleware) {
     $middleware->append(EnsureTokenIsValid::class);
})

withMiddleware 클로저에 제공되는 $middleware 객체는 Illuminate\Foundation\Configuration\Middleware의 인스턴스이며, 애플리케이션의 라우트에 할당된 미들웨어를 관리하는 역할을 합니다. append 메서드는 미들웨어를 글로벌 미들웨어 목록의 끝에 추가합니다. 목록의 맨 앞에 미들웨어를 추가하고 싶다면 prepend 메서드를 사용하세요.

Laravel의 기본 글로벌 미들웨어 수동 관리

Laravel의 글로벌 미들웨어 스택을 직접 관리하고 싶다면, use 메서드에 Laravel의 기본 글로벌 미들웨어 스택을 제공할 수 있습니다. 그런 다음 필요에 따라 기본 미들웨어 스택을 조정할 수 있습니다:

php
->withMiddleware(function (Middleware $middleware) {
    $middleware->use([
        \Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks::class,
        // \Illuminate\Http\Middleware\TrustHosts::class,
        \Illuminate\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ]);
})

미들웨어를 라우트에 할당하기

특정 라우트에 미들웨어를 할당하고 싶다면, 라우트를 정의할 때 middleware 메서드를 호출하면 됩니다:

php
use App\Http\Middleware\EnsureTokenIsValid;

Route::get('/profile', function () {
    // ...
})->middleware(EnsureTokenIsValid::class);

middleware 메서드에 미들웨어 이름의 배열을 전달하여 여러 미들웨어를 라우트에 할당할 수도 있습니다:

php
Route::get('/', function () {
    // ...
})->middleware([First::class, Second::class]);

미들웨어 제외하기

라우트 그룹에 미들웨어를 할당할 때, 때로는 그룹 내의 개별 라우트에서 미들웨어가 적용되지 않도록 해야 할 수도 있습니다. 이럴 때는 withoutMiddleware 메서드를 사용할 수 있습니다:

php
use App\Http\Middleware\EnsureTokenIsValid;

Route::middleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/', function () {
        // ...
    });

    Route::get('/profile', function () {
        // ...
    })->withoutMiddleware([EnsureTokenIsValid::class]);
});

또한, 그룹 전체에서 특정 미들웨어 집합을 제외할 수도 있습니다:

php
use App\Http\Middleware\EnsureTokenIsValid;

Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
    Route::get('/profile', function () {
        // ...
    });
});

withoutMiddleware 메서드는 라우트 미들웨어만 제거할 수 있으며, 글로벌 미들웨어에는 적용되지 않습니다.

미들웨어 그룹

여러 미들웨어를 하나의 키로 묶어 라우트에 더 쉽게 할당하고 싶을 때가 있습니다. 애플리케이션의 bootstrap/app.php 파일에서 appendToGroup 메서드를 사용하여 이를 구현할 수 있습니다:

php
use App\Http\Middleware\First;
use App\Http\Middleware\Second;

->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('group-name', [
        First::class,
        Second::class,
    ]);

    $middleware->prependToGroup('group-name', [
        First::class,
        Second::class,
    ]);
})

미들웨어 그룹은 개별 미들웨어와 동일한 문법으로 라우트 및 컨트롤러 액션에 할당할 수 있습니다:

php
Route::get('/', function () {
    // ...
})->middleware('group-name');

Route::middleware(['group-name'])->group(function () {
    // ...
});

Laravel의 기본 미들웨어 그룹

Laravel에는 웹 및 API 라우트에 적용할 수 있는 일반적인 미들웨어가 포함된 webapi 미들웨어 그룹이 미리 정의되어 있습니다. Laravel은 이 미들웨어 그룹을 각각 routes/web.phproutes/api.php 파일에 자동으로 적용합니다:

web 미들웨어 그룹
Illuminate\Cookie\Middleware\EncryptCookies
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse
Illuminate\Session\Middleware\StartSession
Illuminate\View\Middleware\ShareErrorsFromSession
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
Illuminate\Routing\Middleware\SubstituteBindings
api 미들웨어 그룹
Illuminate\Routing\Middleware\SubstituteBindings

이 그룹에 미들웨어를 추가하거나 앞에 붙이고 싶다면, 애플리케이션의 bootstrap/app.php 파일에서 webapi 메서드를 사용할 수 있습니다. webapi 메서드는 appendToGroup 메서드의 편리한 대안입니다:

php
use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        EnsureUserIsSubscribed::class,
    ]);

    $middleware->api(prepend: [
        EnsureTokenIsValid::class,
    ]);
})

Laravel의 기본 미들웨어 그룹 항목 중 하나를 사용자 정의 미들웨어로 교체할 수도 있습니다:

php
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;

$middleware->web(replace: [
    StartSession::class => StartCustomSession::class,
]);

또는 미들웨어를 완전히 제거할 수도 있습니다:

php
$middleware->web(remove: [
    StartSession::class,
]);

Laravel의 기본 미들웨어 그룹 수동 관리

Laravel의 기본 webapi 미들웨어 그룹 내의 모든 미들웨어를 직접 관리하고 싶다면, 그룹을 완전히 재정의할 수 있습니다. 아래 예시는 기본 미들웨어로 webapi 미들웨어 그룹을 정의하며, 필요에 따라 커스터마이즈할 수 있습니다:

php
->withMiddleware(function (Middleware $middleware) {
    $middleware->group('web', [
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
    ]);

    $middleware->group('api', [
        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        // 'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ]);
})

NOTE

기본적으로 webapi 미들웨어 그룹은 bootstrap/app.php 파일에 의해 애플리케이션의 해당 routes/web.phproutes/api.php 파일에 자동으로 적용됩니다.

미들웨어 별칭

애플리케이션의 bootstrap/app.php 파일에서 미들웨어에 별칭을 지정할 수 있습니다. 미들웨어 별칭을 사용하면 특정 미들웨어 클래스에 짧은 별칭을 정의할 수 있어, 클래스 이름이 긴 미들웨어에 특히 유용합니다:

php
use App\Http\Middleware\EnsureUserIsSubscribed;

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'subscribed' => EnsureUserIsSubscribed::class
    ]);
})

미들웨어 별칭이 bootstrap/app.php 파일에 정의되면, 라우트에 미들웨어를 할당할 때 별칭을 사용할 수 있습니다:

php
Route::get('/profile', function () {
    // ...
})->middleware('subscribed');

편의를 위해, Laravel의 일부 내장 미들웨어는 기본적으로 별칭이 지정되어 있습니다. 예를 들어, auth 미들웨어는 Illuminate\Auth\Middleware\Authenticate 미들웨어의 별칭입니다. 아래는 기본 미들웨어 별칭 목록입니다:

별칭미들웨어
authIlluminate\Auth\Middleware\Authenticate
auth.basicIlluminate\Auth\Middleware\AuthenticateWithBasicAuth
auth.sessionIlluminate\Session\Middleware\AuthenticateSession
cache.headersIlluminate\Http\Middleware\SetCacheHeaders
canIlluminate\Auth\Middleware\Authorize
guestIlluminate\Auth\Middleware\RedirectIfAuthenticated
password.confirmIlluminate\Auth\Middleware\RequirePassword
precognitiveIlluminate\Foundation\Http\Middleware\HandlePrecognitiveRequests
signedIlluminate\Routing\Middleware\ValidateSignature
subscribed\Spark\Http\Middleware\VerifyBillableIsSubscribed
throttleIlluminate\Routing\Middleware\ThrottleRequests 또는 Illuminate\Routing\Middleware\ThrottleRequestsWithRedis
verifiedIlluminate\Auth\Middleware\EnsureEmailIsVerified

미들웨어 정렬

드물게, 미들웨어가 특정 순서로 실행되어야 하지만 라우트에 할당할 때 그 순서를 제어할 수 없는 경우가 있습니다. 이런 상황에서는 애플리케이션의 bootstrap/app.php 파일에서 priority 메서드를 사용하여 미들웨어 우선순위를 지정할 수 있습니다:

php
->withMiddleware(function (Middleware $middleware) {
    $middleware->priority([
        \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
        \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        \Illuminate\Routing\Middleware\ThrottleRequests::class,
        \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ]);
})

미들웨어 파라미터

미들웨어는 추가 파라미터도 받을 수 있습니다. 예를 들어, 애플리케이션에서 인증된 사용자가 특정 "역할"을 가지고 있는지 확인해야 한다면, 역할 이름을 추가 인수로 받는 EnsureUserHasRole 미들웨어를 만들 수 있습니다.

추가 미들웨어 파라미터는 $next 인수 뒤에 미들웨어로 전달됩니다:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserHasRole
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (! $request->user()->hasRole($role)) {
            // 리디렉션...
        }

        return $next($request);
    }
}

미들웨어 파라미터는 미들웨어 이름과 파라미터를 :로 구분하여 라우트를 정의할 때 지정할 수 있습니다:

php
use App\Http\Middleware\EnsureUserHasRole;

Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware(EnsureUserHasRole::class.':editor');

여러 파라미터는 쉼표로 구분할 수 있습니다:

php
Route::put('/post/{id}', function (string $id) {
    // ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');

종료 가능한 미들웨어

때로는 미들웨어가 HTTP 응답이 브라우저로 전송된 후에 작업을 수행해야 할 수도 있습니다. 미들웨어에 terminate 메서드를 정의하고 웹 서버가 FastCGI를 사용 중이라면, 응답이 브라우저로 전송된 후 terminate 메서드가 자동으로 호출됩니다:

php
<?php

namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class TerminatingMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    /**
     * 응답이 브라우저로 전송된 후 작업을 처리합니다.
     */
    public function terminate(Request $request, Response $response): void
    {
        // ...
    }
}

terminate 메서드는 요청과 응답을 모두 받아야 합니다. 종료 가능한 미들웨어를 정의했다면, 애플리케이션의 bootstrap/app.php 파일에서 라우트 또는 글로벌 미들웨어 목록에 추가해야 합니다.

미들웨어의 terminate 메서드를 호출할 때, Laravel은 서비스 컨테이너에서 새로운 미들웨어 인스턴스를 해석합니다. handleterminate 메서드가 호출될 때 동일한 미들웨어 인스턴스를 사용하고 싶다면, 컨테이너의 singleton 메서드를 사용하여 미들웨어를 등록하세요. 일반적으로 이는 AppServiceProviderregister 메서드에서 수행해야 합니다:

php
use App\Http\Middleware\TerminatingMiddleware;

/**
 * Register any application services.
 */
public function register(): void
{
    $this->app->singleton(TerminatingMiddleware::class);
}

나를 위한 문서 한글화