본문 바로가기

업그레이드 가이드

업데이트됨

번역일: 2026년 9월 26일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 9월 26일
번역 갱신
2026년 9월 26일

업그레이드 가이드

영향도 높음

영향도 중간

영향도 낮음

12.x에서 13.0으로 업그레이드하기

예상 소요 시간: 10분

NOTE

가능한 모든 하위 호환성 변경 사항을 문서화하려고 노력했습니다. 다만 이 중 일부는 프레임워크의 잘 사용되지 않는 영역에 해당하므로, 실제로 여러분의 애플리케이션에 영향을 미치는 항목은 일부에 불과할 수 있습니다. 시간을 절약하고 싶다면 Shift를 활용해 보세요. Shift는 Laravel 업그레이드를 자동화해주는 커뮤니티 운영 서비스입니다.

AI를 활용한 업그레이드

Laravel Boost를 사용하면 업그레이드 과정을 자동화할 수 있습니다. Boost는 Laravel 공식 MCP 서버로, AI 어시스턴트에게 업그레이드 절차를 안내하는 프롬프트를 제공합니다. Laravel 12 애플리케이션에 Boost를 설치한 뒤, Claude Code·Cursor·OpenCode·Gemini·VS Code에서 /upgrade-laravel-v13 슬래시 명령어를 실행하면 Laravel 13으로의 업그레이드를 시작할 수 있습니다. 이 명령어를 사용하려면 Laravel Boost ^2.0이 필요합니다.

의존성 업데이트

영향 가능성: 높음

애플리케이션의 composer.json 파일에서 다음 의존성을 업데이트해야 합니다:

  • laravel/framework을 ^13.0으로
  • laravel/boost를 ^2.0으로
  • laravel/tinker를 ^3.0으로
  • phpunit/phpunit을 ^12.0으로
  • pestphp/pest를 ^4.0으로

Laravel 인스톨러 업데이트

Laravel 인스톨러 CLI 도구로 새 애플리케이션을 생성하고 있다면, Laravel 13.x와 호환되도록 인스톨러도 업데이트해야 합니다.

composer global require로 인스톨러를 설치했다면 composer global update 명령으로 업데이트할 수 있습니다:

composer global update laravel/installer

혹은 Laravel Herd에 내장된 인스톨러를 사용 중이라면, Herd를 최신 버전으로 업데이트하면 됩니다.

캐시

영향 가능성: 낮음

Laravel의 기본 캐시 및 Redis 키 접두사가 이제 하이픈(-)으로 구분된 형태를 사용합니다.

대부분의 애플리케이션에서는 이미 애플리케이션 설정 파일에서 해당 값을 명시적으로 정의하고 있기 때문에 이 변경의 영향을 받지 않습니다. 이 변경은 해당 설정 값이 없어 프레임워크의 기본값(fallback)에 의존하고 있는 애플리케이션에만 적용됩니다.

기본값에 의존하고 있었다면, 업그레이드 후 캐시 키와 세션 쿠키 이름이 다음과 같이 바뀝니다:

// 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::slug((string) env('APP_NAME', 'laravel')).'-session';

기존 동작을 유지하려면 환경 변수에 CACHE_PREFIX, REDIS_PREFIX, SESSION_COOKIE 값을 명시적으로 설정하세요.

`Store`와 `Repository` 계약: `touch`

영향 가능성: 매우 낮음

캐시 관련 계약(contract)에 항목의 TTL을 연장하는 touch 메서드가 추가되었습니다. 커스텀 캐시 스토어 구현체를 유지 보수하고 있다면 다음 메서드를 추가해야 합니다:

// Illuminate\Contracts\Cache\Store public function touch($key, $seconds);

캐시 `serializable_classes` 설정

영향 가능성: 중간

기본 애플리케이션 cache 설정에 serializable_classes 옵션이 새롭게 추가되었으며, 기본값은 false입니다. 이는 APP_KEY가 유출되었을 때 발생할 수 있는 PHP 역직렬화(deserialization) 가젯 체인 공격을 방지하기 위한 보안 강화 조치입니다. 애플리케이션에서 의도적으로 PHP 객체를 캐시에 저장하고 있다면, 역직렬화를 허용할 클래스 목록을 명시적으로 지정해야 합니다:

'serializable_classes' => [ App\Data\CachedDashboardStats::class, App\Support\CachedPricingSnapshot::class, ],

이전에 임의의 객체를 캐시에서 역직렬화하는 방식에 의존하고 있었다면, 명시적인 클래스 허용 목록 방식이나 배열 등 객체가 아닌 형태의 캐시 페이로드로 마이그레이션해야 합니다.

