본문으로 건너뛰기
버전: 10.x

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을 생성할 수 있습니다. 이름이 지정된 라우트(named 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 모델

URL을 생성할 때 대부분의 경우 Eloquent 모델의 라우트 키(보통은 기본 키)를 사용하게 됩니다. 이를 위해, Eloquent 모델 인스턴스를 파라미터 값으로 바로 전달할 수 있습니다. route 헬퍼가 모델의 라우트 키를 자동으로 추출해 사용합니다.

echo route('post.show', ['post' => $post]);

서명된 URL

라라벨에서는 이름이 지정된 라우트에 대해 "서명된" URL을 손쉽게 생성할 수 있습니다. 이러한 URL에는 쿼리 스트링에 "시그니처" 해시가 추가되어 라라벨이 URL이 생성된 이후로 변경되지 않았음을 검증할 수 있습니다. 서명된 URL은 누구나 접근할 수 있는 공개 라우트이면서도, URL 조작에 대한 보호가 필요한 경우에 특히 유용합니다.

예를 들어, 고객에게 이메일로 발송하는 공개 "구독 취소" 링크를 구현할 때 서명된 URL을 사용할 수 있습니다. 이름이 지정된 라우트에 대한 서명된 URL을 생성하려면 URL 파사드의 signedRoute 메서드를 사용하면 됩니다.

use Illuminate\Support\Facades\URL;

return URL::signedRoute('unsubscribe', ['user' => 1]);

서명된 URL의 해시에서 도메인을 제외하고 싶다면 signedRoute 메서드에 absolute 인수를 제공하면 됩니다.

return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);

특정 시간 후에 만료되도록 임시 서명 URL을 만들고 싶다면 temporarySignedRoute 메서드를 사용할 수 있습니다. 라라벨이 임시 서명 URL을 검증할 때, 서명된 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);
}

요청 인스턴스를 직접 사용하지 않고, 라우트에 Illuminate\Routing\Middleware\ValidateSignature 미들웨어를 할당하여 서명 URL을 검증할 수도 있습니다. 만약 이 미들웨어가 등록되어 있지 않다면, HTTP 커널의 $middlewareAliases 배열에 별칭을 추가하세요.

/**
* 애플리케이션의 미들웨어 별칭 목록입니다.
*
* 별칭을 사용하면 라우트와 그룹에 미들웨어를 간편하게 지정할 수 있습니다.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];

커널에 미들웨어가 등록되었다면, 해당 미들웨어를 라우트에 붙일 수 있습니다. 요청의 시그니처가 유효하지 않을 경우, 미들웨어가 자동으로 403 HTTP 응답을 반환합니다.

Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');

만약 서명된 URL의 해시에 도메인이 포함되어 있지 않다면, 미들웨어에 relative 인수를 전달합니다.

Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed:relative');

잘못된 서명 URL에 대한 응답 처리

누군가 만료된 서명 URL로 접근하면, 일반적으로 403 HTTP 상태 코드의 에러 페이지가 표시됩니다. 이 동작은 예외 핸들러에서 InvalidSignatureException 예외에 대해 커스텀 "렌더러블(renderable)" 클로저를 정의함으로써 직접 제어할 수 있습니다. 이 클로저는 HTTP 응답을 반환해야 합니다.

use Illuminate\Routing\Exceptions\InvalidSignatureException;

/**
* 애플리케이션의 예외 처리 콜백을 등록합니다.
*/
public function register(): void
{
$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\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;

class SetDefaultLocaleForUrls
{
/**
* 들어오는 요청을 처리합니다.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
URL::defaults(['locale' => $request->user()->locale]);

return $next($request);
}
}

locale 파라미터의 기본값이 설정되면, route 헬퍼로 URL을 생성할 때 값을 별도로 전달하지 않아도 됩니다.

URL 기본값과 미들웨어 우선순위

URL 기본값을 설정하는 미들웨어는 라라벨의 암시적 모델 바인딩 처리와 충돌할 수 있습니다. 따라서 URL 기본값을 설정하는 미들웨어는 라라벨의 SubstituteBindings 미들웨어보다 먼저 실행되도록 미들웨어 우선순위를 지정해야 합니다. 이를 위해 애플리케이션 HTTP 커널의 $middlewarePriority 속성에서 해당 미들웨어를 SubstituteBindings보다 앞에 위치시키세요.

$middlewarePriority 속성은 기본적으로 Illuminate\Foundation\Http\Kernel 클래스에 정의되어 있습니다. 이 속성을 애플리케이션의 HTTP 커널로 복사해서 순서를 수정할 수 있습니다.

/**
* 우선순위가 지정된 미들웨어 목록입니다.
*
* 이 목록은 글로벌 미들웨어가 아닌 미들웨어의 실행 순서를 강제합니다.
*
* @var array
*/
protected $middlewarePriority = [
// ...
\App\Http\Middleware\SetDefaultLocaleForUrls::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// ...
];