시작하기
비밀번호 재설정은 웹 애플리케이션에 빠질 수 없는 인증 플로우입니다. 스타터 킷을 사용하면 이 기능은 자동으로 구축되지만, API 전용 프로젝트나 자체 UI를 구축하는 프로젝트에서는 수동 구현이 필요합니다.
수동 구현이 필요한 경우:
API 전용 백엔드(프론트엔드가 SPA·모바일 앱)
스타터 킷을 사용하지 않고 인증 UI를 직접 구축하는 경우
메일 알림이나 재설정 URL의 디자인을 완전히 커스터마이즈하고 싶은 경우
스타터 킷(laravel new)으로 프로젝트를 생성한 경우, 비밀번호 재설정 기능은 이미 구현되어 있습니다. 이 페이지에서는 스타터 킷 없이 구현하는 방법을 설명합니다.
비밀번호 재설정의 전체 플로우는 다음과 같습니다.
비밀번호 재설정 설정은 config/auth.php의 passwords 키에서 관리합니다.
// config/auth.php
'passwords' => [
'users' => [
'driver' => 'database' ,
'provider' => 'users' ,
'table' => env ( 'AUTH_PASSWORD_RESET_TOKEN_TABLE' , 'password_reset_tokens' ),
'expire' => 60 ,
'throttle' => 60 ,
],
],
driver — 데이터 저장 방식(database 또는 cache)
expire — 토큰의 유효 기간(분). 기본값은 60분
throttle — 재전송을 제한할 때까지의 대기 시간(초)
드라이버
database 드라이버
기본 드라이버입니다. 비밀번호 재설정 토큰을 데이터베이스의 password_reset_tokens 테이블에 저장합니다. 이 테이블은 Laravel의 기본 마이그레이션(0001_01_01_000000_create_users_table.php)에 포함되어 있습니다.
'passwords' => [
'users' => [
'driver' => 'database' ,
'provider' => 'users' ,
'table' => env ( 'AUTH_PASSWORD_RESET_TOKEN_TABLE' , 'password_reset_tokens' ),
'expire' => 60 ,
'throttle' => 60 ,
],
],
cache 드라이버
Laravel 11 이상에서 사용 가능한 새로운 옵션입니다. 데이터베이스 테이블이 필요 없기 때문에 간단한 구성으로 비밀번호 재설정을 구현할 수 있습니다.
cache 드라이버는 캐시 스토어에 토큰을 저장합니다. password_reset_tokens 테이블 마이그레이션이 필요 없게 됩니다. 토큰은 사용자의 이메일 주소를 키로 저장되므로, 앱 내 다른 위치에서 이메일 주소를 캐시 키로 사용하지 않도록 주의하세요.
'passwords' => [
'users' => [
'driver' => 'cache' ,
'provider' => 'users' ,
'store' => 'passwords' , // 옵션: 전용 캐시 스토어
'expire' => 60 ,
'throttle' => 60 ,
],
],
store 키에 전용 캐시 스토어를 지정하면 php artisan cache:clear로 재설정 데이터가 지워지는 것을 방지할 수 있습니다. 지정하는 값은 config/cache.php에 설정된 스토어 이름에 대응시킵니다.
모델 준비
비밀번호 재설정 기능을 사용하려면 App\Models\User 모델에 두 개의 트레이트가 필요합니다.
<? php
namespace App\Models ;
use Illuminate\Auth\Passwords\ CanResetPassword ;
use Illuminate\Foundation\Auth\ User as Authenticatable ;
use Illuminate\Notifications\ Notifiable ;
class User extends Authenticatable
{
use Notifiable , CanResetPassword ;
// ...
}
Notifiable — 메일 알림을 전송하기 위해 필요
CanResetPassword — 비밀번호 재설정 토큰의 생성·검증에 필요한 메서드를 제공
Laravel의 기본 User 모델에는 이미 이 트레이트들이 포함되어 있습니다. 신규 설치의 경우 추가할 필요가 없습니다.
라우팅 구현
비밀번호 재설정에는 네 개의 라우트가 필요합니다.
1. 재설정 링크 요청 폼
이메일 주소를 입력하는 폼을 표시합니다.
// routes/web.php
Route :: get ( '/forgot-password' , function () {
return view ( 'auth.forgot-password' );
}) -> middleware ( 'guest' ) -> name ( 'password.request' );
해당하는 Blade 뷰:
{{-- resources/views/auth/forgot-password.blade.php --}}
< form method = "POST" action = "/forgot-password" >
@csrf
< div >
< label for = "email" > 이메일 주소 </ label >
< input id = "email" type = "email" name = "email" value = " {{ old ('email') }} " required autofocus >
@error ( 'email' )
< span > {{ $message }} </ span >
@enderror
</ div >
@if ( session ( 'status' ))
< div > {{ session ( 'status' ) }} </ div >
@endif
< button type = "submit" > 재설정 링크 전송 </ button >
</ form >
2. 재설정 링크 전송 처리
폼 제출을 받아 Password::sendResetLink()로 재설정 메일을 전송합니다.
use Illuminate\Http\ Request ;
use Illuminate\Support\Facades\ Password ;
Route :: post ( '/forgot-password' , function ( Request $request ) {
$request -> validate ([ 'email' => 'required|email' ]);
$status = Password :: sendResetLink (
$request -> only ( 'email' )
);
return $status === Password :: ResetLinkSent
? back () -> with ([ 'status' => __ ( $status )])
: back () -> withErrors ([ 'email' => __ ( $status )]);
}) -> middleware ( 'guest' ) -> name ( 'password.email' );
Password::sendResetLink()는 상태 문자열을 반환합니다.
상태 상수 설명 Password::ResetLinkSent재설정 링크를 전송함 Password::INVALID_USER지정한 이메일의 사용자를 찾을 수 없음 Password::RESET_THROTTLED전송이 제한됨(스로틀링)
3. 비밀번호 재설정 폼
메일 안의 링크를 클릭한 사용자에게 새 비밀번호를 입력하게 하는 폼을 표시합니다.
Route :: get ( '/reset-password/{token}' , function ( string $token ) {
return view ( 'auth.reset-password' , [ 'token' => $token ]);
}) -> middleware ( 'guest' ) -> name ( 'password.reset' );
해당하는 Blade 뷰:
{{-- resources/views/auth/reset-password.blade.php --}}
< form method = "POST" action = "/reset-password" >
@csrf
< input type = "hidden" name = "token" value = " {{ $token }} " >
< div >
< label for = "email" > 이메일 주소 </ label >
< input id = "email" type = "email" name = "email" value = " {{ old ('email') }} " required autofocus >
@error ( 'email' )
< span > {{ $message }} </ span >
@enderror
</ div >
< div >
< label for = "password" > 새 비밀번호 </ label >
< input id = "password" type = "password" name = "password" required >
@error ( 'password' )
< span > {{ $message }} </ span >
@enderror
</ div >
< div >
< label for = "password_confirmation" > 비밀번호 확인 </ label >
< input id = "password_confirmation" type = "password" name = "password_confirmation" required >
</ div >
< button type = "submit" > 비밀번호 재설정 </ button >
</ form >
4. 비밀번호 재설정 실행 처리
폼을 받아 Password::reset()으로 비밀번호를 갱신합니다.
use App\Models\ User ;
use Illuminate\Auth\Events\ PasswordReset ;
use Illuminate\Http\ Request ;
use Illuminate\Support\Facades\ Hash ;
use Illuminate\Support\Facades\ Password ;
use Illuminate\Support\ Str ;
Route :: post ( '/reset-password' , function ( Request $request ) {
$request -> validate ([
'token' => 'required' ,
'email' => 'required|email' ,
'password' => 'required|min:8|confirmed' ,
]);
$status = Password :: reset (
$request -> only ( 'email' , 'password' , 'password_confirmation' , 'token' ),
function ( User $user , string $password ) {
$user -> forceFill ([
'password' => Hash :: make ( $password ),
]) -> setRememberToken ( Str :: random ( 60 ));
$user -> save ();
event ( new PasswordReset ( $user ));
}
);
return $status === Password :: PasswordReset
? redirect () -> route ( 'login' ) -> with ( 'status' , __ ( $status ))
: back () -> withErrors ([ 'email' => [ __ ( $status )]]);
}) -> middleware ( 'guest' ) -> name ( 'password.update' );
Password::reset()의 상태 상수:
상태 상수 설명 Password::PasswordReset비밀번호 재설정 성공 Password::INVALID_TOKEN토큰이 무효이거나 만료됨 Password::INVALID_USER지정한 이메일의 사용자를 찾을 수 없음
토큰의 유효 기간
config/auth.php의 expire 옵션으로 토큰의 유효 기간을 분 단위로 설정할 수 있습니다. 기본값은 60분입니다.
'passwords' => [
'users' => [
'driver' => 'database' ,
'expire' => 60 , // 60분 후 만료
'throttle' => 60 ,
],
],
database 드라이버를 사용하는 경우, 만료된 토큰은 데이터베이스에 계속 남습니다. 정기적으로 정리하려면 다음 Artisan 명령을 사용합니다.
php artisan auth:clear-resets
스케줄러로 자동화하는 것을 권장합니다.
use Illuminate\Support\Facades\ Schedule ;
Schedule :: command ( 'auth:clear-resets' ) -> everyFifteenMinutes ();
커스터마이징
커스텀 알림 사용
비밀번호 재설정 메일을 커스터마이즈하려면 User 모델에서 sendPasswordResetNotification 메서드를 오버라이드합니다.
use App\Notifications\ ResetPasswordNotification ;
class User extends Authenticatable
{
use Notifiable , CanResetPassword ;
/**
* 비밀번호 재설정 알림 전송
*/
public function sendPasswordResetNotification ( $token ) : void
{
$url = 'https://example.com/reset-password?token=' . $token ;
$this -> notify ( new ResetPasswordNotification ( $url ));
}
}
재설정 링크 URL 커스터마이징
AppServiceProvider의 boot 메서드에서 ResetPassword::createUrlUsing()을 사용하면 재설정 링크 URL을 변경할 수 있습니다. SPA와 같이 다른 오리진의 프론트엔드로 리다이렉트하고 싶을 때 편리합니다.
use App\Models\ User ;
use Illuminate\Auth\Notifications\ ResetPassword ;
public function boot () : void
{
ResetPassword :: createUrlUsing ( function ( User $user , string $token ) {
return 'https://example.com/reset-password?token=' . $token ;
});
}
Trusted Hosts 설정
비밀번호 재설정 링크는 HTTP 요청의 Host 헤더를 사용해 생성됩니다. 부정한 호스트에서 오는 요청을 방지하기 위해 bootstrap/app.php에서 Trusted Hosts 설정을 권장합니다.
-> withMiddleware ( function ( Middleware $middleware ) {
$middleware -> trustHosts ( at : [ 'example.com' ]);
})
비밀번호 재설정 기능을 구현할 경우 Trusted Hosts 설정을 반드시 확인하세요. 설정이 미흡하면 호스트 헤더 인젝션 공격의 위험이 있습니다.
하고 싶은 일 방법 재설정 링크 전송 Password::sendResetLink(['email' => $email])비밀번호 재설정 Password::reset($credentials, $callback)테이블 없이 재설정 cache 드라이버 설정만료 토큰 삭제 php artisan auth:clear-resets메일 알림 커스터마이즈 sendPasswordResetNotification 오버라이드재설정 URL 커스터마이즈 ResetPassword::createUrlUsing()
다음 단계
인증 입문 Laravel의 인증 시스템 전체 구조를 학습합니다.
알림 메일 알림의 커스터마이징 방법을 자세히 학습합니다.