URL 생성 (URL Generation)
소개
라라벨은 애플리케이션에서 URL을 생성할 때 편리하게 사용할 수 있는 여러 헬퍼 함수를 제공합니다. 이 헬퍼들은 주로 템플릿과 API 응답에서의 링크 생성이나, 애플리케이션 내 다른 위치로 리디렉션을 할 때 유용하게 사용됩니다.
기본 사항
URL 생성하기
url
헬퍼 함수를 사용하여 애플리케이션의 임의의 URL을 생성할 수 있습니다. 이 때 생성되는 URL은 처리 중인 현재 요청의 스킴(HTTP 또는 HTTPS)과 호스트 정보를 자동으로 반영합니다.
$post = App\Models\Post::find(1);
echo url("/posts/{$post->id}");
// http://example.com/posts/1
현재 URL 가져오기
url
헬퍼에 경로를 전달하지 않으면 Illuminate\Routing\UrlGenerator
인스턴스가 반환되어, 현재 URL에 관한 정보를 조회할 수 있습니다.
// 쿼리 문자열을 제외한 현재 URL 가져오기...
echo url()->current();
// 쿼리 문자열을 포함한 전체 현재 URL 가져오기...
echo url()->full();
// 이전 요청의 전체 URL 가져오기...
echo url()->previous();
이러한 메서드들은 파사드인 URL
을 통해서도 사용할 수 있습니다.
use Illuminate\Support\Facades\URL;
echo URL::current();
이름이 지정된 라우트의 URL
route
헬퍼는 이름이 지정된 라우트로 이동하는 URL을 생성할 때 사용합니다. 이름이 지정된 라우트를 사용하면 실제 라우트의 URL에 직접 의존하지 않아도 되어, 라우트 URL이 변경되더라도 route
함수 호출 코드를 수정할 필요가 없습니다. 예를 들어, 아래와 같은 라우트가 있다고 가정해보겠습니다.
Route::get('/post/{post}', function (Post $post) {
//
})->name('post.show');
이 라우트로 이동하는 URL을 생성하려면 다음과 같이 route
헬퍼를 사용하면 됩니다.
echo route('post.show', ['post' => 1]);
// http://example.com/post/1
물론, route
헬퍼는 여러 개의 파라미터를 가지는 라우트에 대해서도 URL을 생성할 수 있습니다.
Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) {
//
})->name('comment.show');
echo route('comment.show', ['post' => 1, 'comment' => 3]);
// http://example.com/post/1/comment/3
라우트 정의에 없는 추가 배열 요소들은 URL의 쿼리 문자열로 자동으로 추가됩니다.
echo route('post.show', ['post' => 1, 'search' => 'rocket']);
// http://example.com/post/1?search=rocket
Eloquent 모델
Eloquent 모델의 라우트 키(보통 기본 키 값)를 이용해 URL을 생성하는 경우가 많습니다. 이런 경우, 파라미터 값으로 Eloquent 모델을 직접 전달할 수 있으며, route
헬퍼가 모델의 라우트 키를 자동으로 추출하여 사용합니다.
echo route('post.show', ['post' => $post]);
서명된 URL
라라벨은 이름이 지정된 라우트에 대해 "서명된" URL을 손쉽게 만들 수 있도록 지원합니다. 서명된 URL은 쿼리 문자열에 "서명(signature)" 해시값이 추가되어, 생성 이후 URL이 변경되지 않았음을 라라벨이 검증할 수 있도록 해줍니다. 서명된 URL은 외부에 공개되지만 URL 변조로부터 보호해야 하는 라우트에서 특히 유용합니다.
예를 들어, 이메일로 전송하는 공개 "구독 해지" 링크를 구현할 때 서명된 URL을 사용할 수 있습니다. 이름이 지정된 라우트의 서명된 URL을 생성하려면 URL
파사드의 signedRoute
메서드를 사용하세요.
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);
지정한 시간 후 만료되는 임시 서명 URL을 만들고 싶다면 temporarySignedRoute
메서드를 사용할 수 있습니다. 이 경우 라라벨은 서명된 URL 안에 암호화된 만료 타임스탬프가 아직 유효한지 확인합니다.
use Illuminate\Support\Facades\URL;
return URL::temporarySignedRoute(
'unsubscribe', now()->addMinutes(30), ['user' => 1]
);
서명된 라우트 요청 검증
들어오는 요청이 유효한 서명을 가지고 있는지 확인하려면, 요청 객체(Illuminate\Http\Request
)에서 hasValidSignature
메서드를 호출해야 합니다.
use Illuminate\Http\Request;
Route::get('/unsubscribe/{user}', function (Request $request) {
if (! $request->hasValidSignature()) {
abort(401);
}
// ...
})->name('unsubscribe');
때로는, 프론트엔드에서 페이지네이션 등으로 URL에 데이터를 추가해야 할 수도 있습니다. 이럴 경우, 인증에서 무시해야 할 쿼리 파라미터를 hasValidSignatureWhileIgnoring
메서드에 지정할 수 있습니다. 단, 무시된 파라미터는 누구나 자유롭게 수정할 수 있다는 점을 주의하세요.
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}
서명된 URL을 직접 검증하는 대신, 라우트에 Illuminate\Routing\Middleware\ValidateSignature
미들웨어를 부여할 수도 있습니다. 만약 이 미들웨어가 등록되어 있지 않다면 HTTP 커널의 routeMiddleware
배열에 키를 할당해 추가합니다.
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];
미들웨어 등록을 마쳤다면, 라우트에 해당 미들웨어를 적용할 수 있습니다. 요청이 유효한 서명을 포함하지 않은 경우 미들웨어는 자동으로 403
HTTP 응답을 반환합니다.
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');
잘못된 서명 라우트에 대한 응답
사용자가 만료된 서명 URL에 접근하면, 기본적으로 403
HTTP 상태 코드에 해당하는 일반적인 에러 페이지가 표시됩니다. 하지만 예외 처리기에서 InvalidSignatureException
예외에 대한 커스텀 "renderable" 클로저를 정의하여 이 동작을 원하는 대로 맞춤 설정할 수 있습니다. 이 클로저는 HTTP 응답을 반환해야 합니다.
use Illuminate\Routing\Exceptions\InvalidSignatureException;
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->renderable(function (InvalidSignatureException $e) {
return response()->view('error.link-expired', [], 403);
});
}
컨트롤러 액션의 URL
action
함수는 지정한 컨트롤러 액션에 해당하는 URL을 생성합니다.
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);
만약 컨트롤러 메서드가 라우트 파라미터를 받는 경우, 두 번째 인수로 연관 배열 형태의 파라미터를 전달할 수 있습니다.
$url = action([UserController::class, 'profile'], ['id' => 1]);
기본값 지정
일부 애플리케이션에서는 특정 URL 파라미터에 대해 요청마다 기본값을 지정하고 싶을 수도 있습니다. 예를 들어, 여러 라우트에서 {locale}
파라미터를 사용하는 경우를 가정해보세요.
Route::get('/{locale}/posts', function () {
//
})->name('post.index');
매번 route
헬퍼를 호출할 때마다 locale
값을 일일이 전달하는 것은 번거로운 일입니다. 이때는 URL::defaults
메서드를 이용해 해당 파라미터의 기본값을 설정할 수 있습니다. 이렇게 하면 현재 요청에 한해 항상 이 기본값이 자동으로 적용됩니다. 보통 라우트 미들웨어 내에서 현재 요청 정보를 활용하여 호출하는 것이 좋습니다.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\URL;
class SetDefaultLocaleForUrls
{
/**
* Handle the incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\Response
*/
public function handle($request, Closure $next)
{
URL::defaults(['locale' => $request->user()->locale]);
return $next($request);
}
}
locale
파라미터의 기본값을 설정한 후에는, 이후 route
헬퍼를 사용할 때 더 이상 별도로 값을 전달하지 않아도 됩니다.
URL 기본값과 미들웨어 우선순위
URL 기본값을 지정하면 라라벨의 암묵적 모델 바인딩 처리에 영향을 줄 수 있습니다. 따라서 URL 기본값을 설정하는 미들웨어가 라라벨의 SubstituteBindings
미들웨어보다 먼저 실행되도록 미들웨어 우선순위를 반드시 조정해야 합니다. 이를 위해서는 애플리케이션 HTTP 커널의 $middlewarePriority
프로퍼티에서, 해당 미들웨어가 SubstituteBindings
미들웨어보다 앞서 위치하도록 설정하면 됩니다.
$middlewarePriority
프로퍼티는 기본적으로 Illuminate\Foundation\Http\Kernel
클래스에 정의되어 있습니다. 정의를 복사하여 애플리케이션의 HTTP 커널에 덮어쓰고, 필요한 순서로 수정하면 됩니다.
/**
* The priority-sorted list of middleware.
*
* This forces non-global middleware to always be in the given order.
*
* @var array
*/
protected $middlewarePriority = [
// ...
\App\Http\Middleware\SetDefaultLocaleForUrls::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// ...
];