메인 콘텐츠로 건너뛰기

시작하며

Laravel 13은 2026년 3월에 릴리스되었습니다. 이 가이드에서는 Laravel 12.x에서 13.x로의 업그레이드 절차를 설명합니다.
업그레이드에 필요한 예상 시간은 약 10분입니다. 다만 파괴적 변경이 애플리케이션에 미치는 영향은 규모나 사용하고 있는 기능에 따라 다릅니다.

AI를 사용한 업그레이드

Laravel Boost를 사용하여 업그레이드를 자동화할 수도 있습니다. Boost는 퍼스트파티 MCP 서버로, AI 어시스턴트에 단계적인 업그레이드 프롬프트를 제공합니다. Laravel 12 애플리케이션에 설치한 후, Claude Code, Cursor, OpenCode, Gemini, VS Code에서 /upgrade-laravel-v13 슬래시 명령을 사용하여 Laravel 13으로의 업그레이드를 시작할 수 있습니다. 이 명령에는 laravel/boost ^2.0이 필요합니다. 슬래시 명령을 사용할 수 없는 AI 도구에서도 프롬프트 파일을 직접 참조함으로써 같은 업그레이드 절차를 실행할 수 있습니다. 아래의 프롬프트를 그대로 AI에 붙여 넣어 주세요.
프롬프트
Laravel Boost가 제공하고 있는 프롬프트를 로드하여 Laravel 12에서 13으로의 업그레이드 작업을 수행해 주세요.
https://raw.githubusercontent.com/laravel/boost/refs/heads/main/src/Mcp/Prompts/UpgradeLaravelv13/upgrade-laravel-v13.blade.php

- `laravel/laravel` 스켈레톤의 변경점을 반영시킨다. master가 아니라 13.x 브랜치를 확인한다.
- `database/migrations/*_create_cache_table.php`는 그대로 변경이 아니라 새 마이그레이션을 작성.
- `config/session.php`의 `serialization`은 `php`로 설정. `'serialization' => 'php'`
- Laravel 13에서 가장 앱에 오류를 발생시키는 변경은 캐시의 동작 변경이므로 프로젝트 내 캐시 사용 부분을 검색하여 허가해도 문제 없을 것 같으면 config/cache.php의 `serializable_classes`에서 허가하도록 갱신.

'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
    Illuminate\Support\Collection::class,
    Illuminate\Database\Eloquent\Collection::class,
],

영향도별 변경점

영향도: 높음

  • 의존관계 갱신
  • Laravel 인스톨러 갱신
  • 요청 위조 방지 (CSRF)

영향도: 중간

  • 캐시 serializable_classes 설정
  • 세션 serialization 설정

영향도: 낮음

  • 캐시 프리픽스와 세션 쿠키 이름
  • 컬렉션 모델 시리얼라이제이션
  • Container::call과 Nullable 클래스 기본값
  • 도메인 라우트 등록의 우선순위
  • JobAttempted 이벤트의 예외 페이로드
  • Manager extend 콜백의 바인딩
  • MySQL DELETE 쿼리 (JOIN / ORDER BY / LIMIT)
  • 페이지네이션 Bootstrap 뷰 이름
  • 폴리모픽 피벗 테이블 이름의 생성
  • QueueBusy 이벤트의 프로퍼티 이름 변경
  • Str 팩토리의 테스트 간 리셋

업그레이드 절차

의존관계 갱신

영향도: 높음 composer.json의 이하 의존관계를 갱신해 주세요.
{
  "require": {
    "laravel/framework": "^13.0",
    "laravel/tinker": "^3.0"
  },
  "require-dev": {
    "phpunit/phpunit": "^12.0",
    "pestphp/pest": "^4.0"
  }
}
Laravel Boost를 사용하고 있는 경우에는 함께 갱신합니다.
{
  "require": {
    "laravel/boost": "^2.0"
  }
}
갱신 후, 이하 명령으로 의존관계를 설치합니다.
composer update

Laravel 인스톨러 갱신

영향도: 높음 Laravel 인스톨러 CLI를 사용하여 새 Laravel 애플리케이션을 만드는 경우, Laravel 13.x 대응 버전으로 갱신해 주세요. composer global require로 설치한 경우:
composer global update laravel/installer
Laravel Herd의 번들 버전을 사용하고 있는 경우에는 Herd 자체를 최신 릴리스로 갱신해 주세요.

파괴적 변경 (Breaking Changes)

보안

요청 위조 방지

