Laravel Fortify
번역일: 2026년 6월 25일
Laravel Fortify
소개
Laravel Fortify는 프론트엔드에 독립적인 Laravel 인증 백엔드 구현체입니다. Fortify는 로그인, 회원가입, 비밀번호 재설정, 이메일 인증 등 Laravel의 인증 기능 전반에 필요한 라우트와 컨트롤러를 자동으로 등록합니다. 설치 후 route:list Artisan 명령어를 실행하면 Fortify가 등록한 라우트 목록을 확인할 수 있습니다.
Fortify는 자체 UI를 제공하지 않으므로, 직접 만든 UI에서 Fortify가 등록한 라우트로 요청을 보내는 방식으로 사용합니다. 각 라우트에 요청을 보내는 방법은 이 문서 전반에 걸쳐 자세히 설명합니다.
NOTE
Fortify는 Laravel 인증 기능 구현을 빠르게 시작할 수 있도록 도와주는 패키지입니다. 반드시 사용해야 하는 것은 아닙니다. 인증, 비밀번호 재설정, 이메일 인증 문서를 참고하여 직접 구현해도 됩니다.
Fortify란 무엇인가?
앞서 설명했듯이, Laravel Fortify는 프론트엔드에 독립적인 인증 백엔드 구현체입니다. 로그인, 회원가입, 비밀번호 재설정, 이메일 인증 등에 필요한 라우트와 컨트롤러를 자동으로 등록해 줍니다.
Laravel 인증 기능을 사용하는 데 Fortify가 반드시 필요하지는 않습니다. 인증, 비밀번호 재설정, 이메일 인증 문서를 참고하여 직접 구현할 수도 있습니다.
Laravel을 처음 접한다면, Fortify보다 먼저 Laravel Breeze 스타터 킷을 살펴보는 것을 권장합니다. Breeze는 Tailwind CSS로 만들어진 UI를 포함한 인증 스캐폴딩을 제공합니다. Fortify와 달리 Breeze는 라우트와 컨트롤러를 프로젝트 내부에 직접 게시하므로, 코드를 직접 보며 Laravel 인증의 동작 방식을 익히기에 좋습니다.
한 마디로, Fortify는 Laravel Breeze의 라우트와 컨트롤러를 UI 없이 패키지 형태로 제공하는 것입니다. 특정 프론트엔드 방식에 얽매이지 않으면서 인증 백엔드를 빠르게 구성하고 싶을 때 유용합니다.
언제 Fortify를 사용해야 할까?
언제 Fortify를 사용하는 것이 적합한지 궁금할 수 있습니다. 우선, Laravel의 애플리케이션 스타터 킷을 사용하고 있다면 Fortify를 별도로 설치할 필요가 없습니다. 스타터 킷에는 이미 완전한 인증 구현이 포함되어 있기 때문입니다.
스타터 킷을 사용하지 않으면서 인증 기능이 필요하다면 두 가지 선택지가 있습니다. 첫째는 직접 인증 기능을 구현하는 것이고, 둘째는 Fortify를 사용해 백엔드 구현을 맡기는 것입니다.
Fortify를 선택했다면, UI에서 이 문서에 설명된 Fortify의 인증 라우트로 요청을 보내 사용자 인증 및 회원가입을 처리하면 됩니다.
직접 구현하고 싶다면 인증, 비밀번호 재설정, 이메일 인증 문서를 참고하세요.
Laravel Fortify와 Laravel Sanctum
Laravel Sanctum과 Fortify의 차이를 헷갈려 하는 분들이 많습니다. 두 패키지는 서로 다른 문제를 해결하며, 경쟁 관계가 아닌 상호 보완적인 관계입니다.
Laravel Sanctum은 API 토큰 관리와 세션 쿠키 또는 토큰을 통한 기존 사용자 인증에만 집중합니다. Sanctum은 회원가입이나 비밀번호 재설정 같은 라우트를 제공하지 않습니다.
API를 제공하거나 SPA의 백엔드 역할을 하는 애플리케이션의 인증 레이어를 직접 구축한다면, Fortify(회원가입, 비밀번호 재설정 등)와 Sanctum(API 토큰 관리, 세션 인증)을 함께 사용하는 것이 일반적입니다.
설치
Composer로 Fortify를 설치합니다.
composer require laravel/fortify다음으로, vendor:publish 명령어로 Fortify의 리소스를 게시합니다.
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"이 명령어는 Fortify의 Action 클래스들을 app/Actions 디렉터리에 게시하고(없으면 자동 생성), FortifyServiceProvider, 설정 파일, 그리고 필요한 데이터베이스 마이그레이션 파일들도 함께 게시합니다.
이후 마이그레이션을 실행합니다.
php artisan migrateFortify 서비스 프로바이더
vendor:publish 명령어를 실행하면 App\Providers\FortifyServiceProvider 클래스도 함께 게시됩니다. 이 클래스가 config/app.php의 providers 배열에 등록되어 있는지 확인하세요.
Fortify 서비스 프로바이더는 게시된 Action 클래스들을 등록하고, 각 인증 작업이 실행될 때 해당 Action을 사용하도록 Fortify에 알려줍니다.
Fortify 기능 설정
fortify 설정 파일에는 features 배열이 있습니다. 이 배열에서 Fortify가 노출할 백엔드 라우트와 기능을 선택할 수 있습니다. Laravel Jetstream과 함께 사용하는 것이 아니라면, 대부분의 Laravel 애플리케이션에서 필요로 하는 기본 인증 기능만 활성화하는 것을 권장합니다.
'features' => [
Features::registration(),
Features::resetPasswords(),
Features::emailVerification(),
],뷰 비활성화
기본적으로 Fortify는 로그인 화면이나 회원가입 화면 같은 뷰를 반환하는 라우트를 등록합니다. 그러나 JavaScript 기반 SPA를 개발하는 경우에는 이러한 라우트가 필요 없을 수 있습니다. config/fortify.php에서 views 값을 false로 설정하면 뷰 라우트를 완전히 비활성화할 수 있습니다.
'views' => false,뷰 비활성화 시 비밀번호 재설정 주의사항
뷰를 비활성화하더라도 비밀번호 재설정 기능을 직접 구현할 경우, "비밀번호 재설정" 뷰를 반환하는 password.reset이라는 이름의 라우트는 반드시 정의해야 합니다. Laravel의 Illuminate\Auth\Notifications\ResetPassword 알림이 password.reset 네임드 라우트를 통해 비밀번호 재설정 URL을 생성하기 때문입니다.
인증
Fortify에 "로그인" 뷰를 반환하는 방법을 알려주는 것부터 시작합니다. Fortify는 UI가 없는 헤드리스 인증 라이브러리입니다. 이미 완성된 프론트엔드 인증 구현이 필요하다면 애플리케이션 스타터 킷을 사용하세요.
뷰 렌더링 로직은 Laravel\Fortify\Fortify 클래스의 메서드를 통해 커스터마이징할 수 있습니다. 보통 App\Providers\FortifyServiceProvider의 boot 메서드에서 호출합니다. Fortify는 이 뷰를 반환하는 /login 라우트를 자동으로 정의합니다.
use Laravel\Fortify\Fortify;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Fortify::loginView(function () {
return view('auth.login');
});
// ...
}로그인 템플릿에는 /login으로 POST 요청을 보내는 폼이 포함되어야 합니다. /login 엔드포인트는 문자열 email / username과 password를 받습니다. 이메일/사용자명 필드 이름은 config/fortify.php의 username 값과 일치해야 합니다. 또한 "로그인 상태 유지" 기능을 위해 boolean 타입의 remember 필드를 추가로 제공할 수 있습니다.
로그인에 성공하면 Fortify는 fortify 설정 파일의 home 옵션에 지정된 URI로 리다이렉트합니다. XHR 요청인 경우에는 200 HTTP 응답이 반환됩니다.
로그인에 실패하면 로그인 화면으로 다시 리다이렉트되며, 유효성 검사 오류는 Blade의 공유 변수 $errors를 통해 확인할 수 있습니다. XHR 요청인 경우에는 422 HTTP 응답과 함께 유효성 검사 오류가 반환됩니다.
사용자 인증 커스터마이징
Fortify는 기본적으로 제공된 자격 증명과 설정된 인증 가드를 기반으로 사용자를 조회하고 인증합니다. 그러나 로그인 자격 증명을 검증하고 사용자를 조회하는 방식을 완전히 직접 제어하고 싶을 때가 있습니다. 이럴 때는 Fortify::authenticateUsing 메서드를 사용하세요.
이 메서드는 수신된 HTTP 요청을 받는 클로저를 인자로 받습니다. 클로저는 요청의 로그인 자격 증명을 검증하고, 해당 사용자 인스턴스를 반환해야 합니다. 자격 증명이 유효하지 않거나 사용자를 찾을 수 없으면 null 또는 false를 반환하면 됩니다. 일반적으로 FortifyServiceProvider의 boot 메서드에서 호출합니다.
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Laravel\Fortify\Fortify;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)->first();
if ($user &&
Hash::check($request->password, $user->password)) {
return $user;
}
});
// ...
}인증 가드
fortify 설정 파일에서 Fortify가 사용할 인증 가드를 지정할 수 있습니다. 단, 설정된 가드는 반드시 Illuminate\Contracts\Auth\StatefulGuard의 구현체여야 합니다. SPA 인증에 Fortify를 사용하려면 Laravel의 기본 web 가드와 Laravel Sanctum을 함께 사용하세요.
인증 파이프라인 커스터마이징
Laravel Fortify는 로그인 요청을 인자로 호출 가능한 클래스들의 파이프라인을 통해 처리합니다. 필요에 따라 로그인 요청이 거쳐야 할 클래스들의 파이프라인을 직접 정의할 수 있습니다. 각 클래스는 수신된 Illuminate\Http\Request 인스턴스와 미들웨어처럼 다음 클래스로 요청을 전달하는 $next 변수를 받는 __invoke 메서드를 가져야 합니다.
커스텀 파이프라인을 정의하려면 Fortify::authenticateThrough 메서드를 사용합니다. 이 메서드는 로그인 요청이 통과할 클래스 배열을 반환하는 클로저를 받습니다. 보통 App\Providers\FortifyServiceProvider의 boot 메서드에서 호출합니다.
아래는 기본 파이프라인 정의이며, 커스터마이징의 출발점으로 활용할 수 있습니다.
use Laravel\Fortify\Actions\AttemptToAuthenticate;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
use Laravel\Fortify\Fortify;
use Illuminate\Http\Request;
Fortify::authenticateThrough(function (Request $request) {
return array_filter([
config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class,
Features::enabled(Features::twoFactorAuthentication()) ? RedirectIfTwoFactorAuthenticatable::class : null,
AttemptToAuthenticate::class,
PrepareAuthenticatedSession::class,
]);
});리다이렉트 커스터마이징
로그인에 성공하면 Fortify는 fortify 설정 파일의 home 옵션에 지정된 URI로 리다이렉트합니다. XHR 요청인 경우에는 200 HTTP 응답이 반환됩니다. 사용자가 로그아웃하면 / URI로 리다이렉트됩니다.
이 동작을 더 세밀하게 제어하려면 LoginResponse와 LogoutResponse 컨트랙트의 구현체를 Laravel 서비스 컨테이너에 바인딩하면 됩니다. 보통 App\Providers\FortifyServiceProvider의 register 메서드에서 처리합니다.
use Laravel\Fortify\Contracts\LogoutResponse;
/**
* 애플리케이션 서비스를 등록합니다.
*/
public function register(): void
{
$this->app->instance(LogoutResponse::class, new class implements LogoutResponse {
public function toResponse($request)
{
return redirect('/');
}
});
}2단계 인증
Fortify의 2단계 인증 기능을 활성화하면, 사용자는 인증 과정에서 6자리 숫자 토큰을 추가로 입력해야 합니다. 이 토큰은 TOTP(Time-based One-Time Password) 방식으로 생성되며, Google Authenticator 같은 TOTP 호환 모바일 인증 앱에서 확인할 수 있습니다.
시작하기 전에, App\Models\User 모델에 Laravel\Fortify\TwoFactorAuthenticatable 트레이트가 적용되어 있는지 확인하세요.
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable
{
use Notifiable, TwoFactorAuthenticatable;
}다음으로, 사용자가 2단계 인증 설정을 관리할 수 있는 화면을 만들어야 합니다. 이 화면에서는 2단계 인증 활성화/비활성화, 복구 코드 재생성 기능을 제공해야 합니다.
NOTE
기본적으로 fortify 설정 파일의 features 배열은 2단계 인증 설정 변경 시 비밀번호 확인을 요구하도록 되어 있습니다. 따라서 계속 진행하기 전에 Fortify의 비밀번호 확인 기능을 먼저 구현해야 합니다.
2단계 인증 활성화
2단계 인증을 활성화하려면, Fortify가 정의한 /user/two-factor-authentication 엔드포인트로 POST 요청을 보냅니다. 요청에 성공하면 이전 URL로 리다이렉트되며, status 세션 변수가 two-factor-authentication-enabled로 설정됩니다. 이 세션 변수를 템플릿에서 감지하여 적절한 성공 메시지를 표시할 수 있습니다. XHR 요청인 경우 200 HTTP 응답이 반환됩니다.
2단계 인증 활성화를 선택한 후에도, 사용자는 유효한 2단계 인증 코드를 입력하여 설정을 "확인"해야 합니다. 따라서 성공 메시지에서는 추가 확인이 필요함을 안내해야 합니다.
@if (session('status') == 'two-factor-authentication-enabled')
<div class="mb-4 font-medium text-sm">
아래에서 2단계 인증 설정을 완료해 주세요.
</div>
@endif다음으로, 사용자가 인증 앱으로 스캔할 QR 코드를 표시해야 합니다. Blade로 프론트엔드를 렌더링하는 경우, 사용자 인스턴스의 twoFactorQrCodeSvg 메서드를 사용하여 QR 코드 SVG를 가져올 수 있습니다.
$request->user()->twoFactorQrCodeSvg();JavaScript 기반 프론트엔드를 사용하는 경우, /user/two-factor-qr-code 엔드포인트로 XHR GET 요청을 보내면 svg 키를 포함하는 JSON 객체가 반환됩니다.
2단계 인증 확인
QR 코드를 표시하는 것 외에도, 사용자가 유효한 인증 코드를 입력하여 2단계 인증 설정을 "확인"할 수 있는 텍스트 입력 필드를 제공해야 합니다. 이 코드는 Fortify가 정의한 /user/confirmed-two-factor-authentication 엔드포인트로 POST 요청을 통해 전달해야 합니다.
요청에 성공하면 이전 URL로 리다이렉트되며, status 세션 변수가 two-factor-authentication-confirmed로 설정됩니다.
@if (session('status') == 'two-factor-authentication-confirmed')
<div class="mb-4 font-medium text-sm">
2단계 인증이 확인되어 성공적으로 활성화되었습니다.
</div>
@endifXHR 요청인 경우 200 HTTP 응답이 반환됩니다.
복구 코드 표시
사용자에게 2단계 인증 복구 코드도 함께 표시해야 합니다. 복구 코드는 모바일 기기를 분실했을 때 인증에 사용할 수 있습니다. Blade 프론트엔드라면 인증된 사용자 인스턴스를 통해 복구 코드를 가져올 수 있습니다.
(array) $request->user()->recoveryCodes()JavaScript 기반 프론트엔드라면 /user/two-factor-recovery-codes 엔드포인트로 XHR GET 요청을 보내면 복구 코드가 담긴 JSON 배열이 반환됩니다.
복구 코드를 재생성하려면 /user/two-factor-recovery-codes 엔드포인트로 POST 요청을 보내면 됩니다.
2단계 인증으로 로그인하기
인증 과정에서 Fortify는 자동으로 사용자를 2단계 인증 챌린지 화면으로 리다이렉트합니다. XHR 로그인 요청인 경우, 인증 성공 후 반환되는 JSON 응답에 two_factor boolean 속성이 포함됩니다. 이 값을 확인하여 2단계 인증 챌린지 화면으로 이동할지 결정하면 됩니다.
2단계 인증 기능 구현을 시작하려면, 챌린지 뷰를 반환하는 방법을 Fortify에 알려주어야 합니다. 보통 App\Providers\FortifyServiceProvider의 boot 메서드에서 설정합니다.
use Laravel\Fortify\Fortify;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Fortify::twoFactorChallengeView(function () {
return view('auth.two-factor-challenge');
});
// ...
}Fortify는 이 뷰를 반환하는 /two-factor-challenge 라우트를 자동으로 정의합니다. two-factor-challenge 템플릿에는 /two-factor-challenge 엔드포인트로 POST 요청을 보내는 폼이 포함되어야 합니다. 이 엔드포인트는 유효한 TOTP 토큰이 담긴 code 필드 또는 복구 코드가 담긴 recovery_code 필드를 받습니다.
로그인에 성공하면 Fortify는 fortify 설정 파일의 home 옵션에 지정된 URI로 리다이렉트합니다. XHR 요청인 경우 204 HTTP 응답이 반환됩니다.
요청이 실패하면 2단계 인증 챌린지 화면으로 다시 리다이렉트되며, 유효성