> ## 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의 검증 기능을 사용해, 폼의 입력 데이터를 검증하는 방법을 설명합니다.

## 검증이란

검증이란, 사용자로부터 송신된 데이터가 기대하는 형식이나 조건을 만족하고 있는지를 검증하는 것입니다.
Laravel은 리퀘스트 오브젝트의 `validate` 메서드나 전용의 폼 리퀘스트 클래스를 사용한, 심플하고 강력한 검증 기능을 제공하고 있습니다.

```mermaid theme={null}
flowchart TD
    A["HTTP 리퀘스트 수신"] --> B["검증 규칙 적용"]
    B --> C{{"검증<br>통과?"}}
    C -->|"성공"| D["검증 완료 데이터 취득"]
    D --> E["컨트롤러 로직 실행"]
    E --> F["성공 응답"]
    C -->|"실패<br>(Web 리퀘스트)"| G["이전 화면으로 리다이렉트<br>에러를 세션에 보존"]
    C -->|"실패<br>(API 리퀘스트)"| H["422 Unprocessable Entity<br>JSON 에러 응답"]
```

## 컨트롤러에서의 검증

### `$request->validate()`의 사용법

컨트롤러의 메서드 내에서 `$request->validate()`를 호출하는 것이, 가장 심플한 검증 방법입니다.
검증에 실패하면, Laravel은 사용자를 이전 화면으로 자동으로 리다이렉트하고, 에러 정보를 세션에 보존합니다.

라우트를 정의합니다.

```php theme={null}
use App\Http\Controllers\PostController;

Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);
```

컨트롤러를 작성합니다.

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PostController extends Controller
{
    public function create(): View
    {
        return view('post.create');
    }

    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'email' => 'required|email',
        ]);

        // 검증 완료 데이터로 투고를 보존...

        return redirect('/posts');
    }
}
```

`validate` 메서드에는 필드명을 키, 규칙을 값으로 한 배열을 전달합니다.
규칙은 파이프 `|`로 구분하거나, 배열로 지정할 수 있습니다.

```php theme={null}
// 파이프 구분
'title' => 'required|string|max:255',

// 배열 형식
'title' => ['required', 'string', 'max:255'],
```

### 주요 검증 규칙

| 규칙           | 설명                                 |
| ------------ | ---------------------------------- |
| `required`   | 값이 존재하고, 비어 있지 않을 것                |
| `string`     | 문자열일 것                             |
| `email`      | 유효한 메일 주소 형식일 것                    |
| `min:값`      | 문자열의 길이, 또는 수치가 지정 값 이상일 것         |
| `max:값`      | 문자열의 길이, 또는 수치가 지정 값 이하일 것         |
| `unique:테이블` | 지정한 테이블에서 값이 일의일 것                 |
| `nullable`   | 값이 `null`이어도 허가                    |
| `integer`    | 정수일 것                              |
| `boolean`    | 진위값 (`true`/`false`, `1`/`0` 등)일 것 |
| `date`       | 유효한 날짜 형식일 것                       |
| `confirmed`  | `필드명_confirmation` 필드와 일치할 것       |

<Info>
  이용 가능한 모든 검증 규칙은 [공식 문서](https://laravel.com/docs/validation#available-validation-rules)에서 확인할 수 있습니다.
</Info>

### 검증 완료 데이터의 이용

`validate` 메서드의 반환값은, 검증에 통과한 데이터만을 포함하는 배열입니다.
신뢰할 수 있는 데이터로서 안전하게 사용할 수 있습니다.

```php theme={null}
$validated = $request->validate([
    'title' => 'required|string|max:255',
    'body'  => 'required|string',
]);

// $validated는 ['title' => '...', 'body' => '...']의 형식
Post::create($validated);
```

## Blade 템플릿에서의 에러 표시

검증에 실패하면, Laravel은 `$errors` 변수를 모든 뷰에서 자동적으로 이용할 수 있도록 합니다.
`web` 미들웨어 그룹에 포함되는 `ShareErrorsFromSession` 미들웨어가 이를 담당하고 있습니다.

### 모든 에러를 일람 표시

```blade theme={null}
@if ($errors->any())
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif
```

### 필드별로 에러를 표시

`@error` 디렉티브를 사용하면, 특정 필드의 에러를 인라인으로 표시할 수 있습니다.

```blade theme={null}
<label for="title">타이틀</label>

<input
    id="title"
    type="text"
    name="title"
    value="{{ old('title') }}"
    class="@error('title') is-invalid @enderror"
/>

@error('title')
    <div class="error-message">{{ $message }}</div>
@enderror
```

<Tip>
  `old('필드명')`을 사용하면, 검증 실패 후에도 사용자가 입력한 값을 폼에 재표시할 수 있습니다.
</Tip>

## 폼 리퀘스트

### 폼 리퀘스트란

검증 로직이 복잡해진 경우는, "폼 리퀘스트" 클래스에 잘라 내는 것이 효과적입니다.
폼 리퀘스트는 검증과 인가의 로직을 하나의 클래스에 정리한 커스텀 리퀘스트 클래스입니다.

```mermaid theme={null}
flowchart TD
    A["컨트롤러 메서드 호출"] --> B["FormRequest의 인스턴스 생성"]
    B --> C["authorize() 메서드 실행"]
    C --> D{{"인가 OK?"}}
    D -->|"false"| E["403 Forbidden 응답"]
    D -->|"true"| F["rules() 메서드 실행"]
    F --> G["검증 실행"]
    G --> H{{"검증<br>통과?"}}
    H -->|"실패"| I["검증<br>에러 응답"]
    H -->|"성공"| J["컨트롤러 메서드 실행"]
