Laravel 유효성 검증

Jmnote (토론 | 기여)님의 2024년 6월 20일 (목) 21:28 판 (→‎폼 요청 인가)

1 개요

Laravel Validation
라라벨 밸리데이션, 유효성 검증, 유효성 검사

https://laravel.com/docs/11.x/validation

Crystal Clear action info.png 작성 중인 문서입니다.


2 소개

Laravel은 애플리케이션으로 들어오는 데이터를 검증하기 위해 여러 가지 접근 방식을 제공합니다. 가장 일반적으로는 모든 들어오는 HTTP 요청에서 사용할 수 있는 validate 메소드를 사용하는 방법이 있습니다. 그러나 여기서는 다른 검증 접근 방식들도 함께 논의할 것입니다.

Laravel은 데이터에 적용할 수 있는 다양한 편리한 검증 규칙을 포함하고 있으며, 특정 데이터베이스 테이블에서 값이 고유한지 검증할 수 있는 기능도 제공합니다. Laravel의 모든 검증 기능에 익숙해질 수 있도록 각 검증 규칙을 자세히 다룰 것입니다.

3 유효성 검증 빠른 시작

Laravel의 강력한 유효성 검사 기능을 배우기 위해, 폼을 검증하고 사용자에게 오류 메시지를 표시하는 전체 예제를 살펴보겠습니다. 이 고수준 개요를 통해 Laravel을 사용하여 들어오는 요청 데이터를 검증하는 방법에 대한 좋은 일반적인 이해를 얻을 수 있을 것입니다:

3.1 라우트 정의

먼저, routes/web.php 파일에 다음과 같은 라우트가 정의되어 있다고 가정해 보겠습니다:

use App\Http\Controllers\PostController;
 
Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);

GET 라우트는 사용자가 새로운 블로그 포스트를 작성할 수 있는 폼을 표시하고, POST 라우트는 새로운 블로그 포스트를 데이터베이스에 저장합니다.

3.2 컨트롤러 생성

다음으로, 이러한 경로로 들어오는 요청을 처리하는 간단한 컨트롤러를 살펴보겠습니다. store 메소드는 아직 비워둡니다:

<?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
    {
        // 블로그 게시물을 검증하고 저장합니다...

        $post = /** ... */

        return to_route('post.show', ['post' => $post->id]);
    }
}

3.3 유효성 검증 로직 작성

이제 새 블로그 게시물을 유효성 검증할 로직을 store 메소드에 작성할 준비가 되었습니다. 이를 위해 Illuminate\Http\Request 객체에서 제공하는 validate 메소드를 사용할 것입니다. 유효성 검증 규칙을 통과하면 코드가 정상적으로 계속 실행됩니다. 그러나 유효성 검증이 실패하면 Illuminate\Validation\ValidationException 예외가 발생하고 사용자에게 적절한 오류 응답이 자동으로 반환됩니다.

전통적인 HTTP 요청 중 유효성 검증이 실패하면 이전 URL로 리디렉션 응답이 생성됩니다. 들어오는 요청이 XHR 요청인 경우 유효성 검증 오류 메시지를 포함한 JSON 응답이 반환됩니다.

validate 메소드를 더 잘 이해하기 위해 store 메소드로 돌아가 봅시다:

/**
 * 새로운 블로그 게시물 저장.
 */
public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // 블로그 게시물 유효성 검증 통과...

    return redirect('/posts');
}

보다시피, 검증 규칙이 validate 메소드에 전달됩니다. 모든 사용 능한 검증 규칙은 문서화되어 있으니 걱정하지 마십시오. 검증에 실패하면 적절한 응답이 자동으로 생성됩니다. 검증에 성공하면 컨트롤러는 정상적으로 실행을 계속합니다.

또한 검증 규칙은 |로 구분된 문자열 대신 규칙 배열로 지정할 수 있습니다:

$validatedData = $request->validate([
    'title' => ['required', 'unique:posts', 'max:255'],
    'body' => ['required'],
]);


또한, validateWithBag 메소드를 사용하여 요청을 검증하고, 오류 메시지를 명명된 오류 백에 저장할 수 있습니다:

