"Laravel 라우팅"의 두 판 사이의 차이

759번째 줄: 759번째 줄:
==레이트 제한==
==레이트 제한==
===레이트 리미터 정의하기===
===레이트 리미터 정의하기===
Laravel은 주어진 라우트 또는 라우트 그룹에 대한 트래픽 양을 제한하기 위해 사용할 수 있는 강력하고 커스터마이즈가능한 레이트 제한 서비스를 포함하고 있습니다. 시작하려면 애플리케이션의 요구에 맞는 레이트 리미터 설정을 정의해야 합니다.
레이트 리미터는 애플리케이션의 <code>App\Providers\AppServiceProvider</code> 클래스의 <code>boot</code> 메소드 내에서 정의할 수 있습니다:
<syntaxhighlight lang='php'>
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
protected function boot(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
}
</syntaxhighlight>
레이트 리미터는 <code>RateLimiter</code> 파사드의 <code>for</code> 메소드를 사용하여 정의됩니다. <code>for</code> 메서드는 레이트 리미터 이름과 해당 제한 설정(적용할 라우터에 대한 제한 설정을 반환하는 클로저)을 인수로 받습니다. 제한 설정은 <code>Illuminate\Cache\RateLimiting\Limit</code> 클래스의 인스턴스입니다. 이 클래스는 제한을 빠르게 정의할 수 있도록 하는 유용한 "빌더" 메소드를 포함하고 있습니다. 레이트 리미터 이름은 원하는 문자열로 지정할 수 있습니다:
<syntaxhighlight lang='php'>
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
protected function boot(): void
{
    RateLimiter::for('global', function (Request $request) {
        return Limit::perMinute(1000);
    });
}
</syntaxhighlight>
들어오는 요청이 지정된 레이트 제한을 초과하면 Laravel은 자동으로 429 HTTP 상태 코드와 함께 응답을 반환합니다. 레이트 제한에 의해 반환될 응답을 정의하고 싶다면 <code>response</code> 메소드를 사용할 수 있습니다:
<syntaxhighlight lang='php'>
RateLimiter::for('global', function (Request $request) {
    return Limit::perMinute(1000)->response(function (Request $request, array $headers) {
        return response('Custom response...', 429, $headers);
    });
});
</syntaxhighlight>
레이트 리미터 콜백은 들어오는 HTTP 요청 인스턴스를 받기 때문에, 들어오는 요청이나 인증된 사용자에 따라 적절한 레이트 제한을 동적으로 구성할 수 있습니다:
<syntaxhighlight lang='php'>
RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100);
});
</syntaxhighlight>
;레이트 제한 분할
때로는 임의의 값에 따라 레이트 제한을 분할하고 싶을 수 있습니다. 예를 들어, IP 주소별로 주어진 라우트에 대해 분당 100회 접근을 허용하고 싶을 수 있습니다. 이를 위해 레이트 제한을 구성할 때 <code>by</code> 메소드를 사용할 수 있습니다:
<syntaxhighlight lang='php'>
RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100)->by($request->ip());
});
</syntaxhighlight>
또 다른 예시로, 인증된 사용자 ID별로 분당 100회 또는 게스트의 경우 IP 주소별로 분당 10회 접근을 제한할 수 있습니다:
<syntaxhighlight lang='php'>
RateLimiter::for('uploads', function (Request $request) {
    return $request->user()
                ? Limit::perMinute(100)->by($request->user()->id)
                : Limit::perMinute(10)->by($request->ip());
});
</syntaxhighlight>
;다중 레이트 제한
필요한 경우, 주어진 레이트 리미터 설정에 대해 여러 개의 레이트 제한을 배열로 반환할 수 있습니다. 배열 내의 순서에 따라 각 레이트 제한이 라우트에 대해 평가됩니다:
<syntaxhighlight lang='php'>
RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(500),
        Limit::perMinute(3)->by($request->input('email')),
    ];
});
</syntaxhighlight>


===라우트에 레이트 리미터 붙이기===
===라우트에 레이트 리미터 붙이기===

2024년 6월 20일 (목) 21:52 판

1 개요

Crystal Clear action info.png 작성 중인 문서입니다.
Routing
라우팅

https://laravel.com/docs/11.x/routing

2 기본 라우팅

가장 기본적인 Laravel 라우트는 URI와 클로저를 받아들여, 복잡한 라우팅 설정 파일 없이 매우 간단하고 표현력 있는 방법으로 라우트와 동작을 정의할 수 있습니다:

use Illuminate\Support\Facades\Route;
 
Route::get('/greeting', function () {
    return 'Hello World';
});

2.1 기본 라우트 파일

모든 Laravel 라우트는 라우트 파일에 정의되며, 이 파일들은 routes 디렉토리에 위치합니다. 이 파일들은 애플리케이션의 bootstrap/app.php 파일에 지정된 설정에 따라 Laravel에 의해 자동으로 로드됩니다. routes/web.php 파일은 웹 인터페이스를 위한 라우트를 정의합니다. 이 라우트들은 세션 상태와 CSRF 보호와 같은 기능을 제공하는 web 미들웨어 그룹에 할당됩니다.