컨테이너

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

영향 가능성: 낮음

Container::call이 이제 매칭되는 바인딩이 없을 때 Nullable 클래스 파라미터의 기본값을 존중하도록 변경되었습니다. 이는 Laravel 12에서 도입된 생성자 주입 동작과 일치시키기 위함입니다:

$container->call(function (?Carbon $date = null) { return $date; }); // Laravel <= 12.x: Carbon 인스턴스 // Laravel >= 13.x: null

메서드 호출 주입 로직이 기존 동작에 의존하고 있었다면 코드를 수정해야 할 수 있습니다.

계약(Contracts)

`Dispatcher` 계약: `dispatchAfterResponse`

영향 가능성: 매우 낮음

Illuminate\Contracts\Bus\Dispatcher 계약에 dispatchAfterResponse($command, $handler = null) 메서드가 새로 추가되었습니다.

커스텀 디스패처 구현체를 유지 보수하고 있다면 해당 메서드를 클래스에 추가해야 합니다.

`ResponseFactory` 계약: `eventStream`

영향 가능성: 매우 낮음

Illuminate\Contracts\Routing\ResponseFactory 계약에 eventStream 시그니처가 새로 추가되었습니다.

이 계약을 직접 구현하고 있다면 해당 메서드를 추가해야 합니다.

`MustVerifyEmail` 계약: `markEmailAsUnverified`

영향 가능성: 매우 낮음

Illuminate\Contracts\Auth\MustVerifyEmail 계약에 markEmailAsUnverified() 메서드가 추가되었습니다.

이 계약을 직접 구현하고 있다면 호환성을 위해 해당 메서드를 추가하세요.

데이터베이스

MySQL·MariaDB에서의 `upsert`

영향 가능성: 중간

이제 Laravel은 호출자가 uniqueBy에 비어 있지 않은 값을 전달했는지 검증하며, 유효하지 않은 SQL을 생성하는 대신 InvalidArgumentException을 발생시킵니다.

MariaDB와 MySQL 드라이버는 실제로는 uniqueBy 값을 사용하지 않고 테이블의 기본 키와 유니크 인덱스를 기준으로 기존 레코드를 판별하지만, 이 검증 로직 자체는 그대로 적용됩니다. uniqueBy가 비어 있으면 InvalidArgumentException이 발생합니다.

`JOIN`, `ORDER BY`, `LIMIT`이 포함된 MySQL `DELETE` 쿼리

영향 가능성: 낮음

이제 Laravel의 MySQL 그래머는 ORDER BY와 LIMIT을 포함한 전체 DELETE ... JOIN 쿼리를 완전하게 컴파일합니다.

이전 버전에서는 조인이 포함된 삭제 쿼리에서 ORDER BY / LIMIT 절이 조용히 무시될 수 있었습니다. Laravel 13부터는 이 절들이 실제로 생성된 SQL에 포함됩니다. 따라서 해당 문법을 지원하지 않는 데이터베이스 엔진(MySQL, 그리고 11.8.1 이전의 MariaDB)에서는 범위 제한 없는 삭제가 실행되는 대신 QueryException이 발생할 수 있습니다.

Eloquent

모델 부팅 중 중첩 인스턴스화 금지

영향 가능성: 매우 낮음

모델이 아직 부팅(booting) 중인 상태에서 해당 모델의 새 인스턴스를 생성하는 것이 이제 허용되지 않으며, LogicException이 발생합니다.

이는 모델의 boot 메서드나 트레이트의 boot* 메서드 내부에서 모델을 인스턴스화하는 코드에 영향을 줍니다:

protected static function boot() { parent::boot(); // 부팅 중에는 더 이상 허용되지 않음... (new static())->getTable(); }

이런 로직은 부팅 사이클 바깥으로 옮겨서 중첩 부팅이 발생하지 않도록 해야 합니다.

다형성 피벗 테이블 이름 생성 규칙

영향 가능성: 낮음

커스텀 피벗 모델 클래스를 사용하는 다형성 피벗 모델의 테이블 이름을 추론할 때, 이제 Laravel은 복수형 이름을 생성합니다.

기존의 단수형 추론 이름에 의존하며 커스텀 피벗 클래스를 사용하고 있었다면, 피벗 모델에 테이블 이름을 명시적으로 지정해야 합니다.

컬렉션 모델 직렬화 시 즉시 로딩된 관계 복원

영향 가능성: 낮음

Eloquent 모델 컬렉션이 직렬화되었다가 복원될 때(예: 큐에 등록된 Job에서), 이제 컬렉션에 포함된 모델들의 즉시 로딩(eager-loaded)된 관계도 함께 복원됩니다.

역직렬화 후 관계가 로딩되지 않은 상태를 전제로 작성된 코드가 있다면 로직을 조정해야 할 수 있습니다.

HTTP 클라이언트

HTTP 클라이언트 `Response::throw`, `throwIf` 시그니처

영향 가능성: 매우 낮음

HTTP 클라이언트 응답 메서드가 이제 콜백 매개변수를 메서드 시그니처에 명시적으로 선언합니다:

public function throw($callback = null); public function throwIf($condition, $callback = null);

커스텀 응답 클래스에서 이 메서드들을 오버라이드하고 있다면 메서드 시그니처가 호환되는지 확인하세요.

알림(Notifications)

비밀번호 재설정 메일의 기본 제목 변경

영향 가능성: 매우 낮음

Laravel의 기본 비밀번호 재설정 메일 제목이 변경되었습니다:

// Laravel <= 12.x
Reset Password Notification

// Laravel >= 13.x
Reset your password

테스트나 어설션, 번역 오버라이드가 기존 기본 문자열에 의존하고 있다면 이를 갱신해야 합니다.

큐에 등록된 알림과 삭제된 모델 처리

영향 가능성: 매우 낮음

큐에 등록된 알림(notification)이 이제 알림 클래스에 정의된 #[DeleteWhenMissingModels] 어트리뷰트와 $deleteWhenMissingModels 속성을 제대로 인식합니다.

이전 버전에서는 참조하는 모델이 존재하지 않는 경우에도, 원래 의도했던 대로 Job이 삭제 처리되지 않고 실패하는 경우가 있었습니다.

큐

`JobAttempted` 이벤트의 예외 페이로드

영향 가능성: 낮음

Illuminate\Queue\Events\JobAttempted 이벤트는 이제 기존의 boolean 속성인 $exceptionOccurred 대신, 예외 객체(또는 null)를 담는 $exception 속성을 제공합니다:

// Laravel <= 12.x $event->exceptionOccurred; // Laravel >= 13.x $event->exception;

이 이벤트를 리스닝하고 있다면 리스너 코드를 이에 맞게 수정하세요.

`QueueBusy` 이벤트 속성명 변경

영향 가능성: 낮음

Illuminate\Queue\Events\QueueBusy 이벤트의 $connection 속성이 다른 큐 이벤트들과의 일관성을 위해 $connectionName으로 이름이 변경되었습니다.

리스너에서 $connection을 참조하고 있다면 $connectionName으로 수정하세요.

`Queue` 계약에 메서드 추가

영향 가능성: 매우 낮음

Illuminate\Contracts\Queue\Queue 계약에, 기존에는 docblock에만 명시되어 있던 큐 크기 조회 메서드들이 정식으로 추가되었습니다.

이 계약을 구현하는 커스텀 큐 드라이버를 유지 보수하고 있다면 다음 메서드들을 구현해야 합니다:

  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

라우팅

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

영향 가능성: 낮음

이제 도메인이 명시적으로 지정된 라우트가 도메인이 없는 라우트보다 먼저 매칭 대상으로 고려됩니다.

이 변경 덕분에 서브도메인을 포괄적으로 처리하는 라우트가, 도메인 없는 라우트가 먼저 등록되어 있어도 일관되게 동작할 수 있습니다. 애플리케이션이 도메인 라우트와 비도메인 라우트 간의 기존 등록 순서에 의존하고 있었다면 라우트 매칭 동작을 다시 점검해 보세요.

세션

세션 `serialization` 설정

영향 가능성: 낮음

PHP 역직렬화 가젯 체인 공격을 방지하기 위해, 기본 애플리케이션 스켈레톤의 config/session.php 파일에서 세션 serialization 옵션 기본값이 이제 json으로 설정됩니다.

기존 애플리케이션을 업그레이드하면서 설정 파일을 Laravel 13 스켈레톤과 동기화한다면, 이 값을 php에서 json으로 변경하는 순간 현재 활성화된 모든 사용자 세션이 무효화된다는 점에 유의해야 합니다.

업그레이드 과정에서 기존 세션을 끊김 없이 유지하고 싶다면 이 값을 php로 그대로 유지하세요. 반면 애플리케이션이 세션에 PHP 객체를 저장하지 않고, 사용자에게 재로그인을 요구해도 괜찮다면 보안 강화를 위해 json으로 변경하는 것을 권장합니다.

스케줄링

`withScheduling` 등록 시점 변경

영향 가능성: 매우 낮음

ApplicationBuilder::withScheduling()을 통해 등록한 스케줄은 이제 Schedule이 실제로 리졸브(resolve)되는 시점까지 지연되어 등록됩니다.

애플리케이션 부트스트랩 과정에서 스케줄이 즉시 등록되는 기존 타이밍에 의존하고 있었다면 관련 로직을 조정해야 할 수 있습니다.

보안

요청 위조 방지

영향 가능성: 높음

Laravel의 CSRF 미들웨어 이름이 VerifyCsrfToken에서 PreventRequestForgery로 변경되었으며, Sec-Fetch-Site 헤더를 이용한 요청 출처(origin) 검증 기능이 새롭게 추가되었습니다.

VerifyCsrfToken과 ValidateCsrfToken은 하위 호환을 위한 지원 중단 예정(deprecated) 별칭으로 계속 남아 있지만, 특히 테스트나 라우트 정의에서 미들웨어를 제외 처리할 때는 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(...) 메서드가 새로 제공됩니다.

Support

Manager `extend` 콜백 바인딩

영향 가능성: 낮음

Manager의 extend 메서드를 통해 등록한 커스텀 드라이버 클로저가 이제 해당 Manager 인스턴스에 바인딩됩니다.

기존에 이 콜백 내부의 $this가 다른 바인딩된 객체(예: 서비스 프로바이더 인스턴스)를 가리키는 것에 의존하고 있었다면, 해당 값을 use (...) 구문을 통해 클로저 캡처로 옮겨야 합니다.

테스트 간 초기화되는 `Str` 팩토리

영향 가능성: 낮음

이제 Laravel은 각 테스트가 종료될 때마다 커스텀 Str 팩토리를 초기화합니다.

커스텀 UUID / ULID / 랜덤 문자열 팩토리가 여러 테스트 메서드 사이에서 유지되는 것에 의존하고 있었다면, 각 테스트나 setup 훅에서 팩토리를 다시 설정해야 합니다.

`Js::from`의 기본 유니코드 이스케이프 처리 변경

영향 가능성: 매우 낮음

Illuminate\Support\Js::from이 이제 기본적으로 JSON_UNESCAPED_UNICODE 옵션을 사용합니다.

테스트나 프론트엔드 출력 비교 로직이 이스케이프된 유니코드 시퀀스(예: \u00e8)에 의존하고 있었다면 예상 값을 갱신해야 합니다.

유틸리티

Symfony PHP 8.5 폴리필과 전역 함수 충돌

영향 가능성: 낮음

Laravel 13은 symfony/polyfill-php85 패키지에 새롭게 의존합니다. PHP 8.5 미만 버전에서는, 부트스트랩 과정에서 미리 정의되지 않은 경우 이 폴리필이 array_first(), array_last()와 같은 전역 함수를 정의합니다.

이 함수들은 laravel/helpers와 같은 레거시 헬퍼 패키지나, 동일한 이름을 사용하는 커스텀 전역 헬퍼와 충돌할 수 있습니다. 예를 들어, 과거의 array_first() 헬퍼는 콜백을 인자로 받아 조건에 맞는 첫 번째 요소를 반환했지만, 폴리필로 제공되는 버전은 단순히 배열의 첫 번째 요소만 반환합니다.

PHP 버전에 관계없이 일관된 동작을 보장하려면 전역 함수 대신 Illuminate\Support\Arr의 메서드를 사용하는 것을 권장합니다:

use Illuminate\Support\Arr; Arr::first($array, function ($value) { return /* 조건 */; });

뷰

페이지네이션 Bootstrap 뷰 이름

영향 가능성: 낮음

Bootstrap 3 기본 페이지네이션에 사용되던 내부 뷰 이름이 이제 좀 더 명시적으로 바뀌었습니다:

// Laravel <= 12.x
pagination::default
pagination::simple-default

// Laravel >= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3

애플리케이션에서 기존 페이지네이션 뷰 이름을 직접 참조하고 있다면 해당 참조를 갱신해야 합니다.

기타

laravel/laravel GitHub 저장소에서 변경 사항을 직접 살펴보는 것도 권장합니다. 이 변경 사항들이 모두 필수는 아니지만, 여러분의 애플리케이션 파일을 이 저장소와 동기화하고 싶을 수도 있습니다. 이 중 일부는 이 업그레이드 가이드에서 다루었지만, 설정 파일이나 주석의 변경처럼 다루지 않은 부분도 있습니다. GitHub 비교 도구를 사용하면 변경 사항을 쉽게 확인하고, 여러분에게 중요한 항목만 선택적으로 반영할 수 있습니다.

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

번역일: 2026년 9월 26일