$validatedData = $request->validateWithBag('post', [
    'title' => ['required', 'unique:posts', 'max:255'],
    'body' => ['required'],
]);
첫 번째 유효성 검증 실패 시 중지하기

때로는 첫 번째 유효성 검증 실패 후 해당 속성에 대한 유효성 검증 규칙 실행을 중지하고 싶을 수 있습니다. 이를 위해 속성에 bail 규칙을 할당할 수 있습니다:

$request->validate([
    'title' => 'bail|required|unique:posts|max:255',
    'body' => 'required',
]);

이 예시에서, title 속성의 unique 규칙이 실패하면 max 규칙은 검증하지 않습니다. 규칙은 할당된 순서대로 유효성이 검증됩니다.

중첩된 속성에 관한 노트

만약 들어오는 HTTP 요청에 "중첩된" 필드 데이터가 포함되어 있다면, "점(dot)" 구문을 사용하여 이 필드들을 유효성 검증 규칙을 지정할 수 있습니다:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'author.name' => 'required',
    'author.description' => 'required',
]);

반면에, 필드 이름에 실제 점(점 문자)이 포함된 경우에는, 백슬래시로 점을 이스케이프하여 "점(dot)" 구문으로 해석되는 것을 명시적으로 방지할 수 있습니다:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'v1\.0' => 'required',
]);

3.4 유효성 검증 오류 표시

그래서 들어오는 요청 필드가 주어진 유효성 검증 규칙을 통과하지 못하면 어떻게 될까요? 앞서 언급한 바와 같이, Laravel은 자동으로 사용자를 이전 위치로 리디렉션합니다. 또한, 모든 유효성 검증 오류와 요청 입력이 자동으로 세션에 플래시됩니다.

Illuminate\View\Middleware\ShareErrorsFromSession 미들웨어는 $errors 변수를 애플리케이션의 모든 뷰와 공유하는데, 이 미들웨어는 웹 미들웨어 그룹에 의해 제공됩니다. 이 미들웨어가 적용되면 $errors 변수는 항상 뷰에서 사용할 수 있으며, 이를 통해 $errors 변수가 항상 정의되어 안전하게 사용할 수 있다고 가정할 수 있습니다. $errors 변수는 Illuminate\Support\MessageBag의 인스턴스가 됩니다. 이 객체를 사용하는 방법에 대한 자세한 정보는 해당 문서를 참고하세요.

이 예제에서, 유효성 검증이 실패하면 사용자는 컨트롤러의 create 메소드로 리디렉션되어 뷰에서 오류 메시지를 표시할 수 있게 됩니다:

/resources/views/post/create.blade.php
<h1>Create Post</h1>

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- 게시물 작성  -->
오류 메시지 커스터마이징

Laravel의 빌트인 유효성 검증 규칙 각각에는 애플리케이션의 lang/en/validation.php 파일에 위치한 오류 메시지가 있습니다. 애플리케이션에 lang 디렉토리가 없다면, lang:publish Artisan 명령어를 사용하여 Laravel에게 이를 생성하도록 지시할 수 있습니다.

lang/en/validation.php 파일 안에서는 각 유효성 검증 규칙에 대한 번역 항목을 찾을 수 있습니다. 애플리케이션의 필요에 따라 이 메시지들을 자유롭게 변경하거나 수정할 수 있습니다.

또한, 이 파일을 다른 언어 디렉토리로 복사하여 애플리케이션의 언어에 맞게 메시지를 번역할 수도 있습니다. Laravel 현지화에 대해 더 알고 싶다면, 전체 현지화 문서를 참고하세요.

Warning

기본적으로, Laravel 애플리케이션 스켈레톤에는 lang 디렉토리가 포함되어 있지 않습니다. Laravel의 언어 파일을 커스터마이징하려면, lang:publish Artisan 명령어를 통해 이를 게시할 수 있습니다.

XHR 요청 및 유효성 검증

이 예제에서는 전통적인 폼을 사용하여 데이터를 애플리케이션으로 전송했습니다. 그러나 많은 애플리케이션은 JavaScript 기반 프론트엔드에서 XHR 요청을 받습니다. XHR 요청 중에 validate 메소드를 사용할 때, Laravel은 리디렉션 응답을 생성하지 않습니다. 대신, Laravel은 모든 유효성 검증 오류를 포함하는 JSON 응답을 생성합니다. 이 JSON 응답은 422 HTTP 상태 코드와 함께 전송됩니다.