대부분의 애플리케이션에서, routes/web.php 파일에 라우트를 정의하는 것으로 시작합니다. routes/web.php에 정의된 라우트는 브라우저에서 정의된 라우트의 URL을 입력하여 접근할 수 있습니다. 예를 들어, 아래의 라우트를 http://example.com/user로 이동하여 접근할 수 있습니다:

use App\Http\Controllers\UserController;
 
Route::get('/user', [UserController::class, 'index']);
API 라우트

애플리케이션이 상태를 유지하지 않는 API를 제공할 경우, install:ap Artisan 명령어를 사용하여 API 라우팅을 활성화할 수 있습니다:

php artisan install:api

install:api 명령어는 Laravel Sanctum을 설치하여 견고하면서도 간단한 API 토큰 인증 가드를 제공하며, 이를 통해 제3자 API 소비자, SPA 또는 모바일 애플리케이션을 인증할 수 있습니다. 또한, install:api 명령어는 routes/api.php 파일을 생성합니다:

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

routes/api.php의 라우트는 상태를 유지하지 않으며, api 미들웨어 그룹에 할당됩니다. 추가적으로, 이 라우트들에는 /api URI 접두어가 자동으로 적용되므로, 파일의 모든 라우트에 이를 수동으로 적용할 필요가 없습니다. 애플리케이션의 bootstrap/app.php 파일을 수정하여 접두어를 변경할 수도 있습니다:

->withRouting(
    api: __DIR__.'/../routes/api.php',
    apiPrefix: 'api/admin',
    // ...
)
사용가능한 라우터 메소드

라우터를 사용하면 모든 HTTP 동사에 응답하는 라우트를 등록할 수 있습니다:

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

때로는 여러 HTTP 동사에 응답하는 라우트를 등록해야 할 수도 있습니다. match 메소드를 사용하여 이를 할 수 있습니다. 또는, any 메소드를 사용하여 모든 HTTP 동사에 응답하는 라우트를 등록할 수도 있습니다:

Route::match(['get', 'post'], '/', function () {
    // ...
});
 
Route::any('/', function () {
    // ...
});

Note

같은 URI를 공유하는 여러 라우트를 정의할 때는, get, post, put, patch, delete, options 메소드를 사용하는 라우트를, any, match, redirect 메소드를 사용하는 라우트보다 먼저 정의해야 합니다. 이는 들어오는 요청이 올바른 라우트와 일치하도록 보장합니다.

의존성 주입

라우트의 콜백 시그니처에 필요한 의존성을 타입힌트로 지정할 수 있습니다. 선언된 의존성은 Laravel 서비스 컨테이너에 의해 자동으로 해결되고 콜백에 주입됩니다. 예를 들어, Illuminate\Http\Request 클래스를 타입힌트로 지정하면 현재 HTTP 요청이 라우트 콜백에 자동으로 주입됩니다:

use Illuminate\Http\Request;
 
Route::get('/users', function (Request $request) {
    // ...
});
CSRF 보호

web 라우트 파일에 정의된 POST, PUT, PATCH 또는 DELETE 라우트를 가리키는 모든 HTML 폼에는 CSRF 토큰 필드를 포함해야 합니다. 그렇지 않으면 요청이 거부됩니다. CSRF 보호에 대한 자세한 내용은 CSRF 문서를 참조하십시오:

<form method="POST" action="/profile">
    @csrf
    ...
</form>

2.2 리디렉트 라우트

다른 URI로 리다이렉트되는 라우트를 정의할 때, Route::redirect 메소드를 사용할 수 있습니다. 이 메소드는 간단한 리다이렉트를 수행하기 위해 전체 라우트 또는 컨트롤러를 정의할 필요가 없도록 간편한 방법을 제공합니다:

Route::redirect('/here', '/there');

기본적으로 Route::redirect302 상태 코드를 반환합니다. 선택적인 세 번째 파라미터를 사용하여 상태 코드를 커스터마이징할 수 있습니다:

Route::redirect('/here', '/there', 301);

또는 Route::permanentRedirect 메소드를 사용하여 301 상태 코드를 반환할 수 있습니다:

Route::permanentRedirect('/here', '/there');

Warning

리다이렉트 라우트에서 라우트 파라미터를 사용할 때, 다음 파라미터는 Laravel에 의해 예약되어 사용할 수 없습니다: destinationstatus.

2.3 뷰 라우트

라우트가 단지 를 반환하기만 하면 된다면, Route::view 메소드를 사용할 수 있습니다. 이 메소드는 redirect 메소드처럼 간편한 방법을 제공하여 전체 라우트나 컨트롤러를 정의할 필요가 없습니다. view 메소드는 URI를 첫 번째 파라미터로, 뷰 이름을 두 번째 파라미터로 받습니다. 추가로, 뷰에 전달할 데이터를 배열 형태로 세 번째 파라미터로 제공할 수 있습니다:

Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

Warning

뷰 라우트에서 라우트 파라미터를 사용할 때, Laravel에서 예약된 다음 파라미터들은 사용할 수 없습니다: view, data, status, headers.

2.4 라우트 목록 조회

route:list Artisan 명령어를 사용하면 애플리케이션에 정의된 모든 라우트를 쉽게 확인할 수 있습니다:

php artisan route:list