영향도: 높음 Laravel의 CSRF 미들웨어가 VerifyCsrfToken에서 PreventRequestForgery로 리네임되었습니다. 또한 Sec-Fetch-Site 헤더를 이용한 요청 발신처의 검증이 추가되었습니다. VerifyCsrfTokenValidateCsrfToken은 비권장 별칭으로 남아 있지만, 직접 참조하고 있는 부분은 모두 PreventRequestForgery로 갱신할 필요가 있습니다. 특히 테스트나 라우트 정의에서 미들웨어를 제외하고 있는 경우에는 주의가 필요합니다.
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;

// Laravel <= 12.x
->withoutMiddleware([VerifyCsrfToken::class]);

// Laravel >= 13.x
->withoutMiddleware([PreventRequestForgery::class]);
미들웨어 설정 API에서도 preventRequestForgery(...)가 사용 가능해졌습니다.

캐시

캐시 프리픽스와 세션 쿠키 이름

영향도: 낮음 Laravel의 기본 캐시 및 Redis 키 프리픽스가 하이픈 구분의 서픽스를 사용하도록 되었습니다. 또한 기본 세션 쿠키 이름이 Str::snake(...)를 사용하도록 되었습니다. 많은 애플리케이션에서는 설정 파일에서 명시적으로 값을 설정하고 있으므로, 이 변경의 영향을 받지 않습니다. 프레임워크의 폴백 설정에 의존하고 있는 애플리케이션만 영향을 받습니다.
// Laravel <= 12.x
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_cache_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_database_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session';

// Laravel >= 13.x
Str::slug((string) env('APP_NAME', 'laravel')).'-cache-';
Str::slug((string) env('APP_NAME', 'laravel')).'-database-';
Str::snake((string) env('APP_NAME', 'laravel')).'_session';
이전의 동작을 유지하려면, .env 파일에서 명시적으로 설정해 주세요.
CACHE_PREFIX=myapp_cache_
REDIS_PREFIX=myapp_database_
SESSION_COOKIE=myapp_session

캐시 serializable_classes 설정

영향도: 중간 기본 cache 설정에 serializable_classes 옵션이 추가되었으며, 기본은 false가 되었습니다. 이로 인해 APP_KEY가 유출된 경우의 PHP 디시리얼라이제이션 가젯 체인 공격을 방지합니다. 애플리케이션이 의도적으로 캐시에 PHP 오브젝트를 저장하고 있는 경우, 디시리얼라이즈를 허가할 클래스를 명시적으로 리스트업할 필요가 있습니다.
// config/cache.php
'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
],
임의의 캐시 오브젝트를 디시리얼라이즈하고 있었던 경우에는, 명시적인 클래스 허가 리스트 또는 비오브젝트의 캐시 페이로드(배열 등)로 이관해야 합니다.

세션 serialization 설정

영향도: 중간 Laravel 13의 스켈레톤(laravel/laravel)의 config/session.php'serialization' => 'json'이 추가되어 있습니다. 다만, 프레임워크 내부의 기본은 php 그대로입니다.
AI 도구를 사용하여 스켈레톤의 변경을 그대로 적용한 경우, config/session.php'serialization' => 'json'이 추가되는 경우가 있습니다. 이 변경은 세션의 시리얼라이제이션 방식을 전환하므로, 세션에 PHP 오브젝트를 저장하고 있는 애플리케이션에서 오류가 발생할 가능성이 있습니다.
공식 업그레이드 가이드에는 이 설정의 변경이 기재되어 있지 않습니다. 이것은 변경하지 않아도 된다는 의도로 생각됩니다. Laravel에서는 하나 이전 버전에서의 업그레이드에서는 영향이 없으므로 업그레이드 가이드에 기재되지 않는 변경이 때때로 있습니다. 몇 년 후에 업그레이드하려고 했을 때 정보가 없어 곤란해지므로 비공식적인 기록이 중요합니다. Laravel 12까지와 같은 동작을 유지하려면, 'serialization' => 'php'를 명시적으로 설정해 주세요.
// config/session.php
'serialization' => 'php',
JSON 시리얼라이제이션을 활성화하는 경우에는, 세션에 PHP 오브젝트를 저장하고 있지 않은 것을 사전에 확인해 주세요.

컨테이너

Container::call과 Nullable 클래스 기본값

영향도: 낮음 Container::call이, 바인딩이 존재하지 않는 경우에 nullable 클래스 파라미터의 기본값을 존중하도록 되었습니다 (Laravel 12에서 생성자 인젝션에 도입된 동작과 일치합니다).
$container->call(function (?Carbon $date = null) {
    return $date;
});

