Laravel Sanctum
번역일: 2026년 6월 27일
Laravel Sanctum
소개
Laravel Sanctum은 SPA(싱글 페이지 애플리케이션), 모바일 앱, 그리고 간단한 토큰 기반 API를 위한 경량 인증 시스템입니다. Sanctum을 사용하면 각 사용자가 자신의 계정에 대해 여러 개의 API 토큰을 생성할 수 있으며, 각 토큰에 허용할 동작을 정의하는 권한(abilities/scopes)을 부여할 수 있습니다.
동작 원리
Sanctum은 서로 다른 두 가지 문제를 해결하기 위해 만들어졌습니다. 각 기능을 먼저 이해하고 나면 라이브러리를 더 효과적으로 활용할 수 있습니다.
API 토큰
첫 번째로, Sanctum은 OAuth의 복잡함 없이 사용자에게 API 토큰을 발급할 수 있는 간단한 패키지입니다. GitHub 등에서 제공하는 "개인 액세스 토큰"에서 영감을 받았습니다. 예를 들어, 애플리케이션의 "계정 설정" 화면에서 사용자가 자신의 API 토큰을 직접 생성할 수 있도록 구성할 수 있습니다. Sanctum은 이러한 토큰의 생성과 관리를 담당합니다. 토큰은 일반적으로 만료 기간이 매우 길게(수년) 설정되지만, 사용자가 언제든지 수동으로 폐기할 수 있습니다.
Sanctum은 사용자의 API 토큰을 단일 데이터베이스 테이블에 저장하고, 들어오는 HTTP 요청의 Authorization 헤더에 포함된 유효한 API 토큰을 통해 인증을 처리합니다.
SPA 인증
두 번째로, Sanctum은 Laravel API와 통신해야 하는 SPA를 간단하게 인증할 수 있는 방법을 제공합니다. 이 SPA는 Laravel 애플리케이션과 같은 저장소에 있을 수도 있고, Vue CLI나 Next.js로 만든 완전히 별도의 저장소에 있을 수도 있습니다.
이 기능은 토큰을 전혀 사용하지 않습니다. 대신 Laravel의 내장 쿠키 기반 세션 인증 서비스를 활용합니다. 구체적으로는 Laravel의 web 인증 가드를 사용하며, 덕분에 CSRF 보호, 세션 인증, XSS를 통한 인증 정보 유출 방지 등의 이점을 모두 누릴 수 있습니다.
Sanctum은 요청이 자체 SPA 프론트엔드에서 온 경우에만 쿠키 인증을 시도합니다. 들어오는 HTTP 요청을 처리할 때 Sanctum은 먼저 인증 쿠키를 확인하고, 쿠키가 없으면 Authorization 헤더에서 유효한 API 토큰을 찾습니다.
NOTE
Sanctum을 API 토큰 인증 전용으로만, 혹은 SPA 인증 전용으로만 사용해도 전혀 문제없습니다. 두 기능을 모두 사용할 의무는 없습니다.
설치
NOTE
최신 버전의 Laravel에는 Sanctum이 이미 포함되어 있습니다. 다만 composer.json에 laravel/sanctum이 없다면 아래 설치 방법을 따르세요.
Composer로 Sanctum을 설치합니다:
composer require laravel/sanctum다음으로 vendor:publish Artisan 명령어로 Sanctum의 설정 파일과 마이그레이션 파일을 퍼블리시합니다. sanctum 설정 파일은 애플리케이션의 config 디렉토리에 생성됩니다:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"마지막으로 마이그레이션을 실행합니다. Sanctum은 API 토큰을 저장할 테이블 하나를 생성합니다:
php artisan migrateSPA 인증에 Sanctum을 사용할 계획이라면, app/Http/Kernel.php 파일의 api 미들웨어 그룹에 Sanctum 미들웨어를 추가해야 합니다:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],마이그레이션 커스터마이즈
Sanctum의 기본 마이그레이션을 사용하지 않으려면, App\Providers\AppServiceProvider 클래스의 register 메서드에서 Sanctum::ignoreMigrations를 호출하세요. 기본 마이그레이션 파일은 다음 명령어로 내보낼 수 있습니다: php artisan vendor:publish --tag=sanctum-migrations
설정
기본 모델 재정의
일반적으로 필요하지 않지만, Sanctum이 내부적으로 사용하는 PersonalAccessToken 모델을 자유롭게 확장할 수 있습니다:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
// ...
}커스텀 모델을 만든 후에는 Sanctum의 usePersonalAccessTokenModel 메서드로 Sanctum이 해당 모델을 사용하도록 지정합니다. 일반적으로 서비스 프로바이더의 boot 메서드에서 호출합니다:
use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}API 토큰 인증
NOTE
자체 퍼스트파티 SPA 인증에는 API 토큰을 사용하지 마세요. 대신 Sanctum의 SPA 인증 기능을 사용하세요.
API 토큰 발급
Sanctum은 API 요청 인증에 사용할 수 있는 API 토큰(개인 액세스 토큰)을 발급할 수 있습니다. API 토큰을 사용해 요청을 보낼 때는 Authorization 헤더에 Bearer 토큰으로 포함해야 합니다.
토큰을 발급하려면 먼저 User 모델에 Laravel\Sanctum\HasApiTokens 트레이트를 추가합니다:
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}토큰은 createToken 메서드로 발급합니다. 이 메서드는 Laravel\Sanctum\NewAccessToken 인스턴스를 반환합니다. API 토큰은 데이터베이스에 저장되기 전에 SHA-256으로 해시되므로, 토큰의 평문 값은 NewAccessToken 인스턴스의 plainTextToken 프로퍼티를 통해서만 접근할 수 있습니다. 토큰이 생성된 직후에 사용자에게 값을 보여주어야 합니다:
use Illuminate\Http\Request;
Route::post('/tokens/create', function (Request $request) {
$token = $request->user()->createToken($request->token_name);
return ['token' => $token->plainTextToken];
});HasApiTokens 트레이트가 제공하는 tokens Eloquent 관계를 통해 사용자의 모든 토큰에 접근할 수 있습니다:
foreach ($user->tokens as $token) {
// ...
}토큰 권한(Abilities)
Sanctum에서는 토큰에 "abilities"를 부여할 수 있습니다. OAuth의 "scope"와 유사한 개념입니다. createToken 메서드의 두 번째 인수로 문자열 배열을 전달해 권한을 지정합니다:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
요청을 처리할 때 tokenCan 메서드로 해당 토큰이 특정 권한을 가지는지 확인할 수 있습니다:
if ($user->tokenCan('server:update')) {
// ...
}토큰 권한 미들웨어
Sanctum은 요청 토큰의 권한을 검사하는 두 가지 미들웨어를 제공합니다. 먼저 app/Http/Kernel.php 파일의 $middlewareAliases 프로퍼티에 다음을 추가합니다:
'abilities' => \Laravel\Sanctum\Http\Middleware\CheckAbilities::class,
'ability' => \Laravel\Sanctum\Http\Middleware\CheckForAnyAbility::class,abilities 미들웨어는 토큰이 나열된 모든 권한을 가지고 있는지 검사합니다:
Route::get('/orders', function () {
// 토큰이 "check-status"와 "place-orders" 권한을 모두 가진 경우...
})->middleware(['auth:sanctum', 'abilities:check-status,place-orders']);ability 미들웨어는 토큰이 나열된 권한 중 하나 이상을 가지고 있는지 검사합니다:
Route::get('/orders', function () {
// 토큰이 "check-status" 또는 "place-orders" 권한을 가진 경우...
})->middleware(['auth:sanctum', 'ability:check-status,place-orders']);퍼스트파티 UI에서 시작된 요청
편의를 위해, 인증된 요청이 자체 SPA에서 온 것이고 Sanctum의 SPA 인증을 사용하고 있다면 tokenCan 메서드는 항상 true를 반환합니다.
단, 이것이 사용자가 해당 동작을 수행할 수 있다는 의미는 아닙니다. 실제 허용 여부는 애플리케이션의 권한 부여 정책에서 토큰의 권한과 사용자 자체의 허용 여부를 함께 확인해야 합니다.
예를 들어, 서버를 관리하는 애플리케이션이라면 토큰이 서버 업데이트 권한을 가지고 있는지, 그리고 해당 서버가 현재 사용자의 것인지 모두 확인해야 합니다:
return $request->user()->id === $server->user_id &&
$request->user()->tokenCan('server:update')처음에는 퍼스트파티 UI 요청에서 tokenCan이 항상 true를 반환하는 것이 이상하게 느껴질 수 있습니다. 하지만 이 설계 덕분에 요청이 UI에서 왔는지 서드파티 API 클라이언트에서 왔는지 신경 쓰지 않고, 권한 부여 정책 내에서 항상 tokenCan을 일관되게 호출할 수 있습니다.
라우트 보호
모든 들어오는 요청에 인증을 요구하려면, routes/web.php 및 routes/api.php의 보호할 라우트에 sanctum 인증 가드를 적용합니다. 이 가드는 요청이 SPA의 스테이트풀 쿠키 인증이거나, 서드파티의 유효한 API 토큰 헤더를 포함하는 경우에 인증을 통과시킵니다.
routes/web.php에서도 sanctum 가드를 사용하는 이유가 궁금할 수 있습니다. Sanctum은 먼저 세션 인증 쿠키로 인증을 시도하고, 쿠키가 없으면 Authorization 헤더의 토큰으로 인증을 시도합니다. 모든 요청에 Sanctum을 사용하면 현재 인증된 사용자 인스턴스에서 항상 tokenCan을 호출할 수 있다는 장점도 있습니다:
use Illuminate\Http\Request;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});토큰 폐기
Laravel\Sanctum\HasApiTokens 트레이트가 제공하는 tokens 관계를 통해 데이터베이스에서 토큰을 삭제하여 폐기할 수 있습니다:
// 모든 토큰 폐기...
$user->tokens()->delete();
// 현재 요청 인증에 사용된 토큰 폐기...
$request->user()->currentAccessToken()->delete();
// 특정 토큰 폐기...
$user->tokens()->where('id', $tokenId)->delete();토큰 만료
기본적으로 Sanctum 토큰은 만료되지 않으며, 토큰을 폐기해야만 무효화됩니다. 만료 시간을 설정하려면 sanctum 설정 파일의 expiration 옵션을 사용하세요. 이 값은 토큰이 만료되기까지의 시간(분)입니다:
'expiration' => 525600,토큰별로 만료 시간을 개별적으로 지정하려면, createToken 메서드의 세 번째 인수로 만료 시간을 전달합니다:
return $user->createToken(
'token-name', ['*'], now()->addWeek()
)->plainTextToken;토큰 만료 시간을 설정했다면, 만료된 토큰을 정기적으로 정리하는 스케줄 작업을 등록하는 것이 좋습니다. Sanctum에는 이를 위한 sanctum:prune-expired Artisan 명령어가 포함되어 있습니다. 예를 들어, 24시간 이상 지난 만료 토큰을 매일 삭제하려면 다음과 같이 설정합니다:
$schedule->command('sanctum:prune-expired --hours=24')->daily();SPA 인증
Sanctum은 Laravel API와 통신하는 SPA를 위한 간단한 인증 방법도 제공합니다. SPA는 Laravel 애플리케이션과 같은 저장소에 있을 수도, 완전히 별도의 저장소에 있을 수도 있습니다.
이 기능은 토큰을 사용하지 않고 Laravel의 내장 쿠키 기반 세션 인증을 활용합니다. 이 방식은 CSRF 보호, 세션 인증, XSS를 통한 인증 정보 유출 방지 등의 장점을 제공합니다.
WARNING
인증이 작동하려면 SPA와 API가 동일한 최상위 도메인을 공유해야 합니다. 서로 다른 서브도메인에 배치하는 것은 괜찮습니다. 또한 요청 시 Accept: application/json 헤더와 Referer 또는 Origin 헤더를 반드시 포함해야 합니다.
아래는 SPA 인증의 전체 흐름을 나타낸 다이어그램입니다:
설정
퍼스트파티 도메인 설정
먼저 SPA가 요청을 보낼 도메인을 설정해야 합니다. sanctum 설정 파일의 stateful 옵션을 사용합니다. 이 설정은 API 요청 시 Laravel 세션 쿠키를 통해 "스테이트풀" 인증을 유지할 도메인을 결정합니다.
WARNING
포트가 포함된 URL(예: 127.0.0.1:8000)로 접근하는 경우, 도메인 설정에 포트 번호도 포함해야 합니다.
Sanctum 미들웨어
다음으로, app/Http/Kernel.php의 api 미들웨어 그룹에 Sanctum 미들웨어를 추가합니다. 이 미들웨어는 SPA의 요청이 세션 쿠키로 인증될 수 있도록 하면서, 서드파티나 모바일 앱의 요청은 API 토큰으로 인증되도록 처리합니다:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],CORS와 쿠키
별도의 서브도메인에서 실행 중인 SPA에서 인증 문제가 발생한다면, CORS(교차 출처 리소스 공유) 또는 세션 쿠키 설정이 잘못된 경우가 많습니다.
config/cors.php 설정 파일에서 supports_credentials 옵션을 true로 설정하여 Access-Control-Allow-Credentials: True 헤더가 반환되도록 해야 합니다.
또한 프론트엔드의 전역 axios 인스턴스에서 withCredentials와 withXSRFToken 옵션을 활성화해야 합니다. 일반적으로 resources/js/bootstrap.js 파일에서 설정합니다. Axios 대신 다른 HTTP 클라이언트를 사용한다면 해당 클라이언트에서 동일한 설정을 적용하세요:
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;마지막으로, 세션 쿠키가 루트 도메인의 모든 서브도메인에서 작동하도록 config/session.php 파일의 도메인 설정 앞에 .을 붙입니다:
'domain' => '.domain.com',
인증 처리
CSRF 보호
SPA 로그인 페이지에서 인증을 시작하기 전에, 먼저 /sanctum/csrf-cookie 엔드포인트에 요청을 보내 CSRF 보호를 초기화해야 합니다:
axios.get('/sanctum/csrf-cookie').then(response => {
// 로그인 처리...
});이 요청 시 Laravel은 현재 CSRF 토큰을 담은 XSRF-TOKEN 쿠키를 설정합니다. 이후 요청에서는 이 토큰을 X-XSRF-TOKEN 헤더에 포함해야 하며, Axios나 Angular의 HttpClient 같은 라이브러리는 이를 자동으로 처리합니다. 자동 처리가 되지 않는 라이브러리를 사용한다면 XSRF-TOKEN 쿠키 값을 읽어 X-XSRF-TOKEN 헤더에 직접 설정해야 합니다.
로그인
CSRF 보호가 초기화되면, Laravel 애플리케이션의 /login 라우트로 POST 요청을 보냅니다. 이 /login 라우트는 직접 구현하거나 Laravel Fortify 같은 헤드리스 인증 패키지를 사용해 구성할 수 있습니다.
로그인 요청이 성공하면 세션 쿠키를 통해 인증이 유지되며, 이후의 모든 요청은 자동으로 인증됩니다. 또한 /sanctum/csrf-cookie 요청을 이미 했으므로, JavaScript HTTP 클라이언트가 XSRF-TOKEN 쿠키 값을 X-XSRF-TOKEN 헤더로 전송하는 한 이후 요청도 자동으로 CSRF 보호를 받습니다.
세션이 비활성으로 인해 만료되면 이후 요청에서 401 또는 419 HTTP 에러가 반환됩니다. 이 경우 SPA의 로그인 페이지로 사용자를 리다이렉트해야 합니다.
WARNING
/login 엔드포인트를 직접 구현할 때는 반드시 Laravel의 표준 세션 기반 인증 서비스를 사용해야 합니다. 일반적으로 web 인증 가드를 사용하면 됩니다.
라우트 보호
SPA에서 오는 모든 요청에 인증을 요구하려면, routes/api.php의 API 라우트에 sanctum 인증 가드를 적용합니다. 이 가드는 SPA의 스테이트풀 인증 요청 또는 서드파티의 유효한 API 토큰 헤더가 있는 요청을 통과시킵니다:
use Illuminate\Http\Request;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});프라이빗 브로드캐스트 채널 권한 부여
SPA에서 프라이빗/프레즌스 브로드캐스트 채널을 인증해야 한다면, routes/api.php 파일에 Broadcast::routes 메서드 호출을 배치합니다:
Broadcast::routes(['middleware' => ['auth:sanctum']]);
다음으로, Pusher의 권한 부여 요청이 성공하려면 Laravel Echo 초기화 시 커스텀 Pusher authorizer를 제공해야 합니다. 이를 통해 크로스 도메인 요청에 맞게 설정된 axios 인스턴스를 사용하도록 Pusher를 구성할 수 있습니다:
window.Echo = new Echo({
broadcaster: "pusher",
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
encrypted: true,
key: import.meta.env.VITE_PUSHER_APP_KEY,
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(false, response.data);
})
.catch(error => {
callback(true, error);
});
}
};
},
})모바일 앱 인증
Sanctum 토큰을 사용해 모바일 앱의 API 요청도 인증할 수 있습니다. 모바일 앱 인증 방식은 서드파티 API 요청 인증과 유사하지만, 토큰 발급 방식에 약간의 차이가 있습니다.
API 토큰 발급
사용자의 이메일/사용자명, 비밀번호, 기기명을 받아 새 Sanctum 토큰을 반환하는 라우트를 만듭니다. "기기명"은 정보 표시 목적으로 사용되며 어떤 값이든 사용할 수 있습니다. 일반적으로 사용자가 알아볼 수 있는 이름(예: "홍길동의 Galaxy S24")으로 지정합니다.
모바일 앱의 로그인 화면에서 이 엔드포인트로 요청을 보내면, 반환된 평문 API 토큰을 기기에 저장하고 이후 API 요청에 활용할 수 있습니다:
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
Route::post('/sanctum/token', function (Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (! $user || ! Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['입력한 인증 정보가 올바르지 않습니다.'],
]);
}
return $user->createToken($request->device_name)->plainTextToken;
});모바일 앱은 이 토큰을 Authorization 헤더에 Bearer 토큰으로 포함해 API 요청을 보내면 됩니다.
NOTE
모바일 앱용 토큰을 발급할 때도 토큰 권한(abilities)을 자유롭게 지정할 수 있습니다.
라우트 보호
앞서 설명한 것과 동일하게, sanctum 인증 가드를 라우트에 적용해 모든 요청에 인증을 요구할 수 있습니다:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});토큰 폐기
웹 애플리케이션의 "계정 설정" 화면에서 모바일 기기에 발급된 토큰 목록을 기기명과 함께 보여주고, 각 토큰 옆에 "폐기" 버튼을 제공하면 좋습니다. 사용자가 "폐기" 버튼을 클릭하면 데이터베이스에서 해당 토큰을 삭제합니다. Laravel\Sanctum\HasApiTokens 트레이트의 tokens 관계를 통해 사용자의 API 토큰에 접근할 수 있습니다:
// 모든 토큰 폐기...
$user->tokens()->delete();
// 특정 토큰 폐기...
$user->tokens()->where('id', $tokenId)->delete();테스트
테스트 시에는 Sanctum::actingAs 메서드를 사용해 사용자를 인증하고 토큰에 부여할 권한을 지정할 수 있습니다:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
public function test_task_list_can_be_retrieved(): void
{
Sanctum::actingAs(
User::factory()->create(),
['view-tasks']
);
$response = $this->get('/api/task');
$response->assertOk();
}토큰에 모든 권한을 부여하려면 actingAs 메서드의 권한 목록에 *를 포함합니다:
Sanctum::actingAs(
User::factory()->create(),
['*']
);