@error 지시어

@error Blade 지시어를 사용하여 주어진 속성에 대한 유효성 검증 오류 메시지가 존재하는지 빠르게 확인할 수 있습니다. @error 지시어 내에서 $message 변수를 출력하여 오류 메시지를 표시할 수 있습니다:

/resources/views/post/create.blade.php
<label for="title">Post Title</label>

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

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

명명된 오류 백을 사용하는 경우, @error 지시어의 두 번째 인수로 오류 백의 이름을 전달할 수 있습니다:

<input ... class="@error('title', 'post') is-invalid @enderror">

3.5 폼 다시 채우기

Laravel이 유효성 검증 오류로 인해 리디렉션 응답을 생성할 때, 프레임워크는 자동으로 요청의 모든 입력을 세션에 플래시합니다. 이렇게 하면 다음 요청 시 입력을 편리하게 액세스하고 사용자가 제출하려고 했던 폼을 다시 채울 수 있습니다.

이전 요청에서 플래시된 입력을 검색하려면 Illuminate\Http\Request 인스턴스에서 old 메소드를 호출합니다. old 메소드는 세션에서 이전에 플래시된 입력 데이터를 가져옵니다:

$title = $request->old('title');

Laravel은 또한 글로벌 old 헬퍼를 제공합니다. 블레이드 템플릿 내에서 이전 입력을 표시하는 경우, old 헬퍼를 사용하여 폼을 다시 채우는 것이 더 편리합니다. 주어진 필드에 대한 이전 입력이 없는 경우 null이 반환됩니다:

<input type="text" name="title" value="{{ old('title') }}">

3.6 선택적 필드 참고사항

기본적으로, Laravel은 TrimStringsConvertEmptyStringsToNull 미들웨어를 애플리케이션의 글로벌 미들웨어 스택에 포함시킵니다. 이로 인해, null 값을 유효하지 않은 것으로 간주하지 않도록 하려면 "선택적(optional)" 요청 필드를 nullable로 표시해야 할 때가 많습니다. 예를 들면 다음과 같습니다:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'body' => 'required',
    'publish_at' => 'nullable|date',
]);

이 예시에서는 publish_at 필드가 null이거나 유효한 날짜 표현일 수 있음을 명시하고 있습니다. 규칙 정의에 nullable 수정자를 추가하지 않으면, 유효성 검증기는 null을 유효하지 않은 날짜로 간주할 것입니다.

3.7 유효성 검증 오류 응답 형식

애플리케이션에서 Illuminate\Validation\ValidationException 예외가 발생하고 들어오는 HTTP 요청이 JSON 응답을 예상하는 경우, Laravel은 자동으로 오류 메시지를 형식화하여 422 Unprocessable Entity HTTP 응답을 반환합니다.

아래에서는 유효성 검증 오류에 대한 JSON 응답 형식의 예시를 볼 수 있습니다. 중첩된 오류 키는 "점" 표기법 형식으로 평탄화된다는 점에 유의하세요.

{
    "message": "The team name must be a string. (and 4 more errors)",
    "errors": {
        "team_name": [
            "The team name must be a string.",
            "The team name must be at least 1 characters."
        ],
        "authorization.role": [
            "The selected authorization.role is invalid."
        ],
        "users.0.email": [
            "The users.0.email field is required."
        ],
        "users.2.email": [
            "The users.2.email must be a valid email address."
        ]
    }
}

4 폼 요청 유효성 검증

4.1 폼 요청 생성

더 복잡한 검증 시나리오에서는 "폼 요청"을 생성하고 싶을 수 있습니다. 폼 요청은 자체 검증 및 인가 로직을 캡슐화하는 커스텀 요청 클래스입니다. 폼 요청 클래스를 생성하려면 make:request Artisan CLI 명령어를 사용할 수 있습니다:

php artisan make:request StorePostRequest

