인증

번역일: 2026년 7월 2일

인증

소개

많은 웹 애플리케이션은 사용자가 로그인할 수 있는 방법을 제공합니다. Laravel은 이러한 인증 기능을 처음부터 직접 구현하지 않아도 되도록, 복잡한 인증 로직을 간단하게 처리할 수 있는 도구를 기본으로 제공합니다.

Laravel의 인증 시스템은 크게 두 가지 개념으로 이루어져 있습니다: GuardProvider입니다. Guard는 각 요청에서 사용자를 어떻게 인증할지를 정의합니다. 예를 들어, 기본 제공되는 session guard는 세션 스토리지와 쿠키를 사용해 사용자 상태를 유지합니다.

Provider는 영속적인 저장소(데이터베이스 등)에서 사용자를 어떻게 조회할지를 정의합니다. Laravel은 기본적으로 Eloquent와 데이터베이스 쿼리 빌더를 통한 사용자 조회를 지원합니다. 필요하다면 애플리케이션에 맞는 커스텀 Provider를 추가할 수도 있습니다.

애플리케이션의 인증 설정 파일은 config/auth.php에 있습니다. 각 옵션에 대한 설명이 잘 정리되어 있으니, 인증 동작을 커스터마이징할 때 참고하세요.

NOTE

Guard와 Provider는 "역할(role)" 및 "권한(permission)" 개념과 혼동하지 않도록 주의하세요. 권한을 통한 사용자 액션 제어가 필요하다면 인가(Authorization) 문서를 참고하세요.

스타터 킷

빠르게 시작하고 싶다면, Laravel 스타터 킷을 활용하는 것이 가장 쉬운 방법입니다. Laravel Breeze는 로그인, 회원가입, 비밀번호 재설정, 이메일 인증, 비밀번호 재확인 등 인증에 필요한 모든 기능을 포함한 심플한 스타터 킷입니다. Breeze의 뷰 레이어는 Tailwind CSS로 스타일링된 Blade 템플릿으로 구성됩니다. Inertia 기반의 스캐폴딩(Vue 또는 React)도 지원합니다.

더 풍부한 기능이 필요하다면 Laravel Jetstream을 고려해볼 수 있습니다. Jetstream은 Livewire 또는 Inertia와 Vue를 선택하여 사용할 수 있으며, 2단계 인증(2FA), 팀 관리, 세션 관리, API 토큰 관리(Laravel Sanctum 기반) 등 고급 기능을 추가로 제공합니다.

데이터베이스 고려사항

Laravel은 기본적으로 app/Models 디렉터리에 App\Models\User Eloquent 모델을 포함하고 있습니다. 이 모델은 기본 Eloquent 인증 드라이버와 함께 사용됩니다. Eloquent를 사용하지 않는다면, Laravel 쿼리 빌더를 활용하는 database 인증 프로바이더를 사용할 수 있습니다.

App\Models\User 모델의 데이터베이스 스키마를 구성할 때, 비밀번호 컬럼은 반드시 60자 이상이어야 합니다. 255자로 설정하는 것이 좋습니다. 기본 마이그레이션에 포함된 string 컬럼 기본값(255자)을 그대로 사용하면 문제없습니다.

또한, users 테이블에는 nullable한 100자 문자열 타입의 remember_token 컬럼이 있어야 합니다. 이 컬럼은 로그인 시 "로그인 상태 유지(remember me)" 옵션을 선택한 사용자의 토큰을 저장하는 데 사용됩니다. 기본 마이그레이션에 이미 이 컬럼이 포함되어 있습니다.

에코시스템 개요

Laravel은 인증과 관련된 여러 패키지를 제공합니다. 본격적으로 시작하기 전에, Laravel의 전반적인 인증 에코시스템을 살펴보고 각 패키지의 목적을 이해해봅시다.

웹 브라우저 기반 인증 (전통적인 방식)

전통적인 웹 애플리케이션에서는 로그인 폼을 통해 사용자 인증을 처리합니다. 사용자가 로그인에 성공하면 서버는 세션에 인증 정보를 저장하고, 이후 요청마다 세션 쿠키를 통해 사용자를 식별합니다.

Laravel은 이런 방식을 기본으로 지원합니다. 스타터 킷을 활용하면 이 모든 것을 빠르게 구성할 수 있습니다.

API 토큰 기반 인증

모바일 앱이나 서드파티 클라이언트를 위한 API를 구축할 때는 세션 대신 토큰 기반 인증을 사용합니다. 이 경우 클라이언트는 요청마다 API 토큰을 함께 전송하고, 서버는 토큰을 검증하여 사용자를 인증합니다. Laravel은 이를 위해 Laravel Sanctum을 제공합니다.

Laravel Passport

Laravel Passport는 완전한 OAuth2 서버 구현체입니다. 복잡한 OAuth2 인증 흐름이 필요한 경우에 사용합니다. 하지만 대부분의 애플리케이션은 OAuth2의 복잡성이 필요하지 않으며, 개발자와 사용자 모두에게 혼란을 줄 수 있습니다. 그래서 Laravel은 단순한 API 토큰 발급과 SPA(Single Page Application) 인증에 더 적합한 Sanctum을 권장합니다.

Laravel Sanctum

Laravel Sanctum은 웹 API 인증과 SPA 인증을 모두 처리할 수 있는 경량 패키지입니다.

Sanctum을 사용하는 애플리케이션에서 사용자가 로그인하면, Sanctum은 해당 사용자의 API 토큰을 데이터베이스에 저장합니다. 이후 API 요청 시 Authorization 헤더에 Bearer 토큰을 포함해 전송하면, Sanctum이 이 토큰을 검증하여 요청한 사용자를 식별합니다.

또한, Sanctum은 SPA와 Laravel 백엔드가 같은 도메인에 있을 경우 세션 쿠키 기반 인증도 지원합니다. 이 방식에서는 별도 토큰 없이 Laravel의 기본 세션 인증을 활용합니다.

이 문서에서는 Laravel의 기본 인증 기능, 스타터 킷, 그리고 Sanctum을 주로 다룹니다. Passport가 필요한 경우 Passport 문서를 별도로 참고하세요.

NOTE

어떤 방식을 선택해야 할지 고민된다면, 대부분의 경우 Laravel Sanctum을 선택하는 것이 좋습니다. Sanctum은 API 토큰 인증과 SPA 인증을 모두 지원하는 "모든 것을 포함하는" 솔루션입니다.

인증 빠른 시작

스타터 킷 설치

새 Laravel 프로젝트에서 인증을 설정하는 가장 빠른 방법은 스타터 킷을 사용하는 것입니다. Laravel BreezeLaravel Jetstream은 인증 기능이 내장된 스캐폴딩을 제공합니다.

Laravel Breeze는 로그인, 회원가입, 비밀번호 재설정, 이메일 인증, 비밀번호 재확인 등 인증의 모든 기능을 최소한의 코드로 구현한 패키지입니다. Blade 템플릿과 Tailwind CSS 기반의 뷰를 생성해주므로, 스타일과 구조를 자유롭게 커스터마이징할 수 있습니다.

또한 Breeze는 Inertia.js를 활용한 Vue, React 기반의 스캐폴딩과 Livewire 기반 스캐폴딩도 지원합니다.

Jetstream은 Breeze보다 더 강력한 기능을 제공하며, Livewire 또는 Inertia.js와 Vue를 사용하는 방식을 선택할 수 있습니다.

스타터 킷을 선택하는 데 도움이 필요하다면 스타터 킷 문서를 참고하세요.

인증된 사용자 조회

스타터 킷을 설치하여 사용자들이 회원가입하고 로그인할 수 있게 되었다면, 이제 현재 인증된 사용자와 상호작용하는 방법을 알아봅시다. 요청을 처리하는 도중에 Auth 파사드의 user 메서드를 통해 현재 인증된 사용자를 조회할 수 있습니다.

use Illuminate\Support\Facades\Auth; // 현재 인증된 사용자 조회 $user = Auth::user(); // 현재 인증된 사용자의 ID 조회 $id = Auth::id();

