미들웨어
번역일: 2026년 6월 20일
미들웨어
소개
미들웨어는 애플리케이션으로 들어오는 HTTP 요청을 검사하고 필터링하는 편리한 메커니즘입니다. 예를 들어, Laravel에는 사용자가 인증되어 있는지 확인하는 미들웨어가 내장되어 있습니다. 인증되지 않은 사용자라면 로그인 화면으로 리다이렉트하고, 인증된 사용자라면 요청을 그대로 애플리케이션으로 전달합니다.
인증 외에도 다양한 목적의 미들웨어를 작성할 수 있습니다. 예를 들어, 로깅 미들웨어는 애플리케이션으로 들어오는 모든 요청을 기록할 수 있습니다. Laravel에는 인증, CSRF 보호 등을 위한 다양한 미들웨어가 기본으로 포함되어 있으며, 직접 작성한 미들웨어는 보통 app/Http/Middleware 디렉토리에 위치합니다.
미들웨어는 요청이 애플리케이션에 도달하기 전에 통과해야 하는 일련의 "관문(레이어)"으로 생각하면 이해하기 쉽습니다. 각 레이어는 요청을 검사하고, 필요하다면 처리를 거부할 수도 있습니다.
미들웨어 정의하기
새 미들웨어를 생성하려면 make:middleware Artisan 명령어를 사용합니다:
php artisan make:middleware EnsureTokenIsValid이 명령어를 실행하면 app/Http/Middleware 디렉토리에 EnsureTokenIsValid 클래스가 생성됩니다. 아래 예시는 요청에 포함된 token 값이 지정된 값과 일치할 때만 라우트 접근을 허용하고, 그렇지 않으면 /home으로 리다이렉트하는 미들웨어입니다:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureTokenIsValid
{
/**
* 들어오는 요청을 처리합니다.
*
* @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 값이 일치하지 않으면 미들웨어가 리다이렉트 응답을 반환하고, 일치하면 $next($request)를 호출하여 요청을 애플리케이션 안쪽으로 전달합니다. $next 콜백을 호출하는 것이 요청을 "통과"시키는 방법입니다.
NOTE
모든 미들웨어는 서비스 컨테이너를 통해 resolve되므로, 미들웨어의 생성자에서 필요한 의존성을 타입힌트로 주입받을 수 있습니다.
요청 전/후 처리
미들웨어는 요청이 애플리케이션으로 전달되기 전과 후 모두에서 작업을 수행할 수 있습니다.
다음은 요청 처리 전에 작업을 수행하는 미들웨어입니다:
<?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
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 파일의 전역 미들웨어 스택에 추가하면 됩니다:
use App\Http\Middleware\EnsureTokenIsValid;
->withMiddleware(function (Middleware $middleware): void {
$middleware->append(EnsureTokenIsValid::class);
})withMiddleware 클로저에 전달되는 $middleware 객체는 Illuminate\Foundation\Configuration\Middleware 인스턴스로, 애플리케이션의 미들웨어를 관리하는 역할을 합니다. append 메서드는 전역 미들웨어 목록의 끝에 추가하고, prepend 메서드를 사용하면 목록의 앞에 추가할 수 있습니다.
Laravel 기본 전역 미들웨어 직접 관리하기
전역 미들웨어 스택을 직접 완전히 제어하고 싶다면, use 메서드에 Laravel의 기본 전역 미들웨어 목록을 전달한 뒤 필요에 따라 수정할 수 있습니다:
->withMiddleware(function (Middleware $middleware): void {
$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 메서드를 호출합니다:
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);여러 미들웨어를 배열로 전달하여 한 번에 적용할 수도 있습니다:
Route::get('/', function () {
// ...
})->middleware([First::class, Second::class]);특정 라우트에서 미들웨어 제외하기
라우트 그룹에 미들웨어를 적용했을 때, 그룹 내 특정 라우트에서만 해당 미들웨어를 제외하려면 withoutMiddleware 메서드를 사용합니다:
use App\Http\Middleware\EnsureTokenIsValid;
Route::middleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/', function () {
// ...
});
Route::get('/profile', function () {
// ...
})->withoutMiddleware([EnsureTokenIsValid::class]);
});라우트 그룹 전체에서 특정 미들웨어를 제외할 수도 있습니다:
use App\Http\Middleware\EnsureTokenIsValid;
Route::withoutMiddleware([EnsureTokenIsValid::class])->group(function () {
Route::get('/profile', function () {
// ...
});
});NOTE
withoutMiddleware 메서드는 라우트 미들웨어만 제거할 수 있으며, 전역 미들웨어에는 적용되지 않습니다.
미들웨어 그룹
여러 미들웨어를 하나의 이름으로 묶어 라우트에 간편하게 적용하고 싶을 때는 미들웨어 그룹을 사용합니다. bootstrap/app.php 파일에서 appendToGroup 메서드로 그룹을 정의할 수 있습니다:
use App\Http\Middleware\First;
use App\Http\Middleware\Second;
->withMiddleware(function (Middleware $middleware): void {
$middleware->appendToGroup('group-name', [
First::class,
Second::class,
]);
$middleware->prependToGroup('group-name', [
First::class,
Second::class,
]);
})미들웨어 그룹은 개별 미들웨어와 동일한 방식으로 라우트에 적용합니다:
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});Laravel 기본 미들웨어 그룹
Laravel은 web과 api 두 가지 미들웨어 그룹을 기본으로 제공합니다. 이 그룹들은 각각 routes/web.php와 routes/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 파일에서 web 및 api 메서드를 사용합니다. 이 메서드들은 appendToGroup의 편리한 대안입니다:
use App\Http\Middleware\EnsureTokenIsValid;
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
EnsureUserIsSubscribed::class,
]);
$middleware->api(prepend: [
EnsureTokenIsValid::class,
]);
})기본 그룹의 특정 미들웨어를 커스텀 미들웨어로 교체할 수도 있습니다:
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);또는 특정 미들웨어를 그룹에서 완전히 제거할 수도 있습니다:
$middleware->web(remove: [
StartSession::class,
]);Laravel 기본 미들웨어 그룹 직접 관리하기
web과 api 그룹의 미들웨어 구성을 처음부터 완전히 직접 제어하고 싶다면, 그룹 전체를 재정의할 수 있습니다. 아래 예시는 기본 미들웨어를 그대로 포함하면서 필요에 따라 수정할 수 있는 출발점입니다:
->withMiddleware(function (Middleware $middleware): void {
$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
web과 api 미들웨어 그룹은 bootstrap/app.php 파일에 의해 각각 routes/web.php와 routes/api.php에 자동으로 적용됩니다.
미들웨어 별칭
클래스 이름이 긴 미들웨어를 라우트에 적용할 때 짧은 별칭을 사용하면 편리합니다. bootstrap/app.php 파일에서 별칭을 등록할 수 있습니다:
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware): void {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})별칭을 등록한 후에는 라우트 정의 시 짧은 이름으로 사용할 수 있습니다:
Route::get('/profile', function () {
// ...
})->middleware('subscribed');Laravel의 내장 미들웨어 중 일부는 기본적으로 별칭이 등록되어 있습니다. 예를 들어, auth는 Illuminate\Auth\Middleware\Authenticate의 별칭입니다. 기본 별칭 목록은 다음과 같습니다:
| 별칭 | 미들웨어 클래스 |
|---|---|
auth | Illuminate\Auth\Middleware\Authenticate |
auth.basic | Illuminate\Auth\Middleware\AuthenticateWithBasicAuth |
auth.session | Illuminate\Session\Middleware\AuthenticateSession |
cache.headers | Illuminate\Http\Middleware\SetCacheHeaders |
can | Illuminate\Auth\Middleware\Authorize |
guest | Illuminate\Auth\Middleware\RedirectIfAuthenticated |
password.confirm | Illuminate\Auth\Middleware\RequirePassword |
precognitive | Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests |
signed | Illuminate\Routing\Middleware\ValidateSignature |
subscribed | \Spark\Http\Middleware\VerifyBillableIsSubscribed |
throttle | Illuminate\Routing\Middleware\ThrottleRequests 또는 Illuminate\Routing\Middleware\ThrottleRequestsWithRedis |
verified | Illuminate\Auth\Middleware\EnsureEmailIsVerified |
미들웨어 실행 순서 지정
드물지만, 미들웨어가 특정 순서로 실행되어야 하는데 라우트에 적용하는 순서를 제어하기 어려운 경우가 있습니다. 이럴 때는 bootstrap/app.php 파일에서 priority 메서드로 미들웨어의 우선순위를 명시할 수 있습니다:
->withMiddleware(function (Middleware $middleware): void {
$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,
]);
})미들웨어 파라미터
미들웨어는 추가 파라미터를 받을 수도 있습니다. 예를 들어, 인증된 사용자가 특정 "역할(role)"을 가지고 있는지 확인해야 한다면, 역할 이름을 파라미터로 받는 EnsureUserHasRole 미들웨어를 만들 수 있습니다.
추가 파라미터는 handle 메서드의 $next 인자 뒤에 선언합니다:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserHasRole
{
/**
* 들어오는 요청을 처리합니다.
*
* @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);
}
}라우트 정의 시 미들웨어 이름 뒤에 :을 붙이고 파라미터를 전달합니다:
use App\Http\Middleware\EnsureUserHasRole;
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor');여러 파라미터는 쉼표로 구분합니다:
Route::put('/post/{id}', function (string $id) {
// ...
})->middleware(EnsureUserHasRole::class.':editor,publisher');종료 가능한 미들웨어
경우에 따라 HTTP 응답이 브라우저로 전송된 이후에 추가 작업을 수행해야 할 수 있습니다. 미들웨어에 terminate 메서드를 정의하고 웹 서버가 FastCGI를 사용하고 있다면, 응답이 전송된 후 terminate 메서드가 자동으로 호출됩니다:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class TerminatingMiddleware
{
/**
* 들어오는 요청을 처리합니다.
*
* @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 메서드는 요청($request)과 응답($response) 객체를 모두 받습니다. 종료 가능한 미들웨어를 정의했다면, bootstrap/app.php 파일에서 라우트 미들웨어 또는 전역 미들웨어 목록에 반드시 등록해야 합니다.
기본적으로 Laravel은 terminate 메서드를 호출할 때 서비스 컨테이너에서 새로운 인스턴스를 생성합니다. handle과 terminate에서 동일한 인스턴스를 사용하고 싶다면, 컨테이너에 싱글톤으로 등록하면 됩니다. 보통 AppServiceProvider의 register 메서드에서 처리합니다:
use App\Http\Middleware\TerminatingMiddleware;
/**
* 애플리케이션 서비스를 등록합니다.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}