미들웨어

미들웨어 (Middleware)

미들웨어는 애플리케이션으로 들어오는 HTTP 요청을 검사하고 필터링하는 편리한 메커니즘이에요. 예를 들어 Laravel에 내장된 미들웨어 중에는 사용자가 인증되었는지 확인해, 인증되지 않았으면 로그인 화면으로 리다이렉트하고 인증됐으면 요청을 계속 진행시키는 게 있어요. 미들웨어를 '요청이 애플리케이션에 닿기 전에 통과해야 하는 여러 겹의 레이어'라고 상상하면 이해가 훨씬 쉬워요. 이 문서에서는 미들웨어 정의, 등록, 파라미터, 종료(terminable) 미들웨어까지 Laravel 12 기준으로 살펴볼게요.

출처: Laravel 공식 문서 — Middleware

미들웨어 정의하기 (Defining Middleware)

make:middleware Artisan 커맨드로 새 미들웨어를 만들어요. 이 커맨드는 app/Http/Middleware 디렉터리에 클래스를 생성해줘요.

php artisan make:middleware EnsureTokenIsValid

미들웨어 클래스의 handle 메서드에서 요청을 검사해요. 아래 예시는 토큰 입력이 지정된 값과 일치하지 않으면 /home으로 리다이렉트하고, 일치하면 요청을 더 깊이 진행시켜요. 요청을 "통과"시키려면 $next 콜백에 $request를 넘기면 돼요.

<?php

namespace App\Http\Middleware;

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

class EnsureTokenIsValid
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->input('token') !== 'my-secret-token') {
            return redirect('/home');
        }

        return $next($request);
    }
}

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

미들웨어와 응답

미들웨어는 요청을 더 깊이 전달하기 전에 일을 처리할 수도 있고, 후에 처리할 수도 있어요. $next($request)를 호출한 뒤 로직을 두면 응답이 나온 다음 작업을 수행해요.

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

        // 응답 후 수행할 작업
        return $response;
    }
}

미들웨어 등록하기 (Registering Middleware)

전역 미들웨어

모든 HTTP 요청에서 실행되게 하려면 bootstrap/app.phpwithMiddleware 클로저에서 전역 미들웨어 스택에 추가해요. $middleware 객체는 Illuminate\Foundation\Configuration\Middleware 인스턴스로, 애플리케이션 라우트에 지정된 미들웨어를 관리해요. append는 목록 끝에, prepend는 목록 앞에 추가해요.

use App\Http\Middleware\EnsureTokenIsValid;

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

전역 미들웨어 스택을 직접 관리하고 싶다면 use 메서드에 Laravel의 기본 스택 전체를 넘긴 뒤 조정할 수 있어요.

라우트에 미들웨어 지정하기

특정 라우트에만 미들웨어를 적용하려면 라우트를 정의할 때 middleware 메서드를 호출해요. 배열로 여러 개를 지정할 수도 있고, 그룹 내 일부 라우트에서만 미들웨어를 제외하려면 withoutMiddleware 메서드를 써요. 단, withoutMiddleware는 라우트 미들웨어만 제거할 수 있고 전역 미들웨어에는 적용되지 않아요.

use App\Http\Middleware\EnsureTokenIsValid;

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

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

미들웨어 그룹

여러 미들웨어를 하나의 키로 묶어 라우트에 쉽게 지정하고 싶다면 appendToGroup(끝에 추가)·prependToGroup(앞에 추가)을 써요. 그룹은 개별 미들웨어와 같은 문법으로 라우트·컨트롤러 액션에 지정할 수 있어요.

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

Laravel에는 미리 정의된 webapi 미들웨어 그룹이 있어요. web 그룹은 쿠키 암호화, 세션 시작, CSRF 토큰 검증, 바인딩 치환 등을 담고 routes/web.php에, api 그룹은 바인딩 치환을 담고 routes/api.php에 자동 적용돼요. 이 그룹들을 조정하려면 withMiddleware에서 web·api 메서드에 append/prepend/replace/remove 인자를 넘기거나, group 메서드로 그룹 전체를 재정의할 수 있어요.

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

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

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

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

미들웨어 별칭 (Aliases)

클래스 이름이 길 때 짧은 별칭을 정의하면 편리해요. withMiddleware에서 alias 메서드로 별칭을 지정하면 라우트에 별칭으로 미들웨어를 지정할 수 있어요. Laravel의 내장 미들웨어 중 auth, guest, can, throttle, verified 등은 기본으로 별칭이 지정돼 있어요.

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'subscribed' => EnsureUserIsSubscribed::class
    ]);
})
Route::get('/profile', function () {
    // ...
})->middleware('subscribed');

미들웨어 정렬 (Sorting)

라우트에 지정할 때 순서를 통제하기 어렵지만 특정 순서로 실행해야 하는 상황이 드물게 있어요. 그럴 땐 priority 메서드로 미들웨어 우선순위를 지정할 수 있어요.

->withMiddleware(function (Middleware $middleware): void {
    $middleware->priority([
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // ...
    ]);
})

미들웨어 파라미터

미들웨어는 추가 파라미터를 받을 수도 있어요. 예를 들어 인증된 사용자가 특정 "역할"을 가졌는지 확인해야 한다면, 역할 이름을 추가 인자로 받는 EnsureUserHasRole 미들웨어를 만들 수 있어요. 추가 파라미터는 $next 인자 다음에 전달되고, 라우트 정의 시 미들웨어 이름과 :로 구분해 지정하며 여러 개는 쉼표로 구분해요.

class EnsureUserHasRole
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }
}
use App\Http\Middleware\EnsureUserHasRole;

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

종료(Terminable) 미들웨어

미들웨어가 HTTP 응답을 브라우저로 보낸 에 처리할 작업이 있을 수도 있어요. 미들웨어에 terminate 메서드를 정의하고 웹 서버가 FastCGI를 쓰면, 응답 전송 후 자동으로 호출돼요. terminate는 request와 response를 모두 받아요. handleterminate가 같은 인스턴스를 쓰게 하려면 서비스 컨테이너에 싱글턴으로 등록하면 돼요.

class TerminatingMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        // 응답 전송 후 수행할 작업
    }
}
public function register(): void
{
    $this->app->singleton(TerminatingMiddleware::class);
}

더 알아보기 (Learn more)

  • Laravel 공식 문서의 Middleware 페이지
  • Routing 문서에서 미들웨어를 라우트 그룹에 적용하는 법 익히기
  • CSRF Protection 문서에서 web 그룹의 CSRF 미들웨어 이해하기
  • Service Container 문서에서 미들웨어 의존성 주입 원리 살펴보기