```

### 폼 리퀘스트의 작성

`make:request` Artisan 명령으로 폼 리퀘스트 클래스를 생성합니다.

```shell theme={null}
php artisan make:request StorePostRequest
```

`app/Http/Requests/StorePostRequest.php`가 생성됩니다.

```php theme={null}
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * 이 리퀘스트를 실행할 권한이 있는지 판정한다
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * 이 리퀘스트에 적용할 검증 규칙을 반환한다
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
            'email' => 'required|email|unique:posts',
        ];
    }
}
```

### `rules()` 메서드

검증 규칙을 배열로 반환합니다. 컨트롤러의 `validate` 메서드에 전달하는 배열과 같은 형식입니다.

### `authorize()` 메서드

이 리퀘스트를 실행할 권한이 있는지를 판정합니다.
`true`를 반환하면 인가 OK, `false`를 반환하면 403 응답이 자동적으로 반환됩니다.

인증된 사용자만 허가하는 경우, `auth()->check()`로 확인할 수 있습니다.
튜토리얼에서는 `true`를 반환해 두면 문제없습니다.

<Warning>
  `authorize` 메서드가 `false`를 반환하면, 컨트롤러의 메서드는 실행되지 않고, 403 Forbidden 응답이 반환됩니다.
</Warning>

### 컨트롤러에의 주입

폼 리퀘스트는 컨트롤러 메서드의 타입 힌트로서 지정하는 것만으로 사용할 수 있습니다.
컨트롤러가 호출되기 전에 검증이 실행되기 때문에, 메서드 내에 검증 코드를 작성할 필요는 없습니다.

```php theme={null}
use App\Http\Requests\StorePostRequest;

class PostController extends Controller
{
    public function store(StorePostRequest $request): RedirectResponse
    {
        // 여기에 도달한 시점에서 검증 완료

        $validated = $request->validated();

        Post::create($validated);

        return redirect('/posts');
    }
}
```

`$request->validated()`로 검증 완료 데이터만을 취득할 수 있습니다.

## 실천 예: 투고 폼의 작성과 보존

폼의 표시부터 송신·검증·보존까지의 일련의 플로우입니다.

<Steps>
  <Step title="라우트의 정의">
    ```php theme={null}
    use App\Http\Controllers\PostController;

    Route::get('/posts/create', [PostController::class, 'create']);
    Route::post('/posts', [PostController::class, 'store']);
    ```
  </Step>

  <Step title="폼 리퀘스트의 작성">
    ```shell theme={null}
    php artisan make:request StorePostRequest
    ```

    ```php theme={null}
    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'body'  => 'required|string',
        ];
    }

    public function authorize(): bool
    {
        return true;
    }
    ```
  </Step>

  <Step title="컨트롤러의 구현">
    ```php theme={null}
    use App\Http\Requests\StorePostRequest;
    use App\Models\Post;

    class PostController extends Controller
    {
        public function create(): View
        {
            return view('posts.create');
        }

        public function store(StorePostRequest $request): RedirectResponse
        {
            Post::create($request->validated());

            return redirect('/posts');
        }
    }
    ```
  </Step>

  <Step title="Blade 템플릿의 작성">
    ```blade theme={null}
    {{-- resources/views/posts/create.blade.php --}}

    <h1>새로운 투고</h1>

    @if ($errors->any())
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    @endif

    <form method="POST" action="/posts">
        @csrf

        <div>
            <label for="title">타이틀</label>
            <input
                id="title"
                type="text"
                name="title"
                value="{{ old('title') }}"
            />
            @error('title')
                <span>{{ $message }}</span>
            @enderror
        </div>

        <div>
            <label for="body">본문</label>
            <textarea id="body" name="body">{{ old('body') }}</textarea>
            @error('body')
                <span>{{ $message }}</span>
            @enderror
        </div>

        <button type="submit">투고하기</button>
    </form>
    ```
  </Step>
</Steps>

<Info>
  Blade 폼에는 반드시 `@csrf` 디렉티브를 포함해 주세요. CSRF 토큰이 없으면 Laravel은 419 에러를 반환합니다.
</Info>

## 다음 단계

<Card title="HTTP 리퀘스트" icon="arrow-up-from-bracket" href="/ko/requests">
  리퀘스트 오브젝트를 사용한 데이터 취득 방법을 되돌아봅니다.
</Card>


## Related topics

- [Laravel Prompts](/ko/prompts.md)
- [검증(Verify)](/ko/packages/laravel-bluesky/verify.md)
- [커스텀 검증 규칙](/ko/advanced/custom-validation-rules.md)
- [CSRF 보호](/ko/csrf.md)
- [⚡Livewire 4 입문 — JavaScript 없이 리액티브한 UI 만들기](/ko/blog/livewire-introduction.md)