기본적으로, route:list 출력에서는 각 라우트에 할당된 미들웨어가 표시되지 않습니다. 그러나 명령어에 -v 옵션을 추가하면 라우트 미들웨어와 미들웨어 그룹 이름을 표시하도록 Laravel에 지시할 수 있습니다:

php artisan route:list -v

# 미들웨어 그룹을 확장하려면...
php artisan route:list -vv

특정 URI로 시작하는 라우트만 표시하도록 Laravel에 지시할 수도 있습니다:

php artisan route:list --path=api

또한, 명령어 실행 시 --except-vendor 옵션을 제공하여 서드파티 패키지에 의해 정의된 라우트를 숨길 수 있습니다:

php artisan route:list --except-vendor

마찬가지로, --only-vendor 옵션을 제공하여 서드파티 패키지에 의해 정의된 라우트만 표시하도록 지시할 수도 있습니다:

php artisan route:list --only-vendor

2.5 라우팅 커스터마이징

기본적으로, 애플리케이션의 라우트는 bootstrap/app.php 파일에 의해 설정되고 로드됩니다:

<?php

use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )->create();


그러나 때로는 애플리케이션의 라우트의 부분집합을 포함하는 완전히 새로운 파일을 정의하고 싶을 수 있습니다. 이를 위해, <cod>withRouting 메소드에 then 클로저를 제공할 수 있습니다. 이 클로저 내에서, 애플리케이션에 필요한 추가 라우트를 등록할 수 있습니다:

use Illuminate\Support\Facades\Route;

->withRouting(
    web: __DIR__.'/../routes/web.php',
    commands: __DIR__.'/../routes/console.php',
    health: '/up',
    then: function () {
        Route::middleware('api')
            ->prefix('webhooks')
            ->name('webhooks.')
            ->group(base_path('routes/webhooks.php'));
    },
)

또는, withRouting 메소드에 using 클로저를 제공하여 라우트 등록을 완전히 제어할 수도 있습니다. 이 인수가 전달되면, 프레임워크에 의해 HTTP 라우트가 전혀 등록되지 않으며, 모든 라우트를 수동으로 등록해야 합니다:

use Illuminate\Support\Facades\Route;

->withRouting(
    commands: __DIR__.'/../routes/console.php',
    using: function () {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));

        Route::middleware('web')
            ->group(base_path('routes/web.php'));
    },
)

3 라우트 파라미터

3.1 필수 파라미터

라우트 내에서 URI의 세그먼트를 캡처해야 하는 경우가 있습니다. 예를 들어, URL에서 사용자의 ID를 캡처해야 할 수 있습니다. 이 경우 라우트 파라미터를 정의하여 이를 수행할 수 있습니다:

Route::get('/user/{id}', function (string $id) {
    return 'User ' . $id;
});

필요한 만큼 라우트 파라미터를 정의할 수 있습니다:

Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) {
    // ...
});

라우트 파라미터는 항상 {} 중괄호 안에 넣어야 하며, 알파벳 문자로 구성되어야 합니다. 밑줄(_)도 라우트 파라미터 이름에 사용할 수 있습니다. 라우트 파라미터는 그 순서에 따라 라우트 콜백/컨트롤러에 주입됩니다. 라우트 콜백/컨트롤러 인수의 이름은 중요하지 않습니다.

파라미터 및 의존성 주입

라우트에 Laravel 서비스 컨테이너가 자동으로 주입해야 하는 의존성이 있는 경우, 라우트 파라미터를 의존성 뒤에 나열해야 합니다:

use Illuminate\Http\Request;

Route::get('/user/{id}', function (Request $request, string $id) {
    return 'User ' . $id;
});

3.2 선택 파라미터

때로는 URI에 항상 존재하지 않을 수 있는 라우트 파라미터를 지정해야 할 때가 있습니다. 이럴 때는 파라미터 이름 뒤에 ?를 붙여서 지정할 수 있습니다. 해당 라우트의 변수에 기본 값을 지정해야 합니다:

Route::get('/user/{name?}', function (?string $name = null) {
    return $name;
});
 
Route::get('/user/{name?}', function (?string $name = 'John') {
    return $name;
});

3.3 정규식 제약조건

라우트 인스턴스에서 where 메소드를 사용하여 라우트 파라미터의 형식을 제약할 수 있습니다. where 메소드는 파라미터의 이름과 파라미터가 제약되어야 하는 정규표현식을 인수로 받습니다:

Route::get('/user/{name}', function (string $name) {
    // ...
})->where('name', '[A-Za-z]+');
 
Route::get('/user/{id}', function (string $id) {
    // ...
})->where('id', '[0-9]+');
 
