오류 처리
업데이트됨번역일: 2026년 10월 2일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 10월 2일
- 번역 갱신
- 2026년 10월 2일
오류 처리
소개
새 Laravel 프로젝트를 시작하면 오류 및 예외 처리는 이미 기본적으로 설정되어 있습니다. 하지만 언제든지 애플리케이션의 bootstrap/app.php 파일에서 withExceptions 메서드를 사용해 예외를 어떻게 보고하고 렌더링할지 직접 관리할 수 있습니다.
withExceptions 클로저에 전달되는 $exceptions 객체는 Illuminate\Foundation\Configuration\Exceptions의 인스턴스로, 애플리케이션의 예외 처리를 담당합니다. 이 문서에서는 이 객체를 좀 더 자세히 살펴보겠습니다.
설정
config/app.php 설정 파일의 debug 옵션은 오류가 발생했을 때 사용자에게 어느 정도의 정보를 보여줄지를 결정합니다. 기본적으로 이 값은 .env 파일에 저장된 APP_DEBUG 환경 변수를 따릅니다.
로컬 개발 환경에서는 APP_DEBUG 환경 변수를 true로 설정해야 합니다.
WARNING
운영(production) 환경에서는 APP_DEBUG 값을 항상 false로 설정해야 합니다. 운영 환경에서 이 값을 true로 설정하면 민감한 설정 정보가 사용자에게 노출될 위험이 있습니다.
예외 처리
예외 보고하기
Laravel에서 예외 보고(reporting)는 예외를 로그로 남기거나 Laravel Nightwatch, Sentry, Flare와 같은 외부 서비스로 전송하는 데 사용됩니다. 기본적으로 예외는 로깅 설정에 따라 기록되지만, 원하는 방식으로 자유롭게 로그를 남길 수도 있습니다.
예외 유형에 따라 서로 다른 방식으로 보고하고 싶다면, 애플리케이션의 bootstrap/app.php에서 report 메서드를 사용해 특정 타입의 예외가 보고될 때 실행할 클로저를 등록할 수 있습니다. Laravel은 클로저의 타입힌트를 검사해 어떤 종류의 예외를 처리할지 자동으로 판단합니다.
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->report(function (InvalidOrderException $e) {
// ...
});
})report 메서드로 커스텀 예외 보고 콜백을 등록하더라도, Laravel은 여전히 기본 로깅 설정에 따라 해당 예외를 기록합니다. 기본 로깅 스택으로 예외가 전파되는 것을 막고 싶다면, 보고 콜백을 정의할 때 stop 메서드를 사용하거나 콜백에서 false를 반환하면 됩니다.
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->report(function (InvalidOrderException $e) {
// ...
})->stop();
$exceptions->report(function (InvalidOrderException $e) {
return false;
});
})NOTE
특정 예외의 보고 방식을 커스터마이징하려면 보고 가능한(reportable) 예외를 활용할 수도 있습니다.
전역 로그 컨텍스트
가능한 경우, Laravel은 현재 사용자의 ID를 모든 예외 로그 메시지에 컨텍스트 데이터로 자동 추가합니다. 애플리케이션의 bootstrap/app.php 파일에서 context 메서드를 사용하면 직접 전역 컨텍스트 데이터를 정의할 수 있습니다. 이 정보는 애플리케이션이 기록하는 모든 예외 로그 메시지에 포함됩니다.
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->context(fn () => [
'foo' => 'bar',
]);
})개별 예외의 로그 컨텍스트
모든 로그 메시지에 공통 컨텍스트를 추가하는 것도 유용하지만, 특정 예외에만 해당하는 고유한 정보를 로그에 포함하고 싶을 때도 있습니다. 애플리케이션의 예외 클래스에 context 메서드를 정의하면, 해당 예외의 로그 항목에 추가할 데이터를 지정할 수 있습니다.
<?php
namespace App\Exceptions;
use Exception;
class InvalidOrderException extends Exception
{
// ...
/**
* 예외의 컨텍스트 정보를 가져옵니다.
*
* @return array<string, mixed>
*/
public function context(): array
{
return ['order_id' => $this->orderId];
}
}`report` 헬퍼
경우에 따라 예외를 보고하면서도 현재 요청 처리는 계속 진행하고 싶을 수 있습니다. report 헬퍼 함수를 사용하면 사용자에게 오류 페이지를 렌더링하지 않고도 빠르게 예외를 보고할 수 있습니다.
public function isValid(string $value): bool
{
try {
// 값을 검증합니다...
} catch (Throwable $e) {
report($e);
return false;
}
}중복 보고 제거하기
애플리케이션 전반에서 report 함수를 사용하다 보면, 같은 예외를 여러 번 보고하여 로그에 중복 항목이 생기는 경우가 있습니다.
동일한 예외 인스턴스는 단 한 번만 보고되도록 하고 싶다면, 애플리케이션의 bootstrap/app.php 파일에서 dontReportDuplicates 메서드를 호출하면 됩니다.
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontReportDuplicates();
})이제 동일한 예외 인스턴스로 report 헬퍼를 호출하면 첫 번째 호출만 보고됩니다.
$original = new RuntimeException('Whoops!');
report($original); // 보고됨
try {
throw $original;
} catch (Throwable $caught) {
report($caught); // 무시됨
}
report($original); // 무시됨
report($caught); // 무시됨예외 로그 레벨
메시지가 애플리케이션의 로그에 기록될 때, 해당 메시지는 지정된 로그 레벨로 기록됩니다. 로그 레벨은 기록되는 메시지의 심각도나 중요도를 나타냅니다.
앞서 설명했듯, report 메서드로 커스텀 예외 보고 콜백을 등록하더라도 Laravel은 애플리케이션의 기본 로깅 설정에 따라 예외를 기록합니다. 하지만 로그 레벨에 따라 메시지가 기록되는 채널이 달라질 수 있으므로, 특정 예외가 기록될 로그 레벨을 직접 설정하고 싶을 수 있습니다.
이를 위해 애플리케이션의 bootstrap/app.php 파일에서 level 메서드를 사용할 수 있습니다. 이 메서드는 첫 번째 인자로 예외 타입을, 두 번째 인자로 로그 레벨을 받습니다.
use PDOException;
use Psr\Log\LogLevel;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->level(PDOException::class, LogLevel::CRITICAL);
})타입별로 예외 무시하기
애플리케이션을 개발하다 보면 아예 보고하고 싶지 않은 예외 유형이 생기기 마련입니다. 이런 예외를 무시하려면 애플리케이션의 bootstrap/app.php 파일에서 dontReport 메서드를 사용하세요. 이 메서드에 전달된 클래스는 보고되지 않지만, 커스텀 렌더링 로직은 여전히 적용될 수 있습니다.
use App\Exceptions\InvalidOrderException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontReport([
InvalidOrderException::class,
]);
})또는 예외 클래스에 Illuminate\Contracts\Debug\ShouldntReport 인터페이스를 구현해 간단히 "표시"해 둘 수도 있습니다. 이 인터페이스가 적용된 예외는 Laravel의 예외 핸들러가 절대 보고하지 않습니다.
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;
class PodcastProcessingException extends Exception implements ShouldntReport
{
//
}특정 타입의 예외를 언제 무시할지 더 세밀하게 제어하고 싶다면, dontReportWhen 메서드에 클로저를 전달할 수 있습니다.
use App\Exceptions\InvalidOrderException;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->dontReportWhen(function (Throwable $e) {
return $e instanceof PodcastProcessingException &&
$e->reason() === 'Subscription expired';
});
})Laravel은 내부적으로 일부 오류 유형을 이미 자동으로 무시하도록 처리해 두었습니다. 예를 들어 404 HTTP 오류로 발생하는 예외, origin 불일치로 생성되는 403 응답, 유효하지 않은 CSRF 토큰으로 발생하는 419 응답 등이 여기에 해당합니다. 특정 예외 타입에 대해 이 무시 처리를 중단하고 싶다면, 애플리케이션의 bootstrap/app.php 파일에서 stopIgnoring 메서드를 사용하세요.
use Symfony\Component\HttpKernel\Exception\HttpException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->stopIgnoring(HttpException::class);
})예외 렌더링하기
기본적으로 Laravel의 예외 핸들러는 예외를 자동으로 HTTP 응답으로 변환합니다. 하지만 특정 타입의 예외에 대해 커스텀 렌더링 클로저를 등록할 수도 있습니다. 애플리케이션의 bootstrap/app.php 파일에서 render 메서드를 사용하면 됩니다.
render 메서드에 전달하는 클로저는 Illuminate\Http\Response 인스턴스를 반환해야 하며, 이는 response 헬퍼를 통해 생성할 수 있습니다. Laravel은 클로저의 타입힌트를 검사해 어떤 예외를 렌더링할지 판단합니다.
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->render(function (InvalidOrderException $e, Request $request) {
return response()->view('errors.invalid-order', status: 500);
});
})render 메서드는 NotFoundHttpException처럼 Laravel이나 Symfony에 내장된 예외의 렌더링 동작을 재정의하는 데도 사용할 수 있습니다. render 메서드에 전달한 클로저가 아무 값도 반환하지 않으면, Laravel의 기본 예외 렌더링 로직이 사용됩니다.
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => 'Record not found.'
], 404);
}
});
})예외를 JSON으로 렌더링하기
예외를 렌더링할 때 Laravel은 요청의 Accept 헤더를 기준으로 HTML과 JSON 응답 중 무엇을 렌더링할지 자동으로 판단합니다. 이 판단 로직을 직접 커스터마이징하고 싶다면 shouldRenderJsonWhen 메서드를 사용할 수 있습니다.
use Illuminate\Http\Request;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
if ($request->is('admin/*')) {
return true;
}
return $request->expectsJson();
});
})예외 응답 커스터마이징하기
드물지만, Laravel의 예외 핸들러가 생성하는 HTTP 응답 전체를 직접 커스터마이징해야 할 때가 있습니다. 이럴 때는 respond 메서드를 사용해 응답을 커스터마이징하는 클로저를 등록할 수 있습니다.
use Symfony\Component\HttpFoundation\Response;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->respond(function (Response $response) {
if ($response->getStatusCode() === 419) {
return back()->with([
'message' => 'The page expired, please try again.',
]);
}
return $response;
});
})보고 및 렌더링 가능한 예외
bootstrap/app.php 파일에서 커스텀 보고·렌더링 동작을 정의하는 대신, 애플리케이션의 예외 클래스에 직접 report와 render 메서드를 정의할 수도 있습니다. 이 메서드들이 존재하면 프레임워크가 자동으로 호출합니다.
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class InvalidOrderException extends Exception
{
/**
* 예외를 보고합니다.
*/
public function report(): void
{
// ...
}
/**
* 예외를 HTTP 응답으로 렌더링합니다.
*/
public function render(Request $request): Response
{
return response(/* ... */);
}
}예외가 이미 렌더링 가능한 예외(예: Laravel이나 Symfony 내장 예외)를 상속받는 경우, render 메서드에서 false를 반환하면 해당 예외의 기본 HTTP 응답이 렌더링됩니다.
/**
* 예외를 HTTP 응답으로 렌더링합니다.
*/
public function render(Request $request): Response|bool
{
if (/** 커스텀 렌더링이 필요한지 판단 */) {
return response(/* ... */);
}
return false;
}예외에 특정 조건에서만 필요한 커스텀 보고 로직이 있다면, 그 외의 경우에는 기본 예외 처리 설정을 사용해 보고하도록 Laravel에 알려야 할 수 있습니다. 이럴 때는 예외의 report 메서드에서 false를 반환하면 됩니다.
/**
* 예외를 보고합니다.
*/
public function report(): bool
{
if (/** 커스텀 보고가 필요한지 판단 */) {
// ...
return true;
}
return false;
}NOTE
report 메서드에 필요한 의존성을 타입힌트로 지정하면, Laravel의 서비스 컨테이너가 자동으로 주입해 줍니다.
보고되는 예외 횟수 제한하기
애플리케이션에서 매우 많은 수의 예외가 보고된다면, 실제로 로그에 기록되거나 외부 오류 추적 서비스로 전송되는 예외 수를 제한하고 싶을 수 있습니다.
예외를 무작위 샘플링 비율로 보고하려면, 애플리케이션의 bootstrap/app.php 파일에서 throttle 메서드를 사용할 수 있습니다. throttle 메서드는 Lottery 인스턴스를 반환하는 클로저를 인자로 받습니다.
use Illuminate\Support\Lottery;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
return Lottery::odds(1, 1000);
});
})예외 타입에 따라 조건부로 샘플링할 수도 있습니다. 특정 예외 클래스의 인스턴스만 샘플링하고 싶다면, 해당 클래스일 때만 Lottery 인스턴스를 반환하면 됩니다.
use App\Exceptions\ApiMonitoringException;
use Illuminate\Support\Lottery;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof ApiMonitoringException) {
return Lottery::odds(1, 1000);
}
});
})Lottery 대신 Limit 인스턴스를 반환하면, 로그에 기록되거나 외부 오류 추적 서비스로 전송되는 예외에 속도 제한(rate limit)을 걸 수도 있습니다. 이는 예를 들어 애플리케이션이 의존하는 외부 서비스에 장애가 발생해 예외가 갑자기 폭주할 때, 로그가 쏟아지는 것을 방지하는 데 유용합니다.
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof BroadcastException) {
return Limit::perMinute(300);
}
});
})기본적으로 제한은 예외의 클래스명을 속도 제한 키로 사용합니다. Limit의 by 메서드를 사용하면 원하는 키를 직접 지정할 수 있습니다.
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
if ($e instanceof BroadcastException) {
return Limit::perMinute(300)->by($e->getMessage());
}
});
})물론, 예외 종류에 따라 Lottery와 Limit 인스턴스를 섞어서 반환할 수도 있습니다.
use App\Exceptions\ApiMonitoringException;
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Lottery;
use Throwable;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->throttle(function (Throwable $e) {
return match (true) {
$e instanceof BroadcastException => Limit::perMinute(300),
$e instanceof ApiMonitoringException => Lottery::odds(1, 1000),
default => Limit::none(),
};
});
})HTTP 예외
일부 예외는 서버에서 발생하는 HTTP 오류 코드를 나타냅니다. 예를 들어 "페이지를 찾을 수 없음" 오류(404), "인증되지 않음" 오류(401), 혹은 개발자가 직접 발생시키는 500 오류 등이 있습니다. 애플리케이션 어디에서든 이런 응답을 생성하려면 abort 헬퍼를 사용하면 됩니다.
abort(404);커스텀 HTTP 오류 페이지
Laravel을 사용하면 다양한 HTTP 상태 코드에 맞는 커스텀 오류 페이지를 손쉽게 표시할 수 있습니다. 예를 들어 404 HTTP 상태 코드에 대한 오류 페이지를 커스터마이징하려면 resources/views/errors/404.blade.php 뷰 템플릿을 만들면 됩니다. 이 뷰는 애플리케이션에서 발생하는 모든 404 오류에 대해 렌더링됩니다. errors 디렉터리 안의 뷰 파일명은 대응하는 HTTP 상태 코드와 일치해야 합니다. abort 함수에 의해 발생한 Symfony\Component\HttpKernel\Exception\HttpException 인스턴스는 $exception 변수로 뷰에 전달됩니다.
<h2>{{ $exception->getMessage() }}</h2>vendor:publish Artisan 명령어를 사용하면 Laravel의 기본 오류 페이지 템플릿을 퍼블리시할 수 있습니다. 템플릿을 퍼블리시한 후에는 원하는 대로 자유롭게 수정할 수 있습니다.
php artisan vendor:publish --tag=laravel-errors대체(fallback) HTTP 오류 페이지
특정 HTTP 상태 코드 시리즈에 대해 "대체(fallback)" 오류 페이지를 정의할 수도 있습니다. 발생한 HTTP 상태 코드에 맞는 전용 페이지가 없는 경우 이 페이지가 대신 렌더링됩니다. 이를 위해 애플리케이션의 resources/views/errors 디렉터리에 4xx.blade.php와 5xx.blade.php 템플릿을 정의하면 됩니다.
다만 대체 오류 페이지는 401, 402, 403, 404, 419, 429, 500, 503 오류 응답에는 영향을 주지 않습니다. Laravel이 이 상태 코드들에 대해 내부적으로 전용 페이지를 이미 가지고 있기 때문입니다. 이 상태 코드들에 대한 페이지를 커스터마이징하려면, 각각에 대해 개별적으로 커스텀 오류 페이지를 정의해야 합니다.