> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel 9에서 10으로의 업그레이드

> Laravel 9.x에서 10.x로의 업그레이드 절차와 주요 변경점 설명

## 시작하며

Laravel 10은 2023년 2월 14일에 릴리스되었습니다. 이 가이드에서는 Laravel 9.x에서 10.x로의 업그레이드 절차를 설명합니다.

<Info>
  업그레이드에 필요한 예상 시간은 **약 10분**입니다. 다만 파괴적 변경이 애플리케이션에 미치는 영향은 규모나 사용하고 있는 기능에 따라 다릅니다.
</Info>

### Laravel Shift를 사용한 자동 업그레이드

[Laravel Shift](https://laravelshift.com/)를 사용하여 업그레이드를 자동화할 수도 있습니다. Shift는 애플리케이션의 의존관계나 설정 파일을 자동으로 갱신해 줍니다.

***

## 영향도별 변경점

### 영향도: 높음

* 의존관계 갱신
* minimum-stability 갱신

### 영향도: 중간

* 데이터베이스 식 변경
* 모델의 `$dates` 프로퍼티 삭제
* Monolog 3
* Redis 캐시 태그
* 서비스 모킹의 변경
* 언어 디렉터리

### 영향도: 낮음

* 클로저 유효성 검증 룰의 메시지
* 폼 리퀘스트의 `after` 메서드
* 퍼블릭 패스 바인딩
* `QueryException` 생성자
* 레이트 리미터의 반환값
* `Redirect::home` 메서드의 삭제
* `Bus::dispatchNow` 메서드의 삭제
* `registerPolicies` 메서드
* ULID 컬럼

***

## 업그레이드 절차

### 의존관계 갱신

**영향도: 높음**

`composer.json`의 이하 의존관계를 갱신해 주세요.

```json theme={null}
{
  "require": {
    "laravel/framework": "^10.0",
    "laravel/sanctum": "^3.2",
    "doctrine/dbal": "^3.0",
    "spatie/laravel-ignition": "^2.0"
  }
}
```

패키지를 사용하고 있는 경우에는 함께 갱신해 주세요.

```json theme={null}
{
  "require": {
    "laravel/passport": "^11.0",
    "laravel/ui": "^4.0"
  }
}
```

PHPUnit 10을 사용하는 경우에는 이하도 갱신해 주세요.

```json theme={null}
{
  "require-dev": {
    "nunomaduro/collision": "^7.0",
    "phpunit/phpunit": "^10.0"
  }
}
```

<Warning>
  PHPUnit 10을 사용하는 경우에는 `phpunit.xml`의 `<coverage>` 섹션에서 `processUncoveredFiles` 속성을 삭제해 주세요.
</Warning>

갱신 후, 이하 명령으로 의존관계를 설치합니다.

```shell theme={null}
composer update
```

Sanctum 2.x에서 3.x로의 업그레이드에는 [Sanctum 3.x 업그레이드 가이드](https://github.com/laravel/sanctum/blob/3.x/UPGRADE.md)도 참고해 주세요.

***

### minimum-stability 갱신

**영향도: 높음**

`composer.json`의 `minimum-stability`를 `stable`로 갱신해 주세요. 기본값이 `stable`이므로 이 설정을 삭제해도 상관없습니다.

```json theme={null}
"minimum-stability": "stable"
```

***

## PHP 버전 요구사항

**영향도: 높음**

Laravel 10에서는 **PHP 8.1.0 이상** 및 **Composer 2.2.0 이상**이 필요합니다.

***

## 파괴적 변경 (Breaking Changes)

### 애플리케이션

#### 퍼블릭 패스 바인딩

**영향도: 낮음**

`path.public`을 컨테이너에 바인딩하여 퍼블릭 패스를 커스터마이즈하고 있는 경우에는, `Illuminate\Foundation\Application`의 `usePublicPath` 메서드를 사용하도록 변경해 주세요.

```php theme={null}
app()->usePublicPath(__DIR__.'/public');
```

***

### 인가

#### `registerPolicies` 메서드

**영향도: 낮음**

`AuthServiceProvider`의 `registerPolicies` 메서드는 프레임워크가 자동으로 호출하도록 되었습니다. `boot` 메서드에서 이 메서드의 호출을 삭제할 수 있습니다.

***

### 캐시

#### Redis 캐시 태그

**영향도: 중간**

`Cache::tags()`는 Memcached를 사용하는 애플리케이션에만 권장됩니다. Redis를 캐시 드라이버로서 사용하고 있는 경우에는 Memcached로의 마이그레이션을 검토해 주세요.

***

### 데이터베이스

#### 데이터베이스 식 변경

**영향도: 중간**

`DB::raw`로 생성되는 데이터베이스 식이 다시 작성되었습니다. 식의 생 문자열 값을 취득하려면 `getValue(Grammar $grammar)` 메서드를 사용할 필요가 있습니다. `(string)`으로의 캐스트는 사용할 수 없게 되었습니다.

```php theme={null}
use Illuminate\Support\Facades\DB;

$expression = DB::raw('select 1');

// 변경 전
$string = (string) $expression;

// 변경 후
$string = $expression->getValue(DB::connection()->getQueryGrammar());
```

최종 사용자의 애플리케이션에는 통상 영향을 주지 않지만, 데이터베이스 식을 문자열로 캐스트하고 있는 경우에는 갱신이 필요합니다.

#### `QueryException` 생성자

**영향도: 매우 낮음**

`Illuminate\Database\QueryException`의 생성자의 첫 번째 인자에 문자열의 접속명이 추가되었습니다. 이 예외를 수동으로 던지고 있는 경우에는 코드를 수정해 주세요.

#### ULID 컬럼

**영향도: 낮음**

마이그레이션에서 인자 없이 `ulid` 메서드를 호출한 경우, 컬럼 이름은 `ulid`가 됩니다. 이전 릴리스에서는 인자 없이 호출한 경우, 잘못하여 `uuid`라는 이름의 컬럼이 작성되고 있었습니다.

```php theme={null}
// 컬럼 이름이 "ulid"가 됨
$table->ulid();

// 컬럼 이름을 명시적으로 지정하는 경우
$table->ulid('ulid');
```

***

### Eloquent

#### 모델의 `$dates` 프로퍼티

**영향도: 중간**

Eloquent 모델의 비권장 프로퍼티 `$dates`가 삭제되었습니다. `$casts` 프로퍼티를 사용해 주세요.

```php theme={null}
// 변경 전
protected $dates = ['deployed_at'];

// 변경 후
protected $casts = [
    'deployed_at' => 'datetime',
];
```

***

### 로컬라이제이션

#### 언어 디렉터리

**영향도: 없음**

기존 애플리케이션에는 영향을 주지 않지만, 신규 Laravel 애플리케이션의 스켈레톤에는 기본으로 `lang` 디렉터리가 포함되지 않게 되었습니다. 필요한 경우 Artisan 명령으로 공개할 수 있습니다.

```shell theme={null}
php artisan lang:publish
```

***

### 로그

#### Monolog 3

**영향도: 중간**

Laravel의 Monolog 의존관계가 Monolog 3.x로 갱신되었습니다. 애플리케이션 내에서 Monolog를 직접 사용하고 있는 경우에는 [Monolog의 업그레이드 가이드](https://github.com/Seldaek/monolog/blob/main/UPGRADE.md)를 확인해 주세요.

BugSnag이나 Rollbar 등의 서드파티 로깅 서비스를 사용하고 있는 경우에는 Monolog 3.x와 Laravel 10.x를 지원하는 버전으로의 업그레이드가 필요한 경우가 있습니다.

<Tip>
  Monolog 3.x의 변경점은 [Monolog 3.x 업그레이드 가이드](https://github.com/Seldaek/monolog/blob/3.x/UPGRADE.md)에서 확인할 수 있습니다.
</Tip>

***

### 큐

#### `Bus::dispatchNow` 메서드의 삭제

**영향도: 낮음**

비권장의 `Bus::dispatchNow`와 `dispatch_now` 메서드가 삭제되었습니다. `Bus::dispatchSync`와 `dispatch_sync` 메서드를 사용해 주세요.

```php theme={null}
// 변경 전
Bus::dispatchNow(new MyJob());
dispatch_now(new MyJob());

// 변경 후
Bus::dispatchSync(new MyJob());
dispatch_sync(new MyJob());
```

***

### 라우팅

#### 미들웨어 별칭

**영향도: 임의**

신규 Laravel 애플리케이션에서는 `App\Http\Kernel` 클래스의 `$routeMiddleware` 프로퍼티가 `$middlewareAliases`로 개명되었습니다. 기존 애플리케이션으로의 적용은 임의입니다.

#### 레이트 리미터의 반환값

**영향도: 낮음**

`RateLimiter::attempt` 메서드를 호출한 경우, 제공한 클로저의 반환값이 그대로 반환되도록 되었습니다. 아무것도 반환하지 않거나 `null`을 반환한 경우에는 `true`가 반환됩니다.

```php theme={null}
$value = RateLimiter::attempt('key', 10, fn () => ['example'], 1);

$value; // ['example']
```

#### `Redirect::home` 메서드의 삭제

**영향도: 매우 낮음**

비권장의 `Redirect::home` 메서드가 삭제되었습니다. 명시적으로 이름 있는 라우트로 리다이렉트하도록 변경해 주세요.

```php theme={null}
// 변경 전
return Redirect::home();

// 변경 후
return Redirect::route('home');
```

***

### 테스트

#### 서비스 모킹

**영향도: 중간**

비권장의 `MocksApplicationServices` 트레이트가 프레임워크에서 삭제되었습니다. 이 트레이트는 `expectsEvents`, `expectsJobs`, `expectsNotifications` 등의 테스트 메서드를 제공하고 있었습니다.

이 메서드들을 사용하고 있는 경우에는 각각 `Event::fake`, `Bus::fake`, `Notification::fake`로 이관해 주세요.

```php theme={null}
// 변경 전
$this->expectsEvents(OrderShipped::class);

// 변경 후
Event::fake();
// ... 테스트 코드 ...
Event::assertDispatched(OrderShipped::class);
```

***

### 유효성 검증

#### 클로저 유효성 검증 룰의 메시지

**영향도: 매우 낮음**

클로저 기반 커스텀 유효성 검증 룰에서 `$fail` 콜백을 여러 번 호출한 경우, 메시지가 덮어써지는 것이 아니라 배열에 추가되도록 되었습니다.

또한 `$fail` 콜백이 오브젝트를 반환하도록 되었습니다. 유효성 검증 클로저의 반환값을 타입 힌트하고 있는 경우에는 갱신이 필요합니다.

```php theme={null}
public function rules()
{
    return [
        'name' => [
            function ($attribute, $value, $fail) {
                $fail('validation.translation.key')->translate();
            },
        ],
    ];
}
```

#### 폼 리퀘스트의 `after` 메서드

**영향도: 매우 낮음**

폼 리퀘스트 내의 `after` 메서드가 Laravel에 의해 예약되었습니다. 폼 리퀘스트에서 `after` 메서드를 정의하고 있는 경우에는, 새로운 "유효성 검증 후" 기능을 활용하도록 리네임 또는 변경해 주세요.

***

## 정리

Laravel 10은 비교적 작은 변경으로 구성되어 있어, 업그레이드는 짧은 시간에 완료할 수 있습니다.

| 변경점                           | 영향도   | 대응                               |
| ----------------------------- | ----- | -------------------------------- |
| `composer.json`의 의존관계 갱신      | 높음    | `laravel/framework ^10.0`으로 변경   |
| PHP 8.1 / Composer 2.2 필수     | 높음    | 버전 확인 · 갱신                       |
| 데이터베이스 식 변경                   | 중간    | `(string)` 캐스트를 `getValue()`로 변경 |
| 모델의 `$dates` 프로퍼티 삭제          | 중간    | `$casts`로 이관                     |
| Monolog 3                     | 중간    | 직접 사용하고 있는 경우는 업그레이드 가이드 확인      |
| Redis 캐시 태그                   | 중간    | Memcached로의 마이그레이션 검토            |
| `MocksApplicationServices` 삭제 | 중간    | `Event::fake` 등으로 이관             |
| `Bus::dispatchNow` 삭제         | 낮음    | `Bus::dispatchSync`로 변경          |
| ULID 컬럼명                      | 낮음    | 마이그레이션의 `ulid()` 호출 확인           |
| `Redirect::home` 삭제           | 매우 낮음 | `Redirect::route('home')`으로 변경   |

***

## 참고 자료

* [공식 업그레이드 가이드 (영어)](https://laravel.com/docs/10.x/upgrade)
* [laravel/laravel 리포지토리의 차분 (9.x → 10.x)](https://github.com/laravel/laravel/compare/9.x...10.x)
* [Laravel Shift](https://laravelshift.com) — 업그레이드를 자동화하는 커뮤니티 서비스
* [Sanctum 업그레이드 가이드 (2.x → 3.x)](https://github.com/laravel/sanctum/blob/3.x/UPGRADE.md)
* [Monolog 3.x 업그레이드 가이드](https://github.com/Seldaek/monolog/blob/3.x/UPGRADE.md)


## Related topics

- [Laravel 10에서 11로의 업그레이드](/ko/blog/upgrade-10-to-11.md)
- [Laravel 12에서 13으로의 업그레이드 가이드](/ko/blog/upgrade-12-to-13.md)
- [Laravel 8에서 9로의 업그레이드](/ko/blog/upgrade-8-to-9.md)
- [Laravel 11에서 12로의 업그레이드 가이드](/ko/blog/upgrade-11-to-12.md)