// Laravel <= 12.x: Carbon 인스턴스 반환
// Laravel >= 13.x: null 반환

데이터베이스

MySQL DELETE 쿼리

영향도: 낮음 Laravel이 MySQL 문법에서 ORDER BYLIMIT을 포함하는 완전한 DELETE ... JOIN 쿼리를 컴파일하도록 되었습니다. 이전 버전에서는 JOIN을 동반하는 DELETE에서 ORDER BY / LIMIT 절이 무시되는 경우가 있었습니다. Laravel 13에서는 이 절들이 생성되는 SQL에 포함됩니다. 그 결과, 이 구문을 지원하지 않는 일부 데이터베이스 엔진에서 QueryException이 발생할 가능성이 있습니다.

Eloquent

폴리모픽 피벗 테이블 이름의 생성

영향도: 낮음 커스텀 피벗 모델 클래스를 사용하는 폴리모픽 피벗 모델의 테이블 이름이 추측될 때, Laravel이 복수형 이름을 생성하도록 되었습니다. 이전의 단수형 추측 이름에 의존하고 있었던 경우에는 피벗 모델에서 테이블 이름을 명시적으로 정의해 주세요.
class RoleUser extends MorphPivot
{
    protected $table = 'role_user'; // 명시적으로 지정
}

컬렉션 모델 시리얼라이제이션

영향도: 낮음 Eloquent 모델 컬렉션이 시리얼라이즈 및 리스토어(큐 투입된 잡 등)될 때, Eager 로드된 릴레이션이 모델을 위해 복원되도록 되었습니다. 디시리얼라이제이션 후에 릴레이션이 존재하지 않는 것에 의존하고 있었던 코드가 있는 경우에는 수정이 필요합니다.

JobAttempted 이벤트의 예외 페이로드

영향도: 낮음 Illuminate\Queue\Events\JobAttempted 이벤트가 이전의 boolean인 $exceptionOccurred 프로퍼티 대신에, 예외 오브젝트(또는 null)를 $exception으로 공개하도록 되었습니다.
// Laravel <= 12.x
if ($event->exceptionOccurred) {
    // 예외가 발생했다
}

// Laravel >= 13.x
if ($event->exception !== null) {
    // 예외가 발생했다
    $exception = $event->exception;
}

QueueBusy 이벤트의 프로퍼티 이름 변경

영향도: 낮음 Illuminate\Queue\Events\QueueBusy 이벤트의 프로퍼티 $connection이, 다른 큐 이벤트와의 일관성을 위해 $connectionName으로 리네임되었습니다.
// Laravel <= 12.x
$event->connection;

// Laravel >= 13.x
$event->connectionName;

라우팅

도메인 라우트 등록의 우선순위

영향도: 낮음 명시적인 도메인을 가지는 라우트가 라우트 매칭에서 비도메인 라우트보다 우선되도록 되었습니다. 이로 인해 캐치올 서브도메인 라우트가, 비도메인 라우트가 먼저 등록되어 있는 경우에도 일관되게 동작하도록 됩니다.

서포트

Manager extend 콜백의 바인딩

영향도: 낮음 Manager의 extend 메서드로 등록된 커스텀 드라이버의 클로저가, 매니저 인스턴스에 바인딩되도록 되었습니다. 이전에는 이 콜백들 내에서 별도의 오브젝트(서비스 프로바이더 인스턴스 등)가 $this로서 참조되고 있었던 경우, use (...)로 값을 클로저 캡처에 이동시켜야 합니다.
// Laravel <= 12.x
Manager::extend('custom', function ($app) {
    return $this->createCustomDriver($app); // $this는 서비스 프로바이더
});

// Laravel >= 13.x
$provider = $this;
Manager::extend('custom', function ($app) use ($provider) {
    return $provider->createCustomDriver($app);
});

Str 팩토리의 테스트 간 리셋

영향도: 낮음 Laravel이 테스트의 티어다운 시에 커스텀 Str 팩토리를 리셋하도록 되었습니다. 커스텀 UUID / ULID / 랜덤 문자열 팩토리가 테스트 메서드 간에 지속되는 것에 의존하고 있었던 경우에는, 관련된 각 테스트 또는 셋업 훅에서 설정하도록 해 주세요.

페이지네이션 Bootstrap 뷰 이름

영향도: 낮음 Bootstrap 3 기본의 내부 페이지네이션 뷰 이름이 명시적으로 되었습니다.
// Laravel <= 12.x
pagination::default
pagination::simple-default

// Laravel >= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3
오래된 페이지네이션 뷰 이름을 직접 참조하고 있는 경우에는 갱신해 주세요.