Route::get('/user/{id}/{name}', function (string $id, string $name) {
    // ...
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

편의를 위해, 자주 사용되는 정규표현식 패턴은 헬퍼 메소드를 사용하여 라우트에 빠르게 패턴 제약조건을 추가할 수 있습니다:

Route::get('/user/{id}/{name}', function (string $id, string $name) {
    // ...
})->whereNumber('id')->whereAlpha('name');
 
Route::get('/user/{name}', function (string $name) {
    // ...
})->whereAlphaNumeric('name');
 
Route::get('/user/{id}', function (string $id) {
    // ...
})->whereUuid('id');
 
Route::get('/user/{id}', function (string $id) {
    // ...
})->whereUlid('id');
 
Route::get('/category/{category}', function (string $category) {
    // ...
})->whereIn('category', ['movie', 'song', 'painting']);
 
Route::get('/category/{category}', function (string $category) {
    // ...
})->whereIn('category', CategoryEnum::cases());

들어오는 요청이 라우트 패턴 제약조건과 일치하지 않으면 404 HTTP 응답이 반환됩니다.

글로벌 제약조건

특정 정규표현식에 항상 제약을 받는 라우트 파라미터를 정의하고 싶다면 pattern 메소드를 사용할 수 있습니다. 이러한 패턴은 애플리케이션의 App\Providers\AppServiceProvider 클래스의 boot 메소드에서 정의해야 합니다:

use Illuminate\Support\Facades\Route;
 
/**
 * 애플리케이션 서비스를 부트스트랩합니다.
 */
public function boot(): void
{
    Route::pattern('id', '[0-9]+');
}

패턴이 정의되면 해당 파라미터 이름을 사용하는 모든 라우트에 자동으로 적용됩니다:

Route::get('/user/{id}', function (string $id) {
    // {id}가 숫자일 때만 실행됨...
});
인코딩된 포워드 슬래시

Laravel 라우팅 컴포넌트는 라우트 파라미터 값 내에서 /를 제외한 모든 문자를 허용합니다. 플레이스홀더에 /를 포함하려면 where 조건 정규표현식을 사용하여 명시적으로 허용해야 합니다:

Route::get('/search/{search}', function (string $search) {
    return $search;
})->where('search', '.*');

Warning

인코딩된 포워드 슬래시는 마지막 라우트 세그먼트에서만 지원됩니다.

4 명명된 라우트

명명된 라우트는 특정 라우트에 대한 URL 생성이나 리디렉션을 편리하게 생성할 수 있게 해줍니다. 라우트 정의에 name 메소드를 연결하여 라우트의 이름을 지정할 수 있습니다:

Route::get('/user/profile', function () {
    // ...
})->name('profile');

컨트롤러 액션에 대해 라우트 이름을 지정할 수도 있습니다:

Route::get(
    '/user/profile',
    [UserProfileController::class, 'show']
)->name('profile');

Warning

라우트 이름은 항상 고유해야 합니다.

명명된 라우트로 URL 생성하기

일단 특정 라우트에 이름을 지정하면, Laravel의 routeredirect 헬퍼 함수를 통해 URL이나 리디렉션을 생성할 때 해당 라우트의 이름을 사용할 수 있습니다:

// URL 생성...
$url = route('profile');
 
// 리디렉션 생성...
return redirect()->route('profile');
 
return to_route('profile');

명명된 라우트가 파라미터를 정의하는 경우, 해당 파라미터들을 route 함수의 두 번째 인수로 전달할 수 있습니다. 주어진 파라미터들은 자동으로 생성된 URL의 올바른 위치에 삽입됩니다:

Route::get('/user/{id}/profile', function (string $id) {
    // ...
})->name('profile');
 
$url = route('profile', ['id' => 1]);

배열에 추가 파라미터를 전달하면, 해당 키/값 쌍이 자동으로 생성된 URL의 쿼리 문자열에 추가됩니다:

Route::get('/user/{id}/profile', function (string $id) {
    // ...
})->name('profile');
 
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
 
// /user/1/profile?photos=yes

Note

때로는, 현재 로케일과 같은 URL 파라미터에 대해 요청 전반에 걸쳐 기본값을 지정하고 싶을 수 있습니다. 이를 위해 URL::defaults 메소드를 사용할 수 있습니다.

현재 라우트 검사하기

현재 요청이 특정 명명된 라우트로 라우팅되었는지 확인하려면, Route 인스턴스의 named 메소드를 사용할 수 있습니다. 예를 들어, 라우트 미들웨어에서 현재 라우트 이름을 확인할 수 있습니다:

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
/**
 * Handle an incoming request.
 *
 * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
 */
public function handle(Request $request, Closure $next): Response
{
    if ($request->route()->named('profile')) {
        // ...
    }
 
    return $next($request);
}

5 라우트 그룹

라우트 그룹을 사용하면 각 개별 라우트에서 해당 속성을 정의할 필요 없이 미들웨어와 같은 라우트 속성을 다수의 라우트에 걸쳐 공유할 수 있습니다.

중첩된 그룹은 속성을 상위 그룹과 지능적으로 "병합"하려고 시도합니다. 미들웨어와 where 조건은 병합되며, 이름과 접두어는 추가됩니다. 네임스페이스 구분자와 URI 접두어의 슬래시는 적절한 위치에 자동으로 추가됩니다.

5.1 미들웨어

그룹 내 모든 라우트에 미들웨어를 할당하려면 그룹을 정의하기 전에 middleware 메소드를 사용할 수 있습니다. 미들웨어는 배열에 나열된 순서대로 실행됩니다:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // first 및 second 미들웨어를 사용합니다...
    });

    Route::get('/user/profile', function () {
        // first 및 second 미들웨어를 사용합니다...
    });
});

5.2 컨트롤러

