유효성 검사

번역일: 2026년 6월 25일

유효성 검사

소개

Laravel은 애플리케이션으로 들어오는 데이터를 검사하는 다양한 방법을 제공합니다. 가장 일반적인 방법은 HTTP 요청 객체에서 제공하는 validate 메서드를 사용하는 것입니다. 이 문서에서는 여러 가지 유효성 검사 방법을 함께 살펴봅니다.

Laravel에는 데이터베이스의 값 유일성 검사를 포함해 편리한 내장 유효성 검사 규칙이 풍부하게 제공됩니다. 이 문서에서 각 규칙을 자세히 설명하므로, 읽고 나면 Laravel의 유효성 검사 기능 전반을 이해할 수 있습니다.

빠른 시작 예제

Laravel의 유효성 검사 기능을 빠르게 익히기 위해, 폼을 검사하고 오류 메시지를 사용자에게 보여주는 전체 예제를 살펴봅니다. 이 예제를 따라가면 실제 요청 데이터를 검사하는 전반적인 흐름을 파악할 수 있습니다.

라우트 정의

routes/web.php에 다음과 같이 라우트를 정의한다고 가정합니다.

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

GET 라우트는 새 게시글 작성 폼을 보여주고, POST 라우트는 입력 데이터를 검사한 뒤 데이터베이스에 저장합니다.

컨트롤러 생성

다음은 이 라우트를 처리하는 간단한 컨트롤러입니다. 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]); } }

유효성 검사 로직 작성

이제 store 메서드에 유효성 검사 로직을 채워 넣겠습니다. Illuminate\Http\Request 객체의 validate 메서드를 사용합니다. 유효성 검사를 통과하면 코드가 계속 실행되고, 실패하면 Illuminate\Validation\ValidationException이 발생하면서 적절한 오류 응답이 자동으로 반환됩니다.

일반 HTTP 요청에서 검사가 실패하면 이전 URL로 리다이렉트되고, XHR(Ajax) 요청이라면 JSON 형식의 오류 응답이 반환됩니다.

/** * 새 게시글을 저장합니다. */ public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); // 게시글이 유효함... return redirect('/posts'); }

유효성 검사 규칙은 |로 구분된 문자열 대신 배열로도 지정할 수 있습니다.

$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',
]);

필드명 자체에 점이 포함되어 있다면 백슬래시로 이스케이프하여 점 표기법으로 해석되지 않도록 합니다.

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

유효성 검사 오류 표시

유효성 검사에 실패하면 Laravel이 자동으로 이전 URL로 리다이렉트합니다. 이때 모든 유효성 검사 오류와 이전 입력값이 자동으로 세션에 플래시됩니다.

$errors 변수는 web 미들웨어 그룹에 포함된 Illuminate\View\Middleware\ShareErrorsFromSession 미들웨어에 의해 모든 뷰에 자동으로 공유됩니다. 이 미들웨어가 적용되면 $errors 변수는 항상 뷰에서 사용 가능하므로 안전하게 참조할 수 있습니다. $errorsIlluminate\Support\MessageBag 인스턴스입니다. 자세한 사용법은 오류 메시지 다루기 섹션을 참고하세요.

이 예제에서는 유효성 검사 실패 시 create 뷰로 리다이렉트되어 오류 메시지를 표시할 수 있습니다.

<!-- /resources/views/post/create.blade.php --> <h1>게시글 작성</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 명령어로 생성할 수 있습니다.

한국어 메시지를 사용하려면 lang/ko/validation.php 파일을 만들고 메시지를 번역하여 사용하세요. Laravel 다국어 처리에 대한 자세한 내용은 다국어 문서를 참고하세요.

WARNING

기본적으로 Laravel 애플리케이션 스켈레톤에는 lang 디렉토리가 포함되어 있지 않습니다. 언어 파일을 커스터마이징하려면 lang:publish Artisan 명령어로 먼저 생성하세요.

XHR 요청과 유효성 검사

이 예제에서는 일반 HTML 폼으로 데이터를 전송했지만, 많은 애플리케이션이 JavaScript 프론트엔드에서 XHR 요청을 사용합니다. XHR 요청에서 validate 메서드를 사용하면 Laravel은 리다이렉트 대신 유효성 검사 오류가 담긴 JSON 응답을 HTTP 상태 코드 422와 함께 반환합니다.

`@error` 디렉티브