비권장이 된 기능

기능대체 수단
VerifyCsrfToken 미들웨어PreventRequestForgery
ValidateCsrfToken 미들웨어PreventRequestForgery
JobAttempted::$exceptionOccurredJobAttempted::$exception
QueueBusy::$connectionQueueBusy::$connectionName

컨트랙트 추가

영향도: 매우 낮음 커스텀 구현을 가지는 경우에만 영향이 있습니다.

Dispatcher 컨트랙트

Illuminate\Contracts\Bus\Dispatcher 컨트랙트에 dispatchAfterResponse($command, $handler = null) 메서드가 추가되었습니다.

ResponseFactory 컨트랙트

Illuminate\Contracts\Routing\ResponseFactory 컨트랙트에 eventStream 시그니처가 추가되었습니다.

MustVerifyEmail 컨트랙트

Illuminate\Contracts\Auth\MustVerifyEmail 컨트랙트에 markEmailAsUnverified()가 추가되었습니다.

Queue 컨트랙트

Illuminate\Contracts\Queue\Queue 컨트랙트에 이하의 큐 크기 검사 메서드가 추가되었습니다 (이전에는 docblock에서만 선언되어 있었습니다).
  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

Store / Repository 컨트랙트

캐시 컨트랙트에 TTL 연장을 위한 touch 메서드가 추가되었습니다.
// Illuminate\Contracts\Cache\Store
public function touch($key, $seconds);

새 기능의 하이라이트

AI 어시스트 업그레이드 (Laravel Boost)

Laravel Boost는 공식 MCP 서버입니다. AI 에디터와 연동하여 /upgrade-laravel-v13 명령으로 업그레이드를 반자동화할 수 있습니다.

Sec-Fetch-Site 헤더에 의한 요청 발신처 검증

PreventRequestForgery 미들웨어가 Sec-Fetch-Site 헤더를 이용한 추가 오리진 검증을 수행합니다. 이로 인해 CSRF 보호가 강화되어 있습니다.

캐시의 안전한 디시리얼라이제이션

serializable_classes 설정에 의해, 허가된 클래스만이 디시리얼라이즈되도록 되었습니다. PHP 디시리얼라이제이션 공격에 대한 보안이 향상되어 있습니다.

SSE (Server-Sent Events)의 eventStream

ResponseFactory 컨트랙트에 eventStream이 추가되어, Server-Sent Events 지원이 개선되었습니다.

큐 가시성의 향상

Queue 컨트랙트의 pendingSize, delayedSize, reservedSize 등의 메서드에 의해, 큐의 상태를 보다 세밀하게 모니터링할 수 있게 되었습니다.

자주 있는 마이그레이션 시의 문제와 해결책

문제: CSRF 관련 테스트가 실패한다

증상: VerifyCsrfToken을 참조하고 있는 테스트가 클래스가 발견되지 않는 오류로 실패한다. 해결책: 모든 참조를 PreventRequestForgery로 갱신해 주세요.
// Before
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
->withoutMiddleware([VerifyCsrfToken::class]);

// After
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
->withoutMiddleware([PreventRequestForgery::class]);

문제: 캐시에서 오브젝트를 복원할 수 없다

증상: 캐시에서 취득한 데이터가 null이 되거나, UnserializationFailedException이 발생한다. 해결책: config/cache.php에서 serializable_classes에 사용할 클래스를 추가하거나, 캐시하는 값을 배열로 변환해 주세요.
'serializable_classes' => [
    App\Models\User::class,
    App\Data\SomeData::class,
],

문제: JobAttempted 리스너가 기능하지 않는다

증상: $event->exceptionOccurrednull이나 미정의 오류가 된다. 해결책: $event->exception !== null로 변경해 주세요.
// Before
if ($event->exceptionOccurred) { ... }

// After
if ($event->exception !== null) { ... }

문제: 세션이 무효화된다

증상: 업그레이드 후에 사용자가 로그아웃된다. 해결책: 기본 세션 쿠키 이름이 변경되어 있습니다. .env에서 SESSION_COOKIE를 명시적으로 설정하여 이전 값을 유지해 주세요.
SESSION_COOKIE=laravel_session

문제: 캐시 키가 발견되지 않는다

증상: 업그레이드 후에 캐시 미스가 증가한다. 해결책: 캐시 프리픽스가 변경되어 있습니다. .env에서 CACHE_PREFIX를 설정하거나, 캐시를 클리어해 주세요.
php artisan cache:clear

참고 자료

마지막 수정일 2026년 7월 13일