한 그룹의 모든 라우트가 동일한 컨트롤러를 사용하는 경우, controller 메소드를 사용하여 그룹 내 모든 라우트에 대해 공통 컨트롤러를 정의할 수 있습니다. 그런 다음 라우트를 정의할 때는 해당 라우트가 호출하는 컨트롤러 메소드만 제공하면 됩니다:

use App\Http\Controllers\OrderController;

Route::controller(OrderController::class)->group(function () {
    Route::get('/orders/{id}', 'show');
    Route::post('/orders', 'store');
});

5.3 서브도메인 라우팅

라우트 그룹은 서브도메인 라우팅을 처리하는 데도 사용할 수 있습니다. 서브도메인은 라우트 URI와 마찬가지로 라우트 파라미터를 할당할 수 있어 서브도메인의 일부를 캡처하여 라우트나 컨트롤러에서 사용할 수 있습니다. 서브도메인은 그룹을 정의하기 전에 domain 메소드를 호출하여 지정할 수 있습니다:

Route::domain('{account}.example.com')->group(function () {
    Route::get('user/{id}', function (string $account, string $id) {
        // ...
    });
});

Warning

서브도메인 라우트가 접근가능하도록 하려면 루트 도메인 라우트를 등록하기 전에 서브도메인 라우트를 등록해야 합니다. 이렇게 하면 동일한 URI 경로를 가진 서브도메인 라우트를 루트 도메인 라우트가 덮어쓰는 것을 방지할 수 있습니다.

5.4 라우트 접두어

prefix 메소드를 사용하면 그룹 내의 각 라우트에 지정된 URI로 접두어를 붙일 수 있습니다. 예를 들어, 그룹 내 모든 라우트 URI에 admin 접두어를 붙이고 싶다면 다음과 같이 할 수 있습니다:

Route::prefix('admin')->group(function () {
    Route::get('/users', function () {
        // "/admin/users" URL에 매칭됩니다.
    });
});

5.5 라우트 이름 접두어

name 메소드를 사용하면 그룹 내 모든 라우트 이름에 지정된 문자열을 접두어로 붙일 수 있습니다. 예를 들어, 그룹 내 모든 라우트 이름에 admin 접두어를 붙이고 싶다면 다음과 같이 할 수 있습니다. 지정된 문자열이 정확하게 라우트 이름에 접두사로 붙기 때문에 접두사에 반드시 . 문자를 포함해야 합니다:

Route::name('admin.')->group(function () {
    Route::get('/users', function () {
        // 라우트 이름이 "admin.users"로 지정됩니다...
    })->name('users');
});

6 라우트 모델 바인딩

라우트나 컨트롤러 액션에 모델 ID를 주입할 때, 일반적으로 해당 ID에 해당하는 모델을 조회하기 위해 데이터베이스를 쿼리하게 됩니다. Laravel 라우트 모델 바인딩은 모델 인스턴스를 라우트에 자동으로 주입하는 편리한 방법을 제공합니다. 예를 들어, 사용자의 ID를 주입하는 대신, 주어진 ID에 해당하는 전체 User 모델 인스턴스를 주입할 수 있습니다.

6.1 암시적 바인딩

Laravel은 라우트 또는 컨트롤러 액션에 정의된 Eloquent 모델을 해당 타입 힌트된 변수 이름이 라우트 세그먼트 이름과 일치할 때 자동으로 해결합니다. 예를 들면 다음고 같습니다:

use App\Models\User;
 
Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

$user 변수가 App\Models\User Eloquent 모델로 타입 힌트되어 있고, 변수 이름이 {user} URI 세그먼트와 일치하므로, Laravel은 요청 URI의 해당 값과 일치하는 ID를 가진 모델 인스턴스를 자동으로 주입합니다. 만약 데이터베이스에서 일치하는 모델 인스턴스를 찾을 수 없는 경우, 404 HTTP 응답이 자동으로 생성됩니다.

물론, 컨트롤러 메소드를 사용할 때도 암시적 바인딩이 가능합니다. 다시 말해, {user} URI 세그먼트가 App\Models\User 타입 힌트를 가진 컨트롤러의 $user 변수와 매치하는 것을 확인할 수 있습니다:

use App\Http\Controllers\UserController;
use App\Models\User;
 
// 라우트 정의...
Route::get('/users/{user}', [UserController::class, 'show']);
 
// 컨트롤러 메소드 정의...
public function show(User $user)
{
    return view('user.profile', ['user' => $user]);
}

위의 예시에서 {user} URI 세그먼트가 컨트롤러의 $user 변수와 매치하기 때문에, Laravel은 요청된 URI의 값과 일치하는 ID를 가진 User 모델 인스턴스를 자동으로 주입합니다.

소프트 삭제된 모델

일반적으로, 암시적 모델 바인딩은 소프트 삭제된 모델을 조회하지 않습니다. 그러나, 라우트 정의에 withTrashed 메소드를 체이닝하여 암시적 바인딩이 이러한 모델을 조회하도록 지시할 수 있습니다:

use App\Models\User;
 
Route::get('/users/{user}', function (User $user) {
    return $user->email;
})->withTrashed();
키 커스터마이징

때로는 id 이외의 컬럼을 사용하여 Eloquent 모델을 해결하고자 할 수 있습니다. 이를 위해, 라우트 파라미터 정의에서 컬럼을 지정할 수 있습니다:

