미들웨어
소개
미들웨어는 애플리케이션에 들어오는 HTTP 요청을 검사하고 필터링할 수 있는 편리한 메커니즘을 제공합니다. 예를 들어, Laravel에는 사용자가 인증되었는지 확인하는 미들웨어가 포함되어 있습니다. 사용자가 인증되지 않은 경우, 미들웨어는 사용자를 애플리케이션의 로그인 화면으로 리디렉션합니다. 하지만 사용자가 인증된 경우, 미들웨어는 요청이 애플리케이션 내부로 더 진행될 수 있도록 허용합니다.
인증 외에도 다양한 작업을 수행하는 추가 미들웨어를 작성할 수 있습니다. 예를 들어, 로깅 미들웨어는 애플리케이션에 들어오는 모든 요청을 기록할 수 있습니다. Laravel에는 인증 및 CSRF 보호를 위한 미들웨어 등 다양한 미들웨어가 포함되어 있지만, 사용자가 정의한 모든 미들웨어는 일반적으로 애플리케이션의 app/Http/Middleware
디렉터리에 위치합니다.
미들웨어 정의하기
새로운 미들웨어를 생성하려면 make:middleware
아티즌 명령어를 사용하세요:
php artisan make:middleware EnsureTokenIsValid
이 명령어는 새로운 EnsureTokenIsValid
클래스를 app/Http/Middleware
디렉터리에 생성합니다. 이 미들웨어에서는 제공된 token
입력값이 지정된 값과 일치할 때만 라우트에 접근을 허용합니다. 그렇지 않으면 사용자를 /home
URI로 리디렉션합니다:
<?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
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) {
$middleware->append(EnsureTokenIsValid::class);
})
withMiddleware
클로저에 제공되는 $middleware
객체는 Illuminate\Foundation\Configuration\Middleware
의 인스턴스이며, 애플리케이션의 라우트에 할당된 미들웨어를 관리하는 역할을 합니다. append
메서드는 미들웨어를 글로벌 미들웨어 목록의 끝에 추가합니다. 목록의 맨 앞에 미들웨어를 추가하고 싶다면 prepend
메서드를 사용하세요.
Laravel의 기본 글로벌 미들웨어 수동 관리
Laravel의 글로벌 미들웨어 스택을 직접 관리하고 싶다면, use
메서드에 Laravel의 기본 글로벌 미들웨어 스택을 제공할 수 있습니다. 그런 다음 필요에 따라 기본 미들웨어 스택을 조정할 수 있습니다:
->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
메서드를 호출하면 됩니다:
use App\Http\Middleware\EnsureTokenIsValid;
Route::get('/profile', function () {
// ...
})->middleware(EnsureTokenIsValid::class);
middleware
메서드에 미들웨어 이름의 배열을 전달하여 여러 미들웨어를 라우트에 할당할 수도 있습니다:
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 () {
// ...
});
});
withoutMiddleware
메서드는 라우트 미들웨어만 제거할 수 있으며, 글로벌 미들웨어에는 적용되지 않습니다.
미들웨어 그룹
여러 미들웨어를 하나의 키로 묶어 라우트에 더 쉽게 할당하고 싶을 때가 있습니다. 애플리케이션의 bootstrap/app.php
파일에서 appendToGroup
메서드를 사용하여 이를 구현할 수 있습니다:
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,
]);
})
미들웨어 그룹은 개별 미들웨어와 동일한 문법으로 라우트 및 컨트롤러 액션에 할당할 수 있습니다:
Route::get('/', function () {
// ...
})->middleware('group-name');
Route::middleware(['group-name'])->group(function () {
// ...
});
Laravel의 기본 미들웨어 그룹
Laravel에는 웹 및 API 라우트에 적용할 수 있는 일반적인 미들웨어가 포함된 web
및 api
미들웨어 그룹이 미리 정의되어 있습니다. Laravel은 이 미들웨어 그룹을 각각 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
메서드를 사용할 수 있습니다. web
및 api
메서드는 appendToGroup
메서드의 편리한 대안입니다:
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의 기본 미들웨어 그룹 항목 중 하나를 사용자 정의 미들웨어로 교체할 수도 있습니다:
use App\Http\Middleware\StartCustomSession;
use Illuminate\Session\Middleware\StartSession;
$middleware->web(replace: [
StartSession::class => StartCustomSession::class,
]);
또는 미들웨어를 완전히 제거할 수도 있습니다:
$middleware->web(remove: [
StartSession::class,
]);
Laravel의 기본 미들웨어 그룹 수동 관리
Laravel의 기본 web
및 api
미들웨어 그룹 내의 모든 미들웨어를 직접 관리하고 싶다면, 그룹을 완전히 재정의할 수 있습니다. 아래 예시는 기본 미들웨어로 web
및 api
미들웨어 그룹을 정의하며, 필요에 따라 커스터마이즈할 수 있습니다:
->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
기본적으로 web
및 api
미들웨어 그룹은 bootstrap/app.php
파일에 의해 애플리케이션의 해당 routes/web.php
및 routes/api.php
파일에 자동으로 적용됩니다.
미들웨어 별칭
애플리케이션의 bootstrap/app.php
파일에서 미들웨어에 별칭을 지정할 수 있습니다. 미들웨어 별칭을 사용하면 특정 미들웨어 클래스에 짧은 별칭을 정의할 수 있어, 클래스 이름이 긴 미들웨어에 특히 유용합니다:
use App\Http\Middleware\EnsureUserIsSubscribed;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'subscribed' => EnsureUserIsSubscribed::class
]);
})
미들웨어 별칭이 bootstrap/app.php
파일에 정의되면, 라우트에 미들웨어를 할당할 때 별칭을 사용할 수 있습니다:
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) {
$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
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);
}
}
미들웨어 파라미터는 미들웨어 이름과 파라미터를 :
로 구분하여 라우트를 정의할 때 지정할 수 있습니다:
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
{
/**
* 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은 서비스 컨테이너에서 새로운 미들웨어 인스턴스를 해석합니다. handle
및 terminate
메서드가 호출될 때 동일한 미들웨어 인스턴스를 사용하고 싶다면, 컨테이너의 singleton
메서드를 사용하여 미들웨어를 등록하세요. 일반적으로 이는 AppServiceProvider
의 register
메서드에서 수행해야 합니다:
use App\Http\Middleware\TerminatingMiddleware;
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(TerminatingMiddleware::class);
}