또는, 사용자가 이미 인증된 상태라면 Illuminate\Http\Request 인스턴스를 통해서도 인증된 사용자에 접근할 수 있습니다. 컨트롤러 메서드에 타입 힌트를 사용하면 Laravel이 자동으로 의존성을 주입합니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class FlightController extends Controller { /** * 사용 가능한 항공편 목록을 업데이트합니다. */ public function update(Request $request): RedirectResponse { $user = $request->user(); // ... } }

현재 사용자의 인증 여부 확인

현재 요청을 보낸 사용자가 인증된 상태인지 확인하려면 Auth 파사드의 check 메서드를 사용합니다. 사용자가 인증되어 있으면 true를 반환합니다.

use Illuminate\Support\Facades\Auth; if (Auth::check()) { // 사용자가 로그인된 상태입니다. }

NOTE

check 메서드로 인증 여부를 확인할 수도 있지만, 일반적으로는 미들웨어를 사용하여 특정 라우트나 컨트롤러에 접근하기 전에 인증 여부를 검사하는 것이 더 실용적입니다. 자세한 내용은 라우트 보호 문서를 참고하세요.

라우트 보호

라우트 미들웨어를 사용하면 인증된 사용자만 특정 라우트에 접근할 수 있도록 제한할 수 있습니다. Laravel은 Illuminate\Auth\Middleware\Authenticate 클래스를 가리키는 auth 미들웨어 별칭을 기본으로 제공합니다.

Route::get('/flights', function () { // 인증된 사용자만 이 라우트에 접근할 수 있습니다. })->middleware('auth');

미인증 사용자 리다이렉트

auth 미들웨어가 인증되지 않은 사용자를 감지하면, 해당 사용자를 login 이름이 지정된 라우트로 리다이렉트합니다. 이 동작은 애플리케이션의 bootstrap/app.php 파일에서 redirectGuestsTo 메서드를 사용해 변경할 수 있습니다.

use Illuminate\Http\Request; ->withMiddleware(function (Middleware $middleware) { $middleware->redirectGuestsTo('/login'); // 클로저를 사용하는 경우 $middleware->redirectGuestsTo(fn (Request $request) => route('login')); })

Guard 지정

auth 미들웨어를 라우트에 적용할 때, 어떤 Guard를 사용해 사용자를 인증할지 지정할 수 있습니다. 지정한 Guard는 config/auth.php 파일의 guards 배열에 정의된 키 중 하나여야 합니다.

Route::get('/flights', function () { // admin guard로 인증된 사용자만 접근 가능합니다. })->middleware('auth:admin');

로그인 제한

Laravel Breeze 또는 Jetstream 스타터 킷을 사용하는 경우, 로그인 시도에 자동으로 속도 제한이 적용됩니다. 일정 횟수 이상 로그인에 실패하면 잠시 동안 재시도가 제한됩니다. 이 제한은 사용자의 사용자명(이메일)과 IP 주소를 조합해 적용됩니다.

NOTE

애플리케이션의 다른 라우트에도 속도 제한이 필요하다면 속도 제한 문서를 참고하세요.

수동으로 사용자 인증하기

Laravel의 스타터 킷가 제공하는 인증 스캐폴딩을 반드시 사용할 필요는 없습니다. 스캐폴딩 없이 직접 인증 로직을 구현하려면 Laravel의 인증 클래스를 직접 사용하면 됩니다.

Auth 파사드를 import한 뒤, attempt 메서드를 사용하겠습니다. 보통 이 메서드는 로그인 폼을 처리하는 컨트롤러에서 호출합니다. attempt 메서드에는 인증에 사용할 자격증명(credentials)을 배열로 전달합니다. 이 배열의 값을 기반으로 데이터베이스에서 사용자를 조회합니다. 예를 들어 아래 코드에서는 email 컬럼 값으로 사용자를 찾습니다.

사용자를 찾으면 데이터베이스에 저장된 해시된 비밀번호와 전달된 password 값을 비교합니다. 이때 password 값은 직접 해싱할 필요가 없습니다. Laravel이 자동으로 해싱 처리합니다. 비밀번호가 일치하면 해당 사용자의 인증 세션이 시작됩니다.

attempt 메서드는 인증 성공 시 true, 실패 시 false를 반환합니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /** * 로그인 시도를 처리합니다. */ public function authenticate(Request $request): RedirectResponse { $credentials = $request->validate([ 'email' => ['required', 'email'], 'password' => ['required'], ]); if (Auth::attempt($credentials)) { $request->session()->regenerate(); return redirect()->intended('dashboard'); } return back()->withErrors([ 'email' => '입력하신 정보와 일치하는 계정이 없습니다.', ])->onlyInput('email'); } }

NOTE

이 예시에서 email은 반드시 사용해야 하는 필드명이 아닙니다. 데이터베이스의 실제 컬럼명과 일치하면 어떤 이름이든 사용할 수 있습니다.

intended 메서드는 인증 미들웨어에 의해 막히기 전에 사용자가 접근하려 했던 URL로 리다이렉트합니다. 해당 URL이 없는 경우를 대비해 대체 URL을 인수로 지정할 수 있습니다.

추가 조건 지정

이메일과 비밀번호 외에 추가 조건을 인증에 포함시킬 수도 있습니다. attempt 메서드의 배열에 조건을 추가하면 됩니다. 예를 들어, 사용자가 "활성(active)" 상태인지도 확인하려면 다음과 같이 작성합니다.

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // 인증 성공 }

복잡한 쿼리 조건이 필요한 경우 배열에 클로저를 포함할 수 있습니다. 이 클로저는 쿼리 인스턴스를 인수로 받으므로 필요에 따라 쿼리를 자유롭게 수정할 수 있습니다.

use Illuminate\Database\Eloquent\Builder; if (Auth::attempt([ 'email' => $email, 'password' => $password, fn (Builder $query) => $query->has('activeSubscription'), ])) { // 인증 성공 }

WARNING

위 예시에서 password는 쿼리 필터 조건이 아닙니다. Laravel이 자동으로 처리합니다. password를 배열에 포함한다고 해서 데이터베이스의 password 컬럼에 직접 매칭되는 것이 아님에 주의하세요.

NOTE

attempt 메서드에 전달되는 배열에서 password를 제외한 나머지 키-값 쌍은 모두 데이터베이스 쿼리 조건으로 사용됩니다. 따라서 위 예시에서는 email 컬럼 값과 active 컬럼 값이 모두 일치하는 사용자를 데이터베이스에서 조회합니다.

특정 Guard 인스턴스 사용

Auth 파사드의 guard 메서드를 사용하면 인증에 사용할 Guard를 명시적으로 지정할 수 있습니다. 이를 통해 서로 다른 인증 모델이나 사용자 테이블을 사용하는 애플리케이션의 다양한 영역을 독립적으로 관리할 수 있습니다.

guard 메서드에 전달하는 Guard 이름은 config/auth.php 파일에 정의된 Guard 중 하나여야 합니다.

if (Auth::guard('admin')->attempt($credentials)) { // admin guard로 인증 성공 }

사용자 기억하기

많은 웹 애플리케이션은 로그인 폼에 "로그인 상태 유지(remember me)" 체크박스를 제공합니다. 이 기능을 구현하려면 attempt 메서드의 두 번째 인수로 불리언 값을 전달합니다.

값이 true이면 사용자가 직접 로그아웃할 때까지 인증 상태가 무기한 유지됩니다. users 테이블에 remember_token 컬럼이 있어야 하며, 이 컬럼에 "로그인 상태 유지" 토큰이 저장됩니다. 새 Laravel 애플리케이션의 기본 마이그레이션에 이미 이 컬럼이 포함되어 있습니다.

use Illuminate\Support\Facades\Auth; if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // 사용자를 기억합니다. }

애플리케이션에서 "로그인 상태 유지" 기능을 지원한다면, viaRemember 메서드를 통해 현재 사용자가 "remember me" 쿠키로 인증되었는지 확인할 수 있습니다.

use Illuminate\Support\Facades\Auth; if (Auth::viaRemember()) { // 사용자가 "로그인 상태 유지" 기능으로 인증되었습니다. }

기타 인증 방법

사용자 인스턴스로 인증

이미 조회한 사용자 인스턴스를 현재 인증된 사용자로 설정하려면 Auth 파사드의 login 메서드에 해당 인스턴스를 전달합니다. 전달되는 인스턴스는 Illuminate\Contracts\Auth\Authenticatable 계약(Contract)을 구현해야 합니다. Laravel의 App\Models\User 모델은 이미 이 인터페이스를 구현하고 있습니다. 이 방법은 회원가입 직후 사용자를 자동으로 로그인시킬 때 유용합니다.

use Illuminate\Support\Facades\Auth; Auth::login($user);

login 메서드의 두 번째 인수로 불리언 값을 전달하면 "로그인 상태 유지" 여부를 지정할 수 있습니다.

Auth::login($user, $remember = true);

필요한 경우, Guard를 먼저 지정한 뒤 login 메서드를 사용할 수 있습니다.

Auth::guard('admin')->login($user);

ID로 사용자 인증

데이터베이스 레코드의 기본 키(primary key)를 사용해 사용자를 인증하려면 loginUsingId 메서드를 사용합니다.

Auth::loginUsingId(1);

이 메서드의 두 번째 인수로 "로그인 상태 유지" 여부를 지정할 수 있습니다.

Auth::loginUsingId(1, remember: true);

단일 요청 인증

once 메서드를 사용하면 세션이나 쿠키 없이 단 하나의 요청에 대해서만 사용자를 인증할 수 있습니다. 세션과 쿠키를 전혀 활용하지 않으므로, Stateless API를 구현할 때 유용합니다.

if (Auth::once($credentials)) { // 이 요청 동안만 인증됩니다. }

HTTP Basic 인증

HTTP Basic 인증은 별도의 로그인 페이지 없이 사용자를 빠르게 인증하는 방법입니다. auth.basic 미들웨어를 라우트에 추가하기만 하면 바로 사용할 수 있습니다. auth.basic 미들웨어는 Laravel에 기본 포함되어 있으므로 별도로 정의하지 않아도 됩니다.

Route::get('/profile', function () { // 인증된 사용자만 접근 가능합니다. })->middleware('auth.basic');

미들웨어를 라우트에 추가하면, 브라우저에서 해당 라우트에 접근할 때 자동으로 인증 자격증명을 입력하는 프롬프트가 표시됩니다. 기본적으로 auth.basic 미들웨어는 users 테이블의 email 컬럼을 사용자명으로 사용합니다.

FastCGI 사용 시 주의사항

PHP FastCGI와 Apache를 사용하는 경우 HTTP Basic 인증이 정상적으로 동작하지 않을 수 있습니다. 이 경우 애플리케이션의 .htaccess 파일에 다음 내용을 추가하세요.

RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

Stateless HTTP Basic 인증

세션에 사용자 식별 쿠키를 저장하지 않고 HTTP Basic 인증을 사용할 수도 있습니다. 이 방식은 API 요청에서 HTTP Basic 인증을 사용할 때 유용합니다. 먼저 onceBasic 메서드를 호출하는 미들웨어를 정의합니다. onceBasic 메서드가 응답을 반환하지 않으면 다음 미들웨어로 요청을 전달합니다.

<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class AuthenticateOnceWithBasicAuth { /** * 들어오는 요청을 처리합니다. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { return Auth::onceBasic() ?: $next($request); } }

그런 다음 이 미들웨어를 라우트에 적용합니다.

Route::get('/api/user', function () { // 인증된 사용자만 접근 가능합니다. })->middleware(AuthenticateOnceWithBasicAuth::class);

로그아웃

사용자를 수동으로 로그아웃시키려면 Auth 파사드의 logout 메서드를 사용합니다. 이 메서드를 호출하면 사용자의 세션에서 인증 정보가 제거됩니다.

로그아웃 처리 후에는 세션 토큰을 무효화하고, CSRF 토큰을 재생성하는 것이 보안상 중요합니다. 로그아웃 후에는 일반적으로 애플리케이션의 홈 또는 로그인 페이지로 리다이렉트합니다.

use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; /** * 사용자를 로그아웃합니다. */ public function logout(Request $request): RedirectResponse { Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect('/'); }

다른 기기의 세션 무효화

Laravel은 현재 기기의 세션을 유지하면서 다른 기기에서의 세션을 무효화하는 기능도 제공합니다. 비밀번호를 변경하거나 보안을 강화할 때 다른 기기에서의 로그인 세션을 모두 만료시킬 때 유용합니다.

이 기능을 사용하려면 애플리케이션의 bootstrap/app.php 파일에서 AuthenticateSession 미들웨어가 적용된 라우트에 해당 기능이 활성화되어 있는지 확인하세요.

use Illuminate\Session\Middleware\AuthenticateSession; ->withMiddleware(function (Middleware $middleware) { $middleware->authenticateSessions(); })

그런 다음 Auth 파사드의 logoutOtherDevices 메서드를 호출합니다. 이 메서드는 현재 비밀번호를 인수로 받아 검증합니다.

use Illuminate\Support\Facades\Auth; Auth::logoutOtherDevices($currentPassword);

logoutOtherDevices 메서드를 호출하면 다른 기기의 세션이 모두 무효화되어 이전에 인증된 Guard에서 완전히 로그아웃됩니다.

비밀번호 재확인

애플리케이션을 개발하다 보면 특정 액션을 수행하거나 민감한 영역에 접근하기 전에 사용자의 비밀번호를 다시 한번 확인해야 하는 경우가 있습니다. Laravel은 이를 위한 내장 미들웨어를 제공하여 간단하게 구현할 수 있습니다.

이 기능을 구현하려면 두 개의 라우트가 필요합니다: 하나는 비밀번호 재확인 뷰를 표시하는 라우트, 다른 하나는 비밀번호를 검증하고 사용자를 목적지로 리다이렉트하는 라우트입니다.

NOTE

아래 문서에서는 비밀번호 재확인 기능을 직접 통합하는 방법을 설명합니다. 더 빠르게 시작하고 싶다면 Laravel 스타터 킷이 이 기능을 자동으로 지원합니다.

설정

비밀번호 재확인 후에는 기본적으로 3시간 동안 비밀번호를 다시 묻지 않습니다. 이 시간은 config/auth.php 파일의 password_timeout 값으로 변경할 수 있습니다.

라우팅

비밀번호 재확인 폼

먼저, 비밀번호 재확인 뷰를 표시하는 라우트를 정의합니다.

Route::get('/confirm-password', function () { return view('auth.confirm-password'); })->middleware('auth')->name('password.confirm');

이 뷰에는 password 필드를 포함한 폼이 있어야 합니다. 또한 보호된 영역에 접근하려면 비밀번호를 재확인해야 한다는 안내 문구를 추가하는 것이 좋습니다.

비밀번호 검증

다음으로, 비밀번호 재확인 폼의 제출을 처리하는 라우트를 정의합니다. 이 라우트에서 비밀번호를 검증하고, 사용자를 원래 목적지로 리다이렉트합니다.

use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Redirect; Route::post('/confirm-password', function (Request $request) { if (! Hash::check($request->password, $request->user()->password)) { return back()->withErrors([ 'password' => ['입력하신 비밀번호가 일치하지 않습니다.'], ]); } $request->session()->passwordConfirmed(); return redirect()->intended(); })->middleware(['auth', 'throttle:6,1']);

passwordConfirmed 메서드는 세션에 비밀번호 확인 시각을 기록합니다. 이 정보를 기반으로 Laravel은 사용자가 마지막으로 비밀번호를 확인한 시각을 추적합니다.

라우트 보호

비밀번호를 재확인한 사용자만 접근할 수 있는 라우트에는 password.confirm 미들웨어를 적용하세요. 이 미들웨어는 Laravel 기본 설치에 포함되어 있으며, 미들웨어를 통과하기 전에 사용자의 비밀번호 재확인 시각을 세션에 저장합니다. 재확인 후 일정 시간이 지나면 다시 비밀번호를 묻습니다.

Route::get('/settings', function () { // ... })->middleware(['password.confirm']); Route::post('/settings', function () { // ... })->middleware(['password.confirm']);

커스텀 Guard 추가

Auth 파사드의 extend 메서드를 사용하면 직접 인증 Guard를 정의할 수 있습니다. 이 메서드 호출은 서비스 프로바이더 내에서 해야 합니다. Laravel에는 이미 AppServiceProvider가 있으므로, 해당 파일에 코드를 추가하면 됩니다.

<?php namespace App\Providers; use App\Services\Auth\JwtGuard; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { // ... /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::extend('jwt', function (Application $app, string $name, array $config) { // Illuminate\Contracts\Auth\Guard 인스턴스를 반환합니다. return new JwtGuard(Auth::createUserProvider($config['provider'])); }); } }

위 예시처럼 커스텀 Guard를 등록한 뒤, config/auth.php 파일에서 해당 Guard를 설정합니다.

'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],

클로저 요청 Guard

HTTP 요청 기반의 커스텀 인증 시스템을 가장 간단하게 구현하는 방법은 Auth::viaRequest 메서드를 사용하는 것입니다. 이 메서드를 사용하면 클로저 하나만으로 인증 로직을 정의할 수 있습니다.

AppServiceProviderboot 메서드에서 Auth::viaRequest 메서드를 호출하세요. 첫 번째 인수는 Guard 이름(임의의 문자열), 두 번째 인수는 인증 로직을 처리하는 클로저입니다. 클로저는 HTTP 요청을 인수로 받고, 인증 성공 시 사용자 인스턴스를, 실패 시 null을 반환해야 합니다.

use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::viaRequest('custom-token', function (Request $request) { return User::where('token', (string) $request->token)->first(); }); }

커스텀 Guard를 정의했으면 config/auth.php 파일의 guards 설정에 추가합니다.

'guards' => [ 'api' => [ 'driver' => 'custom-token', ], ],

마지막으로, 인증 미들웨어를 라우트에 적용할 때 해당 Guard를 지정합니다.

Route::middleware('auth:api')->group(function () { // ... });

커스텀 사용자 프로바이더 추가

사용자를 관계형 데이터베이스가 아닌 다른 저장소(예: Redis, MongoDB, 외부 API 등)에 저장하는 경우, 커스텀 사용자 프로바이더를 구현해야 합니다. Auth 파사드의 provider 메서드를 사용하여 커스텀 프로바이더를 등록합니다. 등록 코드는 서비스 프로바이더 내에 작성하세요.

<?php namespace App\Providers; use App\Extensions\MongoUserProvider; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { // ... /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::provider('mongo', function (Application $app, array $config) { // Illuminate\Contracts\Auth\UserProvider 인스턴스를 반환합니다. return new MongoUserProvider($app->make('mongo.connection')); }); } }

프로바이더를 등록했으면 config/auth.php 파일에서 새 프로바이더를 설정합니다.

'providers' => [ 'users' => [ 'driver' => 'mongo', ], ],

그리고 이 프로바이더를 guards 설정에서 참조합니다.

'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ],

User Provider 계약

Illuminate\Contracts\Auth\UserProvider 구현체는 영속적인 저장소(MySQL, Redis 등)에서 Illuminate\Contracts\Auth\Authenticatable 구현체를 조회하는 역할을 합니다. 이 두 인터페이스 덕분에 Laravel의 인증 시스템은 사용자 데이터의 저장 방식이나 사용자 클래스 종류에 관계없이 동작할 수 있습니다.

Illuminate\Contracts\Auth\UserProvider 계약을 살펴봅시다.

<?php namespace Illuminate\Contracts\Auth; interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false); }
  • retrieveById: 사용자를 고유 식별자(주로 MySQL의 auto-increment ID)로 조회합니다.
  • retrieveByToken: $identifier와 "로그인 상태 유지" $token으로 사용자를 조회합니다. 토큰은 remember_token 컬럼에 저장됩니다. 일치하는 사용자를 반환하거나, 없으면 null을 반환합니다.
  • updateRememberToken: $userremember_token을 새 $token으로 업데이트합니다. 로그인 성공("로그인 상태 유지" 선택 시) 또는 로그아웃 시 토큰이 교체됩니다.
  • retrieveByCredentials: attempt 메서드에 전달된 자격증명 배열로 사용자를 조회합니다. password 키는 비밀번호 검증에 사용되므로 이 단계에서는 쿼리 조건에 포함하지 않아야 합니다.
  • validateCredentials: 주어진 $user$credentials를 비교하여 비밀번호를 검증합니다. 주로 Hash::check를 사용해 $user->getAuthPassword()$credentials['password']를 비교합니다. 비밀번호가 일치하면 true, 아니면 false를 반환합니다.
  • rehashPasswordIfRequired: 필요한 경우 사용자의 비밀번호를 재해싱합니다.

Authenticatable 계약

UserProvider의 각 메서드를 살펴봤으니, 이번에는 Authenticatable 계약을 살펴봅시다. 프로바이더의 retrieveById, retrieveByToken, retrieveByCredentials 메서드는 이 인터페이스의 구현체를 반환해야 합니다.

<?php namespace Illuminate\Contracts\Auth; interface Authenticatable { public function getAuthIdentifierName(); public function getAuthIdentifier(); public function getAuthPasswordName(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); }

이 인터페이스는 간단합니다. 각 메서드의 역할은 다음과 같습니다.

  • getAuthIdentifierName: 사용자의 "기본 키" 컬럼명을 반환합니다.
  • getAuthIdentifier: 사용자의 "기본 키" 값을 반환합니다. MySQL 기반 저장소를 사용한다면 auto-increment ID 값에 해당합니다.
  • getAuthPasswordName: 비밀번호 필드 이름을 반환합니다.
  • getAuthPassword: 해시된 비밀번호를 반환합니다.
  • getRememberToken / setRememberToken: "로그인 상태 유지" 토큰을 가져오거나 설정합니다.
  • getRememberTokenName: remember_token 컬럼명을 반환합니다.

이 인터페이스를 통해 인증 시스템은 어떤 ORM이나 저장소 레이어를 사용하더라도 일관되게 동작할 수 있습니다. Laravel의 App\Models\User 클래스는 app/Models 디렉터리에 위치하며, 이미 이 인터페이스를 구현하고 있습니다.

자동 비밀번호 재해싱

Laravel의 기본 비밀번호 해싱 알고리즘은 bcrypt입니다. bcrypt의 "work factor(작업 계수)"는 애플리케이션의 config/hashing.php 또는 BCRYPT_ROUNDS 환경 변수로 조정할 수 있습니다.

컴퓨팅 성능이 향상됨에 따라 bcrypt의 work factor를 점차 높이는 것이 일반적입니다. 설정값을 높인 경우, Laravel은 스타터 킷 사용 시 또는 attempt 메서드를 통해 사용자가 인증할 때 비밀번호를 자동으로 재해싱합니다.

일반적으로 자동 비밀번호 재해싱은 애플리케이션에 아무런 영향을 주지 않습니다. 그러나 이 동작을 비활성화하려면 hashing 설정에서 rehash_on_login 옵션을 false로 설정하면 됩니다.

'rehash_on_login' => false,

이벤트

Laravel은 인증 과정에서 다양한 이벤트를 디스패치합니다. 다음 이벤트에 대해 리스너를 직접 정의할 수 있습니다.

이벤트 이름
Illuminate\Auth\Events\Registered
Illuminate\Auth\Events\Attempting
Illuminate\Auth\Events\Authenticated
Illuminate\Auth\Events\Login
Illuminate\Auth\Events\Failed
Illuminate\Auth\Events\Validated
Illuminate\Auth\Events\Verified
Illuminate\Auth\Events\Logout
Illuminate\Auth\Events\CurrentDeviceLogout
Illuminate\Auth\Events\OtherDeviceLogout
Illuminate\Auth\Events\Lockout
Illuminate\Auth\Events\PasswordReset

인증

소개

대부분의 웹 애플리케이션은 사용자가 로그인할 수 있는 인증 기능을 제공합니다. 이 기능을 올바르게 구현하는 것은 생각보다 복잡하고 보안 위험을 수반하기 때문에, Laravel은 인증을 빠르고 안전하게 구현할 수 있는 도구를 기본으로 제공합니다.

Laravel 인증의 핵심은 가드(guard)프로바이더(provider) 로 이루어져 있습니다.

  • 가드: 각 요청마다 사용자를 어떻게 인증할지 정의합니다. 기본 제공되는 session 가드는 세션 스토리지와 쿠키를 사용해 인증 상태를 유지합니다.
  • 프로바이더: 영속적인 저장소에서 사용자를 어떻게 조회할지 정의합니다. 기본적으로 Eloquent와 데이터베이스 쿼리 빌더를 지원하며, 필요에 따라 커스텀 프로바이더를 추가할 수 있습니다.

인증 관련 설정은 config/auth.php에 있으며, 각 옵션에 대한 상세한 주석이 함께 제공됩니다.

NOTE

가드와 프로바이더는 "역할(role)"과 "권한(permission)"과는 다른 개념입니다. 권한 기반의 사용자 행동 제어는 인가(authorization) 문서를 참고하세요.

스타터 킷

빠르게 시작하고 싶다면, 새 Laravel 프로젝트에 애플리케이션 스타터 킷을 설치하세요. 데이터베이스 마이그레이션 후 브라우저에서 /register 또는 애플리케이션에 설정된 URL로 이동하면, 스타터 킷이 전체 인증 시스템을 자동으로 구성해 줍니다.

최종 프로젝트에 스타터 킷을 사용하지 않더라도, 일단 설치해보는 것만으로도 Laravel 인증 기능이 실제로 어떻게 구현되는지 학습하는 좋은 기회가 됩니다. 스타터 킷에는 인증용 컨트롤러, 라우트, 뷰가 모두 포함되어 있으므로, 해당 코드를 살펴보며 인증 흐름을 파악할 수 있습니다.

데이터베이스 고려사항

Laravel은 기본적으로 app/Models 디렉터리에 App\Models\User Eloquent 모델을 포함합니다. 이 모델은 기본 Eloquent 인증 드라이버와 함께 사용됩니다.

Eloquent를 사용하지 않는다면, Laravel 쿼리 빌더 기반의 database 인증 프로바이더를 사용할 수 있습니다. MongoDB를 사용하는 경우에는 MongoDB 공식 Laravel 사용자 인증 문서를 참고하세요.

App\Models\User 모델의 데이터베이스 스키마를 설계할 때는 다음 사항을 확인하세요.

  • password 컬럼은 최소 60자 이상의 길이를 가져야 합니다. 새 Laravel 프로젝트에 기본 포함된 users 테이블 마이그레이션은 이미 충분한 길이로 설정되어 있습니다.
  • users 테이블(또는 이에 상응하는 테이블)에는 nullable 문자열 타입의 remember_token 컬럼(100자)이 있어야 합니다. 이 컬럼은 로그인 시 "로그인 상태 유지" 옵션을 선택한 사용자의 토큰을 저장하는 데 사용됩니다. 역시 기본 마이그레이션에 이미 포함되어 있습니다.

생태계 개요

Laravel은 인증과 관련된 여러 패키지를 제공합니다. 본격적으로 시작하기 전에, 전체적인 인증 생태계와 각 패키지의 목적을 살펴보겠습니다.

브라우저 기반 인증의 흐름

웹 브라우저를 통한 인증을 예로 들면, 사용자가 로그인 폼에 아이디와 비밀번호를 입력합니다. 자격 증명이 올바르면 애플리케이션은 인증된 사용자 정보를 세션에 저장합니다. 브라우저에는 세션 ID가 담긴 쿠키가 발급되어, 이후 요청마다 해당 세션과 사용자를 연결합니다. 이 세션 쿠키를 받으면 애플리케이션은 세션 데이터를 조회하고, 저장된 인증 정보를 확인하여 사용자를 "인증됨" 상태로 처리합니다.

API 기반 인증의 흐름

외부 서비스가 API를 통해 접근할 때는 브라우저가 없으므로 쿠키 방식이 일반적으로 적합하지 않습니다. 대신 각 요청마다 API 토큰을 함께 전송합니다. 애플리케이션은 수신된 토큰을 유효한 토큰 테이블과 비교하여 요청을 인증합니다.

Laravel 내장 브라우저 인증 서비스

Laravel은 AuthSession 파사드를 통해 접근할 수 있는 내장 인증 및 세션 서비스를 제공합니다. 이 기능들은 웹 브라우저에서 시작되는 요청에 대해 쿠키 기반 인증을 제공하며, 사용자 자격 증명 검증, 인증 처리, 세션 데이터 저장, 세션 쿠키 발급 등을 자동으로 처리합니다.

애플리케이션 스타터 킷

이 인증 서비스들을 직접 구현할 수도 있지만, 더 빠르게 시작하고 싶다면 전체 인증 레이어를 현대적으로 구성해 주는 무료 스타터 킷을 활용하세요.

Laravel API 인증 서비스

Laravel은 API 토큰 관리 및 인증을 위한 두 가지 선택적 패키지를 제공합니다: PassportSanctum입니다. 이 패키지들은 내장 쿠키 기반 인증과 상호 배타적이지 않습니다. API 토큰 인증에 특화된 이 패키지들과, 쿠키 기반 브라우저 인증을 함께 사용하는 애플리케이션도 많습니다.

Passport

Passport는 OAuth2 인증 프로바이더로, 다양한 OAuth2 "그랜트 타입"을 통해 여러 종류의 토큰을 발급할 수 있습니다. 강력하고 완성도 높은 패키지이지만, OAuth2 스펙 자체가 복잡하여 대부분의 애플리케이션에서는 그 모든 기능이 필요하지 않습니다. SPA나 모바일 앱 인증에 OAuth2를 어떻게 적용해야 하는지 혼란스러워하는 개발자도 많습니다.

Sanctum

OAuth2의 복잡성을 해소하기 위해 만들어진 패키지가 Laravel Sanctum입니다. Sanctum은 웹 브라우저로부터의 퍼스트파티 요청과 토큰 기반 API 요청을 모두 처리할 수 있는 하이브리드 인증 패키지입니다.

Sanctum 기반 애플리케이션이 요청을 수신하면, 먼저 인증된 세션을 참조하는 세션 쿠키가 있는지 확인합니다. 세션 쿠키가 없다면 API 토큰이 포함되어 있는지 검사하고, 토큰이 있으면 해당 토큰으로 요청을 인증합니다. 자세한 동작 원리는 Sanctum의 "동작 원리" 문서를 참고하세요.

웹 UI와 API를 함께 제공하거나, 별도의 SPA 프론트엔드나 모바일 클라이언트가 있는 Laravel 애플리케이션이라면 Sanctum이 권장되는 선택입니다.

정리: 어떤 인증 방식을 선택할까?

상황에 맞는 인증 방식을 정리하면 다음과 같습니다.

상황권장 방식
브라우저 기반 모놀리식 Laravel 애플리케이션Laravel 내장 인증 서비스
서드파티가 소비하는 API 제공Sanctum (일반적으로 권장) 또는 Passport
SPA + Laravel 백엔드Laravel Sanctum
OAuth2 스펙의 모든 기능이 반드시 필요한 경우Passport
빠르게 시작하고 싶은 경우스타터 킷

일반적으로 Sanctum은 API 인증, SPA 인증, 모바일 인증을 모두 지원하고 "스코프(scope)" 및 "어빌리티(ability)" 기능도 갖춘 단순하고 완전한 솔루션이므로, 가능하다면 Sanctum을 우선적으로 선택하는 것이 좋습니다.

SPA 환경에서 별도의 인증 백엔드가 필요하다면, Laravel Fortify를 헤드리스 인증 백엔드로 활용하는 것도 좋은 방법입니다. Fortify는 회원가입, 비밀번호 재설정, 이메일 인증 등의 라우트와 컨트롤러를 제공합니다.

인증 빠른 시작

WARNING

이 섹션에서는 Laravel 애플리케이션 스타터 킷을 이용한 인증 구현을 설명합니다. 스타터 킷은 UI 스캐폴딩을 포함하여 빠르게 시작할 수 있도록 도와줍니다. Laravel의 인증 시스템을 직접 통합하고 싶다면 수동 사용자 인증 문서를 참고하세요.

스타터 킷 설치

먼저 Laravel 애플리케이션 스타터 킷을 설치하세요. 스타터 킷은 새 Laravel 프로젝트에 인증 기능을 빠르게 추가할 수 있는 잘 설계된 시작점을 제공합니다.

인증된 사용자 가져오기

스타터 킷으로 애플리케이션을 만들고 사용자 회원가입 및 로그인이 가능해지면, 현재 로그인한 사용자 정보에 자주 접근해야 할 것입니다. 요청을 처리하는 중에는 Auth 파사드의 user 메서드로 인증된 사용자를 가져올 수 있습니다.

use Illuminate\Support\Facades\Auth; // 현재 인증된 사용자 가져오기... $user = Auth::user(); // 현재 인증된 사용자의 ID 가져오기... $id = Auth::id();

또는, 컨트롤러 메서드에서 Illuminate\Http\Request 타입힌트를 사용하면 요청 객체를 통해 인증된 사용자에 접근할 수도 있습니다. 타입힌트된 클래스는 컨트롤러 메서드에 자동으로 주입되므로, 별도의 Auth 파사드 없이 $request->user()로 편리하게 사용자를 가져올 수 있습니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class FlightController extends Controller { /** * 항공편 정보를 업데이트합니다. */ public function update(Request $request): RedirectResponse { $user = $request->user(); // ... return redirect('/flights'); } }

현재 사용자의 인증 여부 확인

현재 요청을 보낸 사용자가 로그인 상태인지 확인하려면 Auth 파사드의 check 메서드를 사용하세요. 인증된 사용자라면 true를 반환합니다.

use Illuminate\Support\Facades\Auth; if (Auth::check()) { // 사용자가 로그인되어 있습니다... }

NOTE

check 메서드로 인증 여부를 확인할 수 있지만, 실제로는 특정 라우트나 컨트롤러에 접근하기 전에 미들웨어를 통해 인증을 검사하는 것이 일반적입니다. 자세한 내용은 라우트 보호하기 문서를 참고하세요.

라우트 보호하기

라우트 미들웨어를 사용하면 인증된 사용자만 특정 라우트에 접근하도록 제한할 수 있습니다. Laravel은 Illuminate\Auth\Middleware\Authenticate 클래스에 대한 미들웨어 별칭으로 auth 미들웨어를 기본 제공합니다. 이미 내부적으로 별칭이 등록되어 있으므로, 라우트 정의에 바로 붙여 사용하면 됩니다.

Route::get('/flights', function () { // 인증된 사용자만 이 라우트에 접근할 수 있습니다... })->middleware('auth');

비인증 사용자 리다이렉트

auth 미들웨어가 비인증 사용자를 감지하면 login이라는 이름의 라우트로 리다이렉트합니다. 이 동작은 bootstrap/app.php 파일의 redirectGuestsTo 메서드로 변경할 수 있습니다.

use Illuminate\Http\Request; ->withMiddleware(function (Middleware $middleware): void { $middleware->redirectGuestsTo('/login'); // 클로저를 사용하는 경우... $middleware->redirectGuestsTo(fn (Request $request) => route('login')); })

인증된 사용자 리다이렉트

guest 미들웨어가 이미 로그인한 사용자를 감지하면 dashboard 또는 home이라는 이름의 라우트로 리다이렉트합니다. 이 동작은 bootstrap/app.php 파일의 redirectUsersTo 메서드로 변경할 수 있습니다.

use Illuminate\Http\Request; ->withMiddleware(function (Middleware $middleware): void { $middleware->redirectUsersTo('/panel'); // 클로저를 사용하는 경우... $middleware->redirectUsersTo(fn (Request $request) => route('panel')); })

가드 지정하기

auth 미들웨어를 라우트에 적용할 때, 어떤 "가드"를 사용할지 명시할 수 있습니다. 지정하는 가드 이름은 auth.php 설정 파일의 guards 배열에 정의된 키와 일치해야 합니다.

Route::get('/flights', function () { // 인증된 사용자만 이 라우트에 접근할 수 있습니다... })->middleware('auth:admin');

로그인 횟수 제한

애플리케이션 스타터 킷을 사용하면 로그인 시도에 자동으로 횟수 제한(Rate Limiting)이 적용됩니다. 기본적으로 여러 번 잘못된 자격증명을 입력하면 1분 동안 로그인이 제한됩니다. 이 제한은 사용자의 이메일/아이디와 IP 주소를 조합하여 독립적으로 적용됩니다.

NOTE

애플리케이션의 다른 라우트에도 횟수 제한을 적용하고 싶다면 속도 제한 문서를 참고하세요.

인증

사용자 직접 인증 처리

Laravel의 애플리케이션 스타터 킷에 포함된 인증 스캐폴딩을 반드시 사용할 필요는 없습니다. 스타터 킷을 사용하지 않는다면, Laravel 인증 클래스를 직접 활용해 사용자 인증을 구현해야 합니다. 생각보다 간단하니 걱정하지 않아도 됩니다.

Laravel의 인증 서비스는 Auth 파사드를 통해 접근합니다. 클래스 상단에서 Auth 파사드를 임포트한 뒤, attempt 메서드를 살펴봅시다. attempt 메서드는 주로 로그인 폼에서의 인증 시도를 처리하는 데 사용됩니다. 인증에 성공했다면, 세션 고정(session fixation) 공격을 방지하기 위해 반드시 세션을 재생성해야 합니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { /** * 인증 시도를 처리합니다. */ public function authenticate(Request $request): RedirectResponse { $credentials = $request->validate([ 'email' => ['required', 'email'], 'password' => ['required'], ]); if (Auth::attempt($credentials)) { $request->session()->regenerate(); return redirect()->intended('dashboard'); } return back()->withErrors([ 'email' => '입력하신 정보와 일치하는 계정을 찾을 수 없습니다.', ])->onlyInput('email'); } }

attempt 메서드는 첫 번째 인수로 키/값 쌍의 배열을 받습니다. 배열의 값들은 데이터베이스에서 사용자를 조회하는 데 사용됩니다. 위 예시에서는 email 컬럼 값으로 사용자를 조회합니다. 사용자를 찾으면, 데이터베이스에 저장된 해시된 비밀번호와 배열로 전달된 password 값을 비교합니다. 이때 password 값을 직접 해시할 필요는 없습니다. 프레임워크가 자동으로 해시 처리한 뒤 데이터베이스의 값과 비교하기 때문입니다. 두 해시된 비밀번호가 일치하면 인증 세션이 시작됩니다.

Laravel의 인증 서비스는 config/auth.php에 설정된 인증 가드의 "provider" 구성을 기반으로 데이터베이스에서 사용자를 조회합니다. 기본 설정에서는 Eloquent 사용자 프로바이더가 지정되어 있으며, App\Models\User 모델을 사용해 사용자를 조회합니다. 애플리케이션의 요구사항에 맞게 이 값들을 변경할 수 있습니다.

attempt 메서드는 인증 성공 시 true를, 실패 시 false를 반환합니다.

intended 메서드는 사용자가 인증 미들웨어에 의해 차단되기 전에 접근하려 했던 URL로 리다이렉트합니다. 이동하려던 목적지를 알 수 없는 경우를 위해 폴백 URI를 인수로 전달할 수 있습니다.

추가 조건 지정하기

이메일과 비밀번호 외에 추가적인 조건을 인증 쿼리에 포함할 수 있습니다. attempt 메서드에 전달하는 배열에 원하는 쿼리 조건을 추가하면 됩니다. 예를 들어, 사용자가 "활성(active)" 상태인지 함께 확인할 수 있습니다.

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { // 인증 성공... }

복잡한 쿼리 조건이 필요한 경우, 자격 증명 배열 안에 클로저를 포함할 수 있습니다. 이 클로저는 쿼리 인스턴스를 인수로 받으므로, 애플리케이션 요구에 맞게 쿼리를 자유롭게 커스터마이즈할 수 있습니다.

use Illuminate\Database\Eloquent\Builder; if (Auth::attempt([ 'email' => $email, 'password' => $password, fn (Builder $query) => $query->has('activeSubscription'), ])) { // 인증 성공... }

WARNING

위 예시에서 email은 필수 항목이 아니라 단순한 예시입니다. 실제 데이터베이스 테이블의 "사용자명" 역할을 하는 컬럼명을 사용하세요.

attemptWhen 메서드는 두 번째 인수로 클로저를 받아, 실제 인증 전에 잠재적 사용자를 더 세밀하게 검사할 수 있습니다. 클로저는 잠재적 사용자 인스턴스를 받아 인증 가능 여부를 true 또는 false로 반환해야 합니다.

if (Auth::attemptWhen([ 'email' => $email, 'password' => $password, ], function (User $user) { return $user->isNotBanned(); })) { // 인증 성공... }

특정 가드 인스턴스 사용하기

Auth 파사드의 guard 메서드를 사용하면 인증 시 사용할 가드 인스턴스를 직접 지정할 수 있습니다. 이를 통해 애플리케이션의 서로 다른 영역(예: 일반 사용자와 관리자)을 완전히 별개의 인증 가능 모델이나 사용자 테이블로 관리할 수 있습니다.

guard 메서드에 전달하는 가드 이름은 auth.php 설정 파일에 정의된 가드 중 하나여야 합니다.

if (Auth::guard('admin')->attempt($credentials)) { // ... }

로그인 상태 유지 (Remember Me)

많은 웹 애플리케이션의 로그인 폼에는 "로그인 상태 유지" 체크박스가 있습니다. 이 기능을 구현하려면 attempt 메서드의 두 번째 인수로 불리언 값을 전달하면 됩니다.

이 값이 true이면 Laravel은 사용자가 직접 로그아웃하기 전까지 인증 상태를 무기한 유지합니다. 이 기능을 사용하려면 users 테이블에 "remember me" 토큰을 저장할 remember_token 문자열 컬럼이 있어야 합니다. 새 Laravel 애플리케이션에 포함된 users 테이블 마이그레이션에는 이 컬럼이 이미 포함되어 있습니다.

use Illuminate\Support\Facades\Auth; if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { // 로그인 상태 유지 처리 중... }

"로그인 상태 유지" 기능을 제공하는 경우, viaRemember 메서드를 통해 현재 인증된 사용자가 "remember me" 쿠키로 인증되었는지 확인할 수 있습니다.

use Illuminate\Support\Facades\Auth; if (Auth::viaRemember()) { // ... }

그 외 인증 방법

사용자 인스턴스로 직접 인증하기

기존 사용자 인스턴스를 현재 인증된 사용자로 설정해야 할 경우, Auth 파사드의 login 메서드에 해당 인스턴스를 전달할 수 있습니다. 전달하는 사용자 인스턴스는 Illuminate\Contracts\Auth\Authenticatable 컨트랙트를 구현해야 합니다. Laravel에 기본 포함된 App\Models\User 모델은 이미 이 인터페이스를 구현하고 있습니다. 이 방법은 사용자가 회원가입한 직후처럼 유효한 사용자 인스턴스를 이미 보유하고 있을 때 유용합니다.

use Illuminate\Support\Facades\Auth; Auth::login($user);

login 메서드의 두 번째 인수로 불리언 값을 전달해 "로그인 상태 유지" 기능을 활성화할 수 있습니다. 이 경우 사용자가 직접 로그아웃하기 전까지 세션 인증이 무기한 유지됩니다.

Auth::login($user, $remember = true);

필요하다면 login 메서드 호출 전에 사용할 가드를 지정할 수 있습니다.

Auth::guard('admin')->login($user);

ID로 사용자 인증하기

데이터베이스 레코드의 기본 키(primary key)를 사용해 사용자를 인증하려면 loginUsingId 메서드를 사용합니다. 이 메서드는 인증할 사용자의 기본 키를 인수로 받습니다.

Auth::loginUsingId(1);

loginUsingId 메서드의 remember 인수에 불리언 값을 전달해 "로그인 상태 유지" 기능을 활성화할 수 있습니다. 이 경우 사용자가 직접 로그아웃하기 전까지 세션 인증이 무기한 유지됩니다.

Auth::loginUsingId(1, remember: true);

단일 요청에만 사용자 인증하기

once 메서드를 사용하면 단 하나의 요청에 한해서만 사용자를 인증할 수 있습니다. 이 메서드는 세션과 쿠키를 전혀 사용하지 않으며, Login 이벤트도 발생하지 않습니다. API처럼 상태를 유지하지 않는 요청을 처리할 때 유용합니다.

if (Auth::once($credentials)) { // ... }

HTTP 기본 인증

HTTP Basic Authentication은 별도의 로그인 페이지를 만들지 않고도 사용자를 빠르게 인증할 수 있는 방법입니다. 라우트에 auth.basic 미들웨어를 연결하는 것만으로 동작합니다. 이 미들웨어는 Laravel 프레임워크에 이미 포함되어 있으므로 별도로 정의할 필요가 없습니다.

Route::get('/profile', function () { // 인증된 사용자만 이 라우트에 접근할 수 있습니다... })->middleware('auth.basic');

미들웨어가 연결된 라우트에 브라우저로 접근하면 자동으로 자격 증명 입력 창이 표시됩니다. 기본적으로 auth.basic 미들웨어는 users 테이블의 email 컬럼을 사용자 이름(username)으로 사용합니다.

FastCGI 사용 시 주의사항

PHP FastCGI와 Apache를 함께 사용하는 환경에서는 HTTP Basic 인증이 정상적으로 동작하지 않을 수 있습니다. 이 경우 애플리케이션의 .htaccess 파일에 아래 내용을 추가하면 문제를 해결할 수 있습니다.

RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

상태 비저장 HTTP 기본 인증

세션에 사용자 식별 쿠키를 저장하지 않고 HTTP Basic 인증을 사용할 수도 있습니다. 이 방식은 주로 API 요청을 인증할 때 유용합니다. 구현하려면 onceBasic 메서드를 호출하는 미들웨어를 정의하면 됩니다. onceBasic 메서드가 응답을 반환하지 않으면 요청이 애플리케이션 내부로 계속 전달됩니다.

<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Symfony\Component\HttpFoundation\Response; class AuthenticateOnceWithBasicAuth { /** * 들어오는 요청을 처리합니다. * * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next */ public function handle(Request $request, Closure $next): Response { return Auth::onceBasic() ?: $next($request); } }

이후 이 미들웨어를 라우트에 연결합니다.

Route::get('/api/user', function () { // 인증된 사용자만 이 라우트에 접근할 수 있습니다... })->middleware(AuthenticateOnceWithBasicAuth::class);

NOTE

onceBasic은 세션에 인증 상태를 저장하지 않으므로, 매 요청마다 자격 증명을 확인합니다. 세션 기반 인증이 필요한 일반 웹 페이지에는 적합하지 않으며, API 엔드포인트처럼 무상태(stateless) 환경에서 사용하는 것이 좋습니다.

로그아웃

사용자를 수동으로 로그아웃시키려면 Auth 파사드의 logout 메서드를 사용합니다. 이 메서드는 세션에서 인증 정보를 제거하여 이후 요청이 인증되지 않은 상태로 처리되도록 합니다.

logout을 호출한 후에는 세션을 무효화하고 CSRF 토큰을 재생성하는 것도 권장합니다. 세션을 그대로 두면 기존 세션 데이터가 남아 보안 취약점이 생길 수 있습니다. 로그아웃 처리 후에는 일반적으로 애플리케이션의 루트 경로로 리다이렉트합니다.

use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Auth; /** * 사용자를 로그아웃 처리합니다. */ public function logout(Request $request): RedirectResponse { Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect('/'); }

다른 기기의 세션 무효화

Laravel은 현재 기기의 세션은 유지하면서 다른 기기에서 활성화된 세션만 무효화하는 기능도 제공합니다. 이 기능은 사용자가 비밀번호를 변경할 때 다른 기기의 로그인 상태를 모두 종료하되, 현재 기기는 계속 로그인 상태로 유지하고 싶을 때 주로 활용됩니다.

이 기능을 사용하려면 먼저 세션 인증이 필요한 라우트에 Illuminate\Session\Middleware\AuthenticateSession 미들웨어가 적용되어 있어야 합니다. 일반적으로 라우트 그룹에 등록하여 애플리케이션의 대부분의 라우트에 일괄 적용하는 것이 좋습니다. 이 미들웨어는 auth.session이라는 미들웨어 별칭으로 등록되어 있습니다.

Route::middleware(['auth', 'auth.session'])->group(function () { Route::get('/', function () { // ... }); });

미들웨어 설정이 완료되면 Auth 파사드의 logoutOtherDevices 메서드를 호출합니다. 이 메서드는 현재 비밀번호를 인자로 받으므로, 입력 폼을 통해 사용자에게 현재 비밀번호를 확인받아야 합니다.

use Illuminate\Support\Facades\Auth; Auth::logoutOtherDevices($currentPassword);

logoutOtherDevices가 호출되면 해당 사용자의 다른 모든 세션이 완전히 무효화됩니다. 즉, 이전에 인증되었던 모든 가드에서 로그아웃 처리됩니다.

인증

비밀번호 재확인

애플리케이션을 개발하다 보면, 특정 작업을 수행하거나 민감한 영역으로 이동하기 전에 사용자에게 비밀번호를 다시 한번 확인하도록 요구해야 할 때가 있습니다. Laravel은 이 과정을 간편하게 처리할 수 있는 미들웨어를 기본으로 제공합니다. 이 기능을 구현하려면 두 개의 라우트를 정의해야 합니다. 하나는 비밀번호 확인 폼을 보여주는 라우트이고, 다른 하나는 비밀번호를 검증한 뒤 사용자를 원래 목적지로 리디렉션하는 라우트입니다.

NOTE

아래 문서는 비밀번호 재확인 기능을 직접 구현하는 방법을 설명합니다. 더 빠르게 시작하고 싶다면 Laravel 애플리케이션 스타터 킷을 사용하면 이 기능이 이미 포함되어 있습니다.

설정

사용자가 비밀번호를 확인하면, 이후 3시간 동안은 동일한 확인 요청이 발생하지 않습니다. 이 유효 시간을 변경하려면 config/auth.php 설정 파일의 password_timeout 값을 수정하면 됩니다.

라우팅

비밀번호 확인 폼

먼저, 사용자에게 비밀번호 입력을 요청하는 뷰를 반환하는 라우트를 정의합니다.

Route::get('/confirm-password', function () { return view('auth.confirm-password'); })->middleware('auth')->name('password.confirm');

이 라우트가 반환하는 뷰에는 password 필드가 포함된 폼이 있어야 합니다. 또한 사용자가 보호된 영역에 접근하려 하고 있으며 비밀번호 확인이 필요하다는 안내 문구를 함께 표시하는 것을 권장합니다.

비밀번호 검증 처리

다음으로, 확인 폼에서 제출된 요청을 처리할 라우트를 정의합니다. 이 라우트는 비밀번호를 검증하고, 유효하다면 사용자를 원래 목적지로 리디렉션합니다.

use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; Route::post('/confirm-password', function (Request $request) { if (! Hash::check($request->password, $request->user()->password)) { return back()->withErrors([ 'password' => ['입력한 비밀번호가 일치하지 않습니다.'] ]); } $request->session()->passwordConfirmed(); return redirect()->intended(); })->middleware(['auth', 'throttle:6,1']);

이 라우트의 동작을 단계별로 살펴보겠습니다.

  1. 비밀번호 일치 여부 확인Hash::check()를 사용해 요청으로 전달된 비밀번호와 현재 인증된 사용자의 비밀번호를 비교합니다.
  2. 세션에 확인 시각 기록 — 비밀번호가 일치하면 passwordConfirmed() 메서드를 호출합니다. 이 메서드는 사용자의 세션에 타임스탬프를 저장하며, Laravel은 이 값을 기준으로 재확인 여부를 판단합니다.
  3. 원래 목적지로 리디렉션redirect()->intended()를 사용해 사용자가 원래 접근하려 했던 경로로 이동시킵니다.

throttle:6,1 미들웨어는 1분 동안 최대 6번의 시도를 허용합니다. 무차별 대입 공격(brute force)을 방지하기 위해 반드시 유지하세요.

라우트 보호하기

최근 비밀번호 확인이 필요한 작업을 수행하는 라우트에는 password.confirm 미들웨어를 적용해야 합니다. 이 미들웨어는 Laravel 기본 설치 시 포함되어 있으며, 사용자가 비밀번호를 확인하기 전에 접근하려 했던 목적지를 세션에 자동으로 저장합니다. 비밀번호 확인이 완료되면 미들웨어는 저장해 둔 목적지로 사용자를 리디렉션합니다. 비밀번호 확인 전에는 password.confirm이라는 이름이 지정된 라우트로 이동시킵니다.

Route::get('/settings', function () { // ... })->middleware(['password.confirm']); Route::post('/settings', function () { // ... })->middleware(['password.confirm']);

인증

커스텀 가드 추가하기

Auth 파사드의 extend 메서드를 사용하면 직접 인증 가드를 정의할 수 있습니다. extend 호출은 서비스 프로바이더 안에 두어야 합니다. Laravel에는 기본으로 AppServiceProvider가 포함되어 있으므로, 그 안에 코드를 작성하면 됩니다.

<?php namespace App\Providers; use App\Services\Auth\JwtGuard; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { // ... /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::extend('jwt', function (Application $app, string $name, array $config) { // Illuminate\Contracts\Auth\Guard 인스턴스를 반환해야 합니다. return new JwtGuard(Auth::createUserProvider($config['provider'])); }); } }

위 예시에서 볼 수 있듯이, extend 메서드에 전달하는 콜백은 Illuminate\Contracts\Auth\Guard 인터페이스의 구현체를 반환해야 합니다. 이 인터페이스에는 커스텀 가드를 정의하기 위해 구현해야 할 메서드들이 포함되어 있습니다.

커스텀 가드를 정의한 뒤에는 auth.php 설정 파일의 guards 섹션에서 해당 가드를 참조할 수 있습니다.

'guards' => [ 'api' => [ 'driver' => 'jwt', 'provider' => 'users', ], ],

클로저 기반 요청 가드

HTTP 요청 기반의 커스텀 인증을 구현하는 가장 간단한 방법은 Auth::viaRequest 메서드를 사용하는 것입니다. 클로저 하나만으로 인증 로직을 빠르게 정의할 수 있어 간단한 커스텀 가드에 적합합니다.

AppServiceProviderboot 메서드 안에서 Auth::viaRequest를 호출합니다. 첫 번째 인자로는 인증 드라이버 이름(커스텀 가드를 식별하는 임의의 문자열)을 전달하고, 두 번째 인자로는 클로저를 전달합니다. 클로저는 HTTP 요청 객체를 받아 인증 성공 시 사용자 인스턴스를, 실패 시 null을 반환해야 합니다.

use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::viaRequest('custom-token', function (Request $request) { return User::where('token', (string) $request->token)->first(); }); }

커스텀 인증 드라이버를 정의한 뒤에는 auth.php 설정 파일의 guards 섹션에 드라이버로 등록합니다.

'guards' => [ 'api' => [ 'driver' => 'custom-token', ], ],

이후 라우트에 인증 미들웨어를 적용할 때 해당 가드를 지정할 수 있습니다.

Route::middleware('auth:api')->group(function () { // ... });

커스텀 사용자 프로바이더 추가하기

관계형 데이터베이스가 아닌 다른 저장소에 사용자 정보를 보관하는 경우, 직접 인증 사용자 프로바이더를 구현해야 합니다. Auth 파사드의 provider 메서드를 사용하면 커스텀 프로바이더를 등록할 수 있습니다. 프로바이더 리졸버는 Illuminate\Contracts\Auth\UserProvider의 구현체를 반환해야 합니다.

<?php namespace App\Providers; use App\Extensions\MongoUserProvider; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { // ... /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Auth::provider('mongo', function (Application $app, array $config) { // Illuminate\Contracts\Auth\UserProvider 인스턴스를 반환합니다... return new MongoUserProvider($app->make('mongo.connection')); }); } }

프로바이더를 등록한 뒤에는 auth.php 설정 파일에서 새 드라이버로 전환할 수 있습니다. 먼저 새 드라이버를 사용하는 provider를 정의합니다.

'providers' => [ 'users' => [ 'driver' => 'mongo', ], ],

마지막으로 guards 설정에서 이 프로바이더를 참조합니다.

'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], ],

UserProvider 컨트랙트

Illuminate\Contracts\Auth\UserProvider 구현체는 MySQL, MongoDB 등 영속적 저장소에서 Illuminate\Contracts\Auth\Authenticatable 구현체를 가져오는 역할을 담당합니다. 이 두 인터페이스 덕분에 사용자 데이터를 어떤 방식으로 저장하든, 어떤 클래스로 인증된 사용자를 표현하든 Laravel 인증 메커니즘이 정상적으로 동작할 수 있습니다.

Illuminate\Contracts\Auth\UserProvider 컨트랙트를 살펴보겠습니다.

<?php namespace Illuminate\Contracts\Auth; interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false); }

각 메서드의 역할은 다음과 같습니다.

  • retrieveById: MySQL의 자동 증가 ID처럼 사용자를 식별하는 키를 받아 해당 Authenticatable 구현체를 반환합니다.

  • retrieveByToken: 고유 식별자 $identifier와 "로그인 상태 유지(remember me)" 토큰 $token으로 사용자를 조회합니다. 토큰은 일반적으로 remember_token 컬럼에 저장됩니다.

  • updateRememberToken: $user 인스턴스의 remember_token을 새 $token으로 갱신합니다. "로그인 상태 유지" 인증에 성공하거나 사용자가 로그아웃할 때 새 토큰이 발급됩니다.

  • retrieveByCredentials: Auth::attempt 호출 시 전달된 자격증명 배열을 받아 저장소에서 해당 사용자를 조회합니다. 보통 $credentials['username'] 값과 일치하는 레코드를 where 조건으로 검색합니다. 이 메서드에서는 비밀번호 검증을 수행하지 않아야 합니다.

  • validateCredentials: $user$credentials를 비교하여 인증 여부를 결정합니다. 일반적으로 Hash::check를 사용해 $user->getAuthPassword()$credentials['password']를 비교하고, 일치 여부에 따라 true 또는 false를 반환합니다.

  • rehashPasswordIfRequired: 필요한 경우 사용자의 비밀번호를 재해싱합니다. 보통 Hash::needsRehash로 재해싱 필요 여부를 확인한 뒤, 필요하다면 Hash::make로 새 해시를 생성하고 저장소의 레코드를 업데이트합니다.

Authenticatable 컨트랙트

UserProvider의 각 메서드를 살펴봤으니, 이제 Authenticatable 컨트랙트를 확인해 보겠습니다. retrieveById, retrieveByToken, retrieveByCredentials 메서드는 모두 이 인터페이스의 구현체를 반환해야 합니다.

<?php namespace Illuminate\Contracts\Auth; interface Authenticatable { public function getAuthIdentifierName(); public function getAuthIdentifier(); public function getAuthPasswordName(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); }

인터페이스는 간단한 구조로 되어 있습니다.

메서드역할
getAuthIdentifierName()기본 키 컬럼 이름 반환 (예: id)
getAuthIdentifier()사용자의 기본 키 값 반환
getAuthPasswordName()비밀번호 컬럼 이름 반환
getAuthPassword()해싱된 비밀번호 반환
getRememberToken()"로그인 상태 유지" 토큰 반환
setRememberToken($value)"로그인 상태 유지" 토큰 설정
getRememberTokenName()토큰 컬럼 이름 반환

이 인터페이스 덕분에 어떤 ORM이나 저장소 추상화 계층을 사용하더라도 인증 시스템이 동일하게 동작합니다. Laravel은 기본적으로 app/Models 디렉터리에 이 인터페이스를 구현한 App\Models\User 클래스를 제공합니다.

비밀번호 자동 재해싱

Laravel의 기본 비밀번호 해싱 알고리즘은 bcrypt입니다. bcrypt의 "작업 인수(work factor)"는 config/hashing.php 설정 파일이나 BCRYPT_ROUNDS 환경 변수를 통해 조정할 수 있습니다.

CPU/GPU 성능이 향상됨에 따라 bcrypt 작업 인수를 점진적으로 높이는 것이 보안상 권장됩니다. 작업 인수를 높이면, Laravel은 사용자가 스타터 킷이나 attempt 메서드를 통해 수동으로 인증할 때 기존 비밀번호를 자동으로 재해싱합니다.

NOTE

비밀번호 자동 재해싱은 대부분의 애플리케이션에서 별도의 처리 없이 투명하게 동작합니다. 일반적으로 서비스 운영에 영향을 주지 않습니다.

이 동작을 비활성화하려면 먼저 hashing 설정 파일을 게시합니다:

php artisan config:publish hashing

게시된 설정 파일에서 rehash_on_login 값을 false로 설정합니다:

'rehash_on_login' => false,

이벤트

Laravel은 인증 과정에서 다양한 이벤트를 자동으로 발생시킵니다. 아래 이벤트들에 대해 리스너를 등록하면, 로그인·로그아웃·비밀번호 재설정 등 인증 흐름의 각 단계에 원하는 로직을 추가할 수 있습니다.

이벤트 이름
Illuminate\Auth\Events\Registered
Illuminate\Auth\Events\Attempting
Illuminate\Auth\Events\Authenticated
Illuminate\Auth\Events\Login
Illuminate\Auth\Events\Failed
Illuminate\Auth\Events\Validated
Illuminate\Auth\Events\Verified
Illuminate\Auth\Events\Logout
Illuminate\Auth\Events\CurrentDeviceLogout
Illuminate\Auth\Events\OtherDeviceLogout
Illuminate\Auth\Events\Lockout
Illuminate\Auth\Events\PasswordReset
Illuminate\Auth\Events\PasswordResetLinkSent

NOTE

예를 들어 회원가입 완료 후 환영 이메일을 보내거나, 로그인 실패 횟수를 기록하거나, 다른 기기에서 로그아웃 시 알림을 발송하는 등의 부가 처리를 이벤트 리스너로 깔끔하게 분리할 수 있습니다.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 7월 2일