use App\Models\Post;
 
Route::get('/posts/{post:slug}', function (Post $post) {
    return $post;
});

주어진 모델 클래스에서 항상 id 이외의 데이터베이스 컬럼을 사용하도록 모델 바인딩을 설정하려면, Eloquent 모델에서 getRouteKeyName 메소드를 재정의할 수 있습니다:

/**
 * 모델의 라우트 키를 가져옵니다.
 */
public function getRouteKeyName(): string
{
    return 'slug';
}
커스텀 키와 스코핑

여러 Eloquent 모델을 하나의 라우트 정의에서 암시적으로 바인딩할 때, 두 번째 Eloquent 모델이 이전 Eloquent 모델의 자식이 되도록 스코프를 지정하고 싶을 수 있습니다. 예를 들어, 특정 사용자의 블로그 게시물을 슬러그로 가져오는, 다음 라우트 정의를 살펴 보세요:

use App\Models\Post;
use App\Models\User;
 
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
});

커스텀 키를 사용한 암시적 바인딩을 중첩된 라우트 파라미터로 사용할 때, Laravel은 관계 이름에 대한 규칙을 사용하여 부모를 통해 중첩된 모델을 자동으로 스코프 지정합니다. 이 경우, User 모델이 posts라는 이름의 관계를 가지고 있으며 이를 사용하여 Post 모델을 가져올 수 있다고 가정합니다.

원한다면, 커스텀 키가 제공되지 않았을 때에도 Laravel에 "자식" 바인딩의 스코프를 지정하도록 지시할 수 있습니다. 이를 위해 라우트를 정의할 때 scopeBindings 메소드를 호출할 수 있습니다:

use App\Models\Post;
use App\Models\User;
 
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();

또는, 전체 라우트 정의 그룹이 스코프 지정된 바인딩을 사용하도록 지시할 수 있습니다:

Route::scopeBindings()->group(function () {
    Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
        return $post;
    });
});

마찬가지로, withoutScopedBindings 메소드를 호출하여 Laravel에 스코프 바인딩을 사용하지 않도록 명시적으로 지시할 수 있습니다:

Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
})->withoutScopedBindings();
모델 누락시 동작 커스터마이징

암시적으로 바인딩된 모델을 찾을 수 없으면 일반적으로 404 HTTP 응답이 생성됩니다. 그러나, 라우트를 정의할 때 missing 메소드를 호출하여 이 동작을 커스터마이즈할 수 있습니다. missing 메소드는 암시적으로 바인딩된 모델을 찾을 수 없는 경우 호출될 클로저를 인수로 받습니다:

use App\Http\Controllers\LocationsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
 
Route::get('/locations/{location:slug}', [LocationsController::class, 'show'])
        ->name('locations.view')
        ->missing(function (Request $request) {
            return Redirect::route('locations.index');
        });

6.2 암시적 열거형 바인딩

PHP 8.1은 열거형에 대한 지원을 도입했습니다. 이를 보완하기 위해, Laravel은 라우트 정의에서 백드 열거형을 타입 힌트할 수 있도록 하며, 해당 라우트 세그먼트가 유효한 열거형 값에 해당할 때만 Laravel이 라우트를 호출합니다. 그렇지 않으면, 자동으로 404 HTTP 응답이 반환됩니다. 예를 들어, 다음과 같이 열거형이 주어질 수 있습니다:

<?php

namespace App\Enums;

enum Category: string
{
    case Fruits = 'fruits';
    case People = 'people';
}

{category} 라우트 세그먼트가 fruits 또는 people일 때만 호출되는 라우트를 정의할 수 있습니다. 그렇지 않으면, Laravel은 404 HTTP 응답을 반환합니다:

use App\Enums\Category;
use Illuminate\Support\Facades\Route;

Route::get('/categories/{category}', function (Category $category) {
    return $category->value;
});

6.3 명시적 바인딩

Laravel의 암시적이고 관례 기반의 모델 해결을 사용하지 않아도 됩니다. 라우트 파라미터가 모델에 어떻게 대응되는지를 명시적으로 정의할 수도 있습니다. 명시적 바인딩을 등록하려면, 라우터의 model 메소드를 사용하여 주어진 파라미터에 대한 클래스를 지정하세요. 명시적 모델 바인딩은 AppServiceProvider 클래스의 boot 메소드 시작 부분에 정의해야 합니다:

use App\Models\User;
use Illuminate\Support\Facades\Route;

/**
 * 애플리케이션 서비스를 부트스트랩합니다.
 */
public function boot(): void
{
    Route::model('user', User::class);
}

다음으로, {user} 파라미터를 포함하는 라우트를 정의합니다:

use App\Models\User;

Route::get('/users/{user}', function (User $user) {
    // ...
});

{user} 매개변수는 App\Models\User 모델에 바인딩되므로, 이 클래스의 인스턴스가 라우트에 주입됩니다. 예를 들어, users/1 요청은 데이터베이스에서 ID가 1User 인스턴스를 주입합니다.

매치하는 모델 인스턴스를 데이터베이스에서 찾지 못하면, 자동으로 404 HTTP 응답이 생성됩니다.