생성된 폼 요청 클래스는 app/Http/Requests 디렉토리에 배치됩니다. 이 디렉토리가 존재하지 않으면 make:request 명령어를 실행할 때 생성됩니다. Laravel에서 생성된 각 폼 요청에는 authorizerules라는 두 가지 메소드가 있습니다.

authorize 메소드는 현재 인증된 사용자가 요청에서 나타내는 작업을 수행할 수 있는지를 결정하는 역할을 하며, rules 메소드는 요청 데이터에 적용할 검증 규칙을 반환합니다:

/**
 * 요청에 적용할 검증 규칙을 가져옵니다.
 *
 * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
 */
public function rules(): array
{
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];
}

Note

rules 메소드의 시그니처에 필요한 모든 의존성을 타입 힌트할 수 있습니다. 이는 Laravel 서비스 컨테이너를 통해 자동으로 해결됩니다.

그렇다면 검증 규칙은 어떻게 평가될까요? 컨트롤러 메소드에서 요청을 타입 힌트하면 됩니다. 들어오는 폼 요청은 컨트롤러 메소드가 호출되기 전에 검증되므로 컨트롤러에 검증 로직을 추가할 필요가 없습니다:

/**
 * 새 블로그 게시물을 저장합니다.
 */
public function store(StorePostRequest $request): RedirectResponse
{
    // 들어오는 요청은 유효합니다...
 
    // 검증된 입력 데이터를 검색합니다...
    $validated = $request->validated();
 
    // 검증된 입력 데이터의 일부를 검색합니다...
    $validated = $request->safe()->only(['name', 'email']);
    $validated = $request->safe()->except(['name', 'email']);
 
    // 블로그 게시물을 저장합니다...
 
    return redirect('/posts');
}

검증이 실패하면 사용자를 이전 위치로 되돌리기 위해 리디렉션 응답이 생성됩니다. 오류는 세션에 플래시되어 표시할 수 있도록 제공됩니다. 요청이 XHR 요청인 경우, 검증 오류의 JSON 표현을 포함하는 422 상태 코드의 HTTP 응답이 사용자에게 반환됩니다.

Note

Inertia 기반 Laravel 프론트엔드에 실시간 폼 요청 검증을 추가하려고 하나요? Laravel Precognition을 확인해 보세요.

추가 유효성 검증 수행

초기 검증이 완료된 후 추가 검증이 필요할 때가 있습니다. 이를 위해 폼 요청의 after 메소드를 사용할 수 있습니다.

after 메소드는 검증이 완료된 후 호출될 콜러블 또는 클로저 배열을 반환해야 합니다. 지정된 콜러블은 Illuminate\Validation\Validator 인스턴스를 받아 필요 시 추가 오류 메시지를 발생시킬 수 있습니다:

use Illuminate\Validation\Validator;

/**
 * 요청에 대한 "after" 검증 콜러블을 가져옵니다.
 */
public function after(): array
{
    return [
        function (Validator $validator) {
            if ($this->somethingElseIsInvalid()) {
                $validator->errors()->add(
                    'field',
                    'Something is wrong with this field!'
                );
            }
        }
    ];
}

언급된 것처럼, after 메소드가 반환하는 배열에는 호출가능한(invokable) 클래스도 포함될 수 있습니다. 이러한 클래스의 __invoke 메소드는 Illuminate\Validation\Validator 인스턴스를 받게 됩니다:

use App\Validation\ValidateShippingTime;
use App\Validation\ValidateUserStatus;
use Illuminate\Validation\Validator;

/**
 * 요청에 대한 "after" 검증 콜러블을 가져옵니다.
 */
public function after(): array
{
    return [
        new ValidateUserStatus,
        new ValidateShippingTime,
        function (Validator $validator) {
            //
        }
    ];
}
첫 번째 검증 실패 시 중지

요청 클래스에 stopOnFirstFailure 속성을 추가하면 단일 검증 실패가 발생할 때 모든 속성 검증을 중지하도록 유효성 검증기에 알릴 수 있습니다:

/**
 * 유효성 검증기가 첫 번째 규칙 실패 시 중지해야 하는지 여부를 나타냅니다.
 *
 * @var bool
 */