특정 필드에 유효성 검사 오류가 있는지 확인하려면 Blade의 @error 디렉티브를 사용하세요. @error 블록 안에서 $message 변수로 오류 메시지를 출력할 수 있습니다.

<!-- /resources/views/post/create.blade.php --> <label for="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">

폼 값 유지

유효성 검사 실패로 Laravel이 리다이렉트 응답을 생성할 때, 프레임워크는 요청의 모든 입력값을 자동으로 세션에 플래시합니다. 다음 요청에서 이 값을 꺼내 폼 필드에 다시 채워 넣을 수 있습니다.

이전 요청에서 플래시된 입력값은 old 메서드로 가져옵니다.

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

Blade 템플릿에서는 전역 old 헬퍼를 사용하는 것이 더 편리합니다. 해당 필드에 이전 입력값이 없으면 null을 반환합니다.

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

선택 필드에 대한 참고 사항

Laravel은 기본적으로 TrimStringsConvertEmptyStringsToNull 미들웨어를 전역으로 적용합니다. 이 때문에 null 값을 허용하는 선택 필드에는 반드시 nullable 규칙을 추가해야 합니다. 그렇지 않으면 null이 유효하지 않은 값으로 처리될 수 있습니다.

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

이 예제에서 publish_at 필드는 null이거나 유효한 날짜여야 합니다. nullable을 추가하지 않으면 null 값이 유효하지 않은 날짜로 처리됩니다.

유효성 검사 오류 응답 형식

요청이 JSON 응답을 기대하는 상황에서 Illuminate\Validation\ValidationException이 발생하면, Laravel은 오류 메시지를 자동으로 포맷하여 422 Unprocessable Entity HTTP 응답을 반환합니다.

아래는 유효성 검사 오류 JSON 응답 예시입니다. 중첩된 키는 점(dot) 표기법으로 평탄화됩니다.

{ "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." ] } }

Form Request 유효성 검사

Form Request 생성

복잡한 유효성 검사 시나리오에는 "Form Request"를 활용하는 것이 좋습니다. Form Request는 유효성 검사와 인가(authorization) 로직을 캡슐화한 전용 요청 클래스입니다. make:request Artisan 명령어로 생성합니다.

php artisan make:request StorePostRequest

생성된 클래스는 app/Http/Requests 디렉토리에 위치합니다(디렉토리가 없으면 자동 생성됩니다). Form Request 클래스에는 authorizerules 두 메서드가 있습니다.

  • authorize: 현재 인증된 사용자가 해당 요청을 수행할 권한이 있는지 판단합니다.

  • rules: 요청 데이터에 적용할 유효성 검사 규칙을 반환합니다.

    /**

    • 요청에 적용할 유효성 검사 규칙을 반환합니다.
    • @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string> */ public function rules(): array { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }

NOTE

rules 메서드의 파라미터에 의존성을 타입힌트하면 Laravel 서비스 컨테이너가 자동으로 주입해 줍니다.

유효성 검사 규칙은 컨트롤러 메서드에서 Form Request를 타입힌트하는 것만으로 자동 적용됩니다. 컨트롤러 메서드가 호출되기 전에 요청 유효성 검사가 먼저 실행되므로, 컨트롤러에 검사 로직을 따로 작성할 필요가 없습니다.

/** * 새 게시글을 저장합니다. */ public function store(StorePostRequest $request): RedirectResponse { // 요청이 이미 유효성 검사를 통과한 상태입니다... // 유효성 검사를 통과한 전체 입력값... $validated = $request->validated(); // 일부 필드만 선택하거나 제외... $validated = $request->safe()->only(['name', 'email']); $validated = $request->safe()->except(['name', 'email']); // 게시글 저장... return redirect('/posts'); }

유효성 검사에 실패하면 이전 URL로 리다이렉트되고 오류가 세션에 플래시됩니다. XHR 요청이라면 422 상태 코드와 함께 JSON 형식의 오류 응답이 반환됩니다.

NOTE

Inertia 기반 Laravel 프론트엔드에서 실시간 폼 유효성 검사가 필요하다면 Laravel Precognition을 확인해 보세요.

추가 유효성 검사 수행

기본 유효성 검사 이후 추가 검사가 필요하다면 Form Request의 after 메서드를 사용합니다. 이 메서드는 유효성 검사 완료 후 호출될 클로저나 클래스의 배열을 반환해야 합니다. 각 콜러블은 Illuminate\Validation\Validator 인스턴스를 받아 필요에 따라 오류를 추가할 수 있습니다.

use Illuminate\Validation\Validator; /** * 유효성 검사 후 실행할 콜러블 목록을 반환합니다. */ public function after(): array { return [ function (Validator $validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add( 'field', '이 필드에 문제가 있습니다!' ); } } ]; }