해결 로직 커스터마이징

모델 바인딩 해석 로직을 직접 정의하려면, Route::bind 메소드를 사용할 수 있습니다. bind 메소드에 전달된 클로저는 URI 세그먼트의 값을 받아야 하며, 라우트에 주입할 클래스의 인스턴스를 반환해야 합니다. 이 커스터마이징도 애플리케이션의 AppServiceProvider 클래스의 boot 메소드에서 이루어져야 합니다:

use App\Models\User;
use Illuminate\Support\Facades\Route;

/**
 * 애플리케이션 서비스를 부트스트랩합니다.
 */
public function boot(): void
{
    Route::bind('user', function (string $value) {
        return User::where('name', $value)->firstOrFail();
    });
}

또한, Eloquent 모델의 resolveRouteBinding 메소드를 재정의할 수도 있습니다. 이 메소드는 URI 세그먼트의 값을 받아야 하며, 라우트에 주입할 클래스의 인스턴스를 반환해야 합니다:

/**
 * 바운드된 값에 대한 모델을 가져옵니다.
 *
 * @param  mixed  $value
 * @param  string|null  $field
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveRouteBinding($value, $field = null)
{
    return $this->where('name', $value)->firstOrFail();
}

라우트가 암시적 바인딩 스코프를 사용하고 있다면, 부모 모델의 자식 바인딩을 해결하기 위해 resolveChildRouteBinding 메소드가 사용됩니다:

/**
 * 바운드된 값에 대한 자식 모델을 가져옵니다.
 *
 * @param  string  $childType
 * @param  mixed  $value
 * @param  string|null  $field
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveChildRouteBinding($childType, $value, $field)
{
    return parent::resolveChildRouteBinding($childType, $value, $field);
}

7 폴백 라우트

Route::fallback 메소드를 사용하여 들어오는 요청과 매치하는 다른 라우트가 없을 때 실행될 라우트를 정의할 수 있습니다. 일반적으로 처리되지 않은 요청은 애플리케이션의 예외 처리기를 통해 자동으로 "404" 페이지를 렌더링합니다. 그러나 fallback 라우트를 routes/web.php 파일 내에 정의하면, web 미들웨어 그룹의 모든 미들웨어가 이 라우트에 적용됩니다. 필요에 따라 이 라우트에 추가 미들웨어를 추가할 수 있습니다:

Route::fallback(function () {
    // ...
});

Warning

Fallback 라우트는 항상 애플리케이션에 등록된 마지막 라우트여야 합니다.

8 레이트 제한

8.1 레이트 리미터 정의하기

Laravel은 주어진 라우트 또는 라우트 그룹에 대한 트래픽 양을 제한하기 위해 사용할 수 있는 강력하고 커스터마이즈가능한 레이트 제한 서비스를 포함하고 있습니다. 시작하려면 애플리케이션의 요구에 맞는 레이트 리미터 설정을 정의해야 합니다.

레이트 리미터는 애플리케이션의 App\Providers\AppServiceProvider 클래스의 boot 메소드 내에서 정의할 수 있습니다:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

/**
 * 애플리케이션 서비스를 부트스트랩합니다.
 */
protected function boot(): void
{
    RateLimiter::for('api', function (Request $request) {
        return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
}

레이트 리미터는 RateLimiter 파사드의 for 메소드를 사용하여 정의됩니다. for 메서드는 레이트 리미터 이름과 해당 제한 설정(적용할 라우터에 대한 제한 설정을 반환하는 클로저)을 인수로 받습니다. 제한 설정은 Illuminate\Cache\RateLimiting\Limit 클래스의 인스턴스입니다. 이 클래스는 제한을 빠르게 정의할 수 있도록 하는 유용한 "빌더" 메소드를 포함하고 있습니다. 레이트 리미터 이름은 원하는 문자열로 지정할 수 있습니다:

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

/**
 * 애플리케이션 서비스를 부트스트랩합니다.
 */
protected function boot(): void
{
    RateLimiter::for('global', function (Request $request) {
        return Limit::perMinute(1000);
    });
}

들어오는 요청이 지정된 레이트 제한을 초과하면 Laravel은 자동으로 429 HTTP 상태 코드와 함께 응답을 반환합니다. 레이트 제한에 의해 반환될 응답을 정의하고 싶다면 response 메소드를 사용할 수 있습니다:

RateLimiter::for('global', function (Request $request) {
    return Limit::perMinute(1000)->response(function (Request $request, array $headers) {
        return response('Custom response...', 429, $headers);
    });
});

레이트 리미터 콜백은 들어오는 HTTP 요청 인스턴스를 받기 때문에, 들어오는 요청이나 인증된 사용자에 따라 적절한 레이트 제한을 동적으로 구성할 수 있습니다:

RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100);
});
레이트 제한 분할

때로는 임의의 값에 따라 레이트 제한을 분할하고 싶을 수 있습니다. 예를 들어, IP 주소별로 주어진 라우트에 대해 분당 100회 접근을 허용하고 싶을 수 있습니다. 이를 위해 레이트 제한을 구성할 때 by 메소드를 사용할 수 있습니다:

RateLimiter::for('uploads', function (Request $request) {
    return $request->user()->vipCustomer()
                ? Limit::none()
                : Limit::perMinute(100)->by($request->ip());
});