protected $stopOnFirstFailure = true;
리디렉션 위치 커스터마이징

앞서 논의한 바와 같이, 폼 요청 검증이 실패할 때 사용자를 이전 위치로 되돌리기 위한 리디렉션 응답이 생성됩니다. 그러나 이 동작을 커스터마이징할 수 있습니다. 이를 위해 폼 요청에 $redirect 속성을 정의하세요:

/**
 * 검증 실패 시 사용자가 리디렉션되어야 하는 URI입니다.
 *
 * @var string
 */
protected $redirect = '/dashboard';

또는, 사용자를 명명된 라우트로 리디렉션하고 싶다면 $redirectRoute 속성을 정의할 수 있습니다:

/**
 * 검증 실패 시 사용자가 리디렉션되어야 하는 라우트입니다.
 *
 * @var string
 */
protected $redirectRoute = 'dashboard';

4.2 폼 요청 인가

폼 요청 클래스에는 authorize 메소드도 포함되어 있습니다. 이 메소드 내에서 인증된 사용자가 실제로 특정 리소스를 업데이트할 권한이 있는지 확인할 수 있습니다. 예를 들어, 사용자가 업데이트하려고 하는 블로그 댓글을 실제로 소유하고 있는지 확인할 수 있습니다. 대부분의 경우, 이 메소드 내에서 인가 게이트와 정책을 사용하여 상호작용하게 될 것입니다:

use App\Models\Comment;
 
/**
 * 사용자가 이 요청을 수행할 권한이 있는지 확인합니다.
 */
public function authorize(): bool
{
    $comment = Comment::find($this->route('comment'));
 
    return $comment && $this->user()->can('update', $comment);
}

모든 폼 요청은 기본 Laravel 요청 클래스를 확장하므로, user 메소드를 사용하여 현재 인증된 사용자에 접근할 수 있습니다. 또한, 위 예제에서 route 메소드 호출에 주목하세요. 이 메소드는 호출 중인 라우트에 정의된 URI 파라미터에 접근할 수 있게 해줍니다. 예를 들어 아래 예제의 {comment} 파라미터에 접근할 수 있습니다:

Route::post('/comment/{comment}');

따라서, 애플리케이션이 라우트 모델 바인딩을 활용하고 있다면, 요청의 속성으로 해결된 모델에 접근하여 코드를 더욱 간결하게 만들 수 있습니다:

return $this->user()->can('update', $this->comment);

authorize 메소드가 false를 반환하면 HTTP 403 상태 코드가 자동으로 반환되고 컨트롤러 메소드는 실행되지 않습니다.

만약 요청의 인가 로직을 애플리케이션의 다른 부분에서 처리하려는 경우, authorize 메소드를 완전히 제거하거나 단순히 true를 반환할 수 있습니다:

/**
 * 사용자가 이 요청을 수행할 권한이 있는지 확인합니다.
 */
public function authorize(): bool
{
    return true;
}

Note

authorize 메서드의 시그니처 내에서 필요한 의존성을 타입 힌트로 지정할 수 있습니다. 이 의존성들은 Laravel 서비스 컨테이너를 통해 자동으로 해결됩니다.

4.3 오류 메시지 커스터마이징

4.4 유효성 검증 입력 준비

5 수동 유효성 검증기 생성

5.1 자동 리디렉션

5.2 명명된 오류 백

5.3 오류 메시지 커스터마이징

5.4 추가 유효성 검증 수행

6 유효성 검증된 입력 다루기

7 오류 메시지 다루기

7.1 언어 파일에서 사용자 정의 메시지 지정

7.2 언어 파일에서 속성 지정

7.3 언어 파일에서 값 지정

8 사용가능한 유효성 검증 규칙

9 조건부 규칙 추가

10 배열 유효성 검증

10.1 중첩 배열 입력 유효성 검증

10.2 오류 메시지 인덱스 및 위치

11 파일 유효성 검증

12 패스워드 유효성 검증

13 커스텀 유효성 검증 규칙

13.1 규칙 객체 사용

13.2 클로저 사용

13.3 암시적 규칙

문서 댓글 ({{ doc_comments.length }})
{{ comment.name }} {{ comment.created | snstime }}