after 메서드가 반환하는 배열에는 호출 가능한 클래스도 포함할 수 있습니다. 이 클래스의 __invoke 메서드가 Illuminate\Validation\Validator 인스턴스를 받습니다.

use App\Validation\ValidateShippingTime; use App\Validation\ValidateUserStatus; use Illuminate\Validation\Validator; /** * 유효성 검사 후 실행할 콜러블 목록을 반환합니다. */ public function after(): array { return [ new ValidateUserStatus, new ValidateShippingTime, function (Validator $validator) { // } ]; }

첫 번째 실패 시 검사 중단

Form Request 클래스에 $stopOnFirstFailure 프로퍼티를 추가하면, 하나의 유효성 검사 실패가 발생하는 즉시 나머지 모든 속성의 검사를 중단합니다.

/**
 * 첫 번째 규칙 실패 시 유효성 검사를 중단할지 여부.
 *
 * @var bool
 */
protected $stopOnFirstFailure = true;

리다이렉트 경로 커스터마이징

유효성 검사 실패 시 리다이렉트할 경로를 변경하려면 $redirect 프로퍼티를 정의합니다.

/**
 * 유효성 검사 실패 시 리다이렉트할 URI.
 *
 * @var string
 */
protected $redirect = '/dashboard';

이름이 있는 라우트로 리다이렉트하려면 $redirectRoute 프로퍼티를 사용합니다.

/**
 * 유효성 검사 실패 시 리다이렉트할 라우트 이름.
 *
 * @var string
 */
protected $redirectRoute = 'dashboard';

Form Request 인가 처리

Form Request 클래스의 authorize 메서드에서 현재 인증된 사용자가 해당 작업을 수행할 권한이 있는지 판단할 수 있습니다. 주로 인가 게이트와 정책을 활용합니다.

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

Form Request는 Laravel의 기본 요청 클래스를 상속하므로 user 메서드로 현재 인증된 사용자에 접근할 수 있습니다. 또한 route 메서드로 라우트 파라미터에 접근할 수 있습니다.

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

라우트 모델 바인딩을 활용하면 코드를 더 간결하게 작성할 수 있습니다.

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

authorize 메서드가 false를 반환하면 403 HTTP 응답이 자동으로 반환되고 컨트롤러 메서드는 실행되지 않습니다.

애플리케이션의 다른 곳에서 인가 처리를 할 계획이라면 authorize 메서드를 제거하거나 단순히 true를 반환해도 됩니다.

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

NOTE

authorize 메서드의 파라미터에도 의존성을 타입힌트할 수 있으며, Laravel 서비스 컨테이너가 자동으로 주입해 줍니다.

오류 메시지 커스터마이징

messages 메서드를 오버라이드하면 Form Request에서 사용하는 오류 메시지를 커스터마이징할 수 있습니다. 이 메서드는 필드.규칙 형식의 키와 메시지 문자열로 이루어진 배열을 반환해야 합니다.

/** * 정의된 유효성 검사 규칙에 대한 오류 메시지를 반환합니다. * * @return array<string, string> */ public function messages(): array { return [ 'title.required' => '제목을 입력해 주세요.', 'body.required' => '내용을 입력해 주세요.', ]; }

유효성 검사 속성명 커스터마이징

Laravel의 내장 오류 메시지 중 상당수에는 :attribute 플레이스홀더가 포함되어 있습니다. 이 자리에 표시될 속성명을 변경하려면 attributes 메서드를 오버라이드합니다.

/** * 유효성 검사 오류에서 사용할 커스텀 속성명을 반환합니다. * * @return array<string, string> */ public function attributes(): array { return [ 'email' => '이메일 주소', ]; }

유효성 검사 전 입력값 가공

유효성 검사 규칙을 적용하기 전에 요청 데이터를 정제하거나 변환해야 한다면 prepareForValidation 메서드를 사용합니다.

use Illuminate\Support\Str; /** * 유효성 검사를 위한 데이터를 가공합니다. */ protected function prepareForValidation(): void { $this->merge([ 'slug' => Str::slug($this->slug), ]); }

유효성 검사 통과 후 데이터를 정규화하려면

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

번역일: 2026년 6월 25일