또 다른 예시로, 인증된 사용자 ID별로 분당 100회 또는 게스트의 경우 IP 주소별로 분당 10회 접근을 제한할 수 있습니다:

RateLimiter::for('uploads', function (Request $request) {
    return $request->user()
                ? Limit::perMinute(100)->by($request->user()->id)
                : Limit::perMinute(10)->by($request->ip());
});
다중 레이트 제한

필요한 경우, 주어진 레이트 리미터 설정에 대해 여러 개의 레이트 제한을 배열로 반환할 수 있습니다. 배열 내의 순서에 따라 각 레이트 제한이 라우트에 대해 평가됩니다:

RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(500),
        Limit::perMinute(3)->by($request->input('email')),
    ];
});

8.2 라우트에 레이트 리미터 붙이기

레이트 리미터는 throttle 미들웨어를 사용하여 라우트 또는 라우트 그룹에 부착할 수 있습니다. throttle 미들웨어는 라우트에 할당하려는 레이트 리미터의 이름을 인수로 받습니다:

Route::middleware(['throttle:uploads'])->group(function () {
    Route::post('/audio', function () {
        // ...
    });

    Route::post('/video', function () {
        // ...
    });
});
Redis를 사용한 쓰로틀링

기본적으로 throttle 미들웨어는 Illuminate\Routing\Middleware\ThrottleRequests 클래스에 매핑됩니다. 그러나, 애플리케이션의 캐시 드라이버로 Redis를 사용하는 경우, Laravel이 레이트 리미팅을 관리하기 위해 Redis를 사용하도록 지시할 수 있습니다. 이를 위해, 애플리케이션의 bootstrap/app.php 파일에서 throttleWithRedis 메소드를 사용해야 합니다. 이 메소드는 throttle 미들웨어를 Illuminate\Routing\Middleware\ThrottleRequestsWithRedis 미들웨어 클래스에 매핑합니다:

->withMiddleware(function (Middleware $middleware) {
    $middleware->throttleWithRedis();
    // ...
})

9 폼 메소드 스푸핑

HTML 폼은 PUT, PATCH, DELETE 액션을 지원하지 않습니다. 따라서 HTML 폼에서 호출되는 PUT, PATCH, DELETE 라우트를 정의할 때, 폼에 숨겨진 _method 필드를 추가해야 합니다. _method 필드에 전송된 값이 HTTP 요청 메소드로 사용됩니다:

<form action="/example" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

편의를 위해, @method Blade 디렉티브를 사용하여 _method 입력 필드를 생성할 수 있습니다:

<form action="/example" method="POST">
    @method('PUT')
    @csrf
</form>

10 현재 라우트에 접근하기

Route 파사드의 current, currentRouteName, currentRouteAction 메소드를 사용하여 들어오는 요청을 처리하는 라우트에 대한 정보를 접근할 수 있습니다:

use Illuminate\Support\Facades\Route;

$route = Route::current(); // Illuminate\Routing\Route
$name = Route::currentRouteName(); // string
$action = Route::currentRouteAction(); // string

Route 파사드Route 인스턴스의 기본 클래스에 대한 API 문서를 참조하여 라우터와 라우트 클래스에서 사용할 수 있는 모든 메소드를 살펴볼 수 있습니다.

11 크로스 오리진 리소스 공유 (CORS)

Laravel은 사용자가 설정한 값으로 CORS OPTIONS HTTP 요청에 자동으로 응답할 수 있습니다. OPTIONS 요청은 애플리케이션의 글로벌 미들웨어 스택에 자동으로 포함된 HandleCors 미들웨어에 의해 자동으로 처리됩니다.

때때로, 애플리케이션의 CORS 설정 값을 커스터마이징해야 할 필요가 있을 수 있습니다. 이를 위해 config:publish Artisan 명령어를 사용하여 cors 설정 파일을 퍼블리싱할 수 있습니다:

php artisan config:publish cors

이 명령어는 애플리케이션의 config 디렉토리에 cors.php 설정 파일을 배치합니다.

Note

CORS와 CORS 헤더에 대한 자세한 내용은 CORS에 관한 MDN 웹 문서를 참조하십시오.

12 라우트 캐싱

애플리케이션을 프로덕션에 배포할 때 Laravel의 라우트 캐싱을 활용해야 합니다. 라우트 캐싱을 사용하면 애플리케이션의 모든 라우트를 등록하는 데 걸리는 시간이 크게 줄어듭니다. 라우트 캐시를 생성하려면 다음과 같이 route:cache Artisan 명령어를 실행하십시오:

php artisan route:cache

이 명령어를 실행한 후에는 요청마다 캐시된 라우트 파일이 로드됩니다. 새로운 라우트를 추가하는 경우에는 새로운 라우트 캐시를 생성해야 한다는 점을 기억하세요. 따라서 route:cache 명령어는 프로젝트 배포 중에만 실행하는 것이 좋습니다.

라우트 캐시를 지우려면 route:clear 명령어를 사용할 수 있습니다:

php artisan route:clear

13 같이 보기

문서 댓글 ({{ doc_comments.length }})
{{ comment.name }} {{ comment.created | snstime }}