제2.7장: 오류 처리
학습 시간: 40분
1. 표준 오류가 나쁜 이유
Laravel 애플리케이션에서 오류가 발생하고(예: 데이터베이스에서 레코드를 찾을 수 없음) 이를 처리하지 않으면, 사용자는 방대한 HTML 디버그 정보 페이지나 정보가 부족한 "서버 오류" 메시지를 보게 됩니다.
API의 경우 이는 재앙입니다. 프론트엔드 애플리케이션은 HTML이 아닌 JSON을 받아야 합니다. 우리의 임무는 모든 오류를 가로채서 구조화된 JSON 응답으로 변환하는 것입니다.
2. 중앙 오류 디스패처: bootstrap/app.php
이전 Laravel 버전에서는 App\Exceptions\Handler.php
라는 번거로운 파일이 있었습니다. Laravel 11/12에서는 모든 것이 훨씬 간단하고 우아해졌습니다. 오류 관리 센터는 이제 애플리케이션의 구성 파일인 bootstrap/app.php
에 직접 위치합니다.
bootstrap/app.php
파일을 여세요. 맨 아래에 .withExceptions(...)
블록이 보일 것입니다. 이것이 바로 우리의 "중앙 디스패처"입니다.
<?php
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// ...
})
->withExceptions(function (Exceptions $exceptions) {
// <-- 여기에서 작업할 예정입니다.
})->create();
3. 가장 흔한 오류 처리: "찾을 수 없음" (404)
API에서 가장 흔한 오류는 사용자가 존재하지 않는 리소스를 요청할 때입니다(예: GET /api/planets/999
). 이 경우 Laravel은 ModelNotFoundException
또는 NotFoundHttpException
예외를 생성합니다. 이를 가로채 봅시다.
.withExceptions(...)
안에 다음 코드를 추가하세요:
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// 데이터베이스에서 모델을 찾을 수 없을 때 예외를 가로챕니다.
$exceptions->render(function (ModelNotFoundException $e, Request $request) {
// 요청이 우리 API로 왔는지 확인합니다.
if ($request->is('api/*')) {
return response()->json([
'message' => '요청한 리소스는 우리 은하에서 찾을 수 없습니다.'
], 404);
}
});
// 경로 자체를 찾을 수 없을 때 예외를 가로챕니다.
$exceptions->render(function (NotFoundHttpException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => '그런 우주 경로는 존재하지 않습니다.'
], 404);
}
});
})->create();
$exceptions->render(...)
— 우리는 "핸들러"를 등록합니다. 이는 "ModelNotFoundException
유형의 예외가 발생하면 이 코드를 실행하라"고 말합니다.if ($request->is('api/*'))
— 이것은 중요한 확인입니다. 이 코드는 우리의 예쁜 JSON 응답이 일반 웹 페이지에 영향을 주지 않고 API 요청에만 전송되도록 보장합니다.return response()->json(...)
— 우리는 404 코드와 함께 표준화된 JSON 응답을 생성하고 반환합니다.
이제 존재하지 않는 행성을 요청하면, 보기 흉한 HTML 페이지 대신 깔끔한 JSON을 받게 될 것입니다.
4. 사용자 정의 예외: 나만의 "경고 신호" 생성
때로는 표준 예외만으로는 충분하지 않습니다. "지구 행성을 삭제할 수 없다"는 비즈니스 규칙이 있다고 가정해 봅시다. 누군가 이를 시도하면 의미 있는 오류를 반환해야 합니다.
단계 1: 사용자 정의 예외 클래스 생성 터미널에서 실행:
단계 2: 컨트롤러에서 사용
PlanetController.php
를 열고 destroy
메서드를 수정합니다:
<?php
// app/Http/Controllers/PlanetController.php
use App\Exceptions\CannotDeleteEarthException; // <-- 예외 가져오기
use App\Models\Planet;
public function destroy(Planet $planet)
{
// 새로운 비즈니스 규칙
if (strtolower($planet->name) === 'земля') {
throw new CannotDeleteEarthException('은하계 법규에 따라 지구 행성 삭제는 금지되어 있습니다.');
}
$planet->delete();
return response()->json(null, 204);
}
DELETE /api/planets/1
(여기서 1은 지구의 ID)을 실행하려고 하면, 우리 코드는 CannotDeleteEarthException
예외를 발생시킬 것입니다.
단계 3: Laravel에게 "경고"를 멋지게 처리하도록 가르치기
bootstrap/app.php
로 돌아가서 우리 예외를 위한 새로운 핸들러를 추가합니다.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// 우리의 새로운 핸들러
$exceptions->render(function (CannotDeleteEarthException $e, Request $request) {
return response()->json([
'message' => '작업이 금지되었습니다.',
'details' => $e->getMessage() // throw에 전달한 메시지를 가져옵니다.
], 403); // 403 Forbidden - "접근 금지"
});
// ... (404에 대한 다른 핸들러)
})->create();
5. 그 외 모든 실패 처리 (500 내부 서버 오류)
예기치 않은 다른 모든 오류는 어떻게 처리해야 할까요? 예를 들어, 데이터베이스가 끊겼거나 코드에 구문 오류가 있는 경우입니다. 이를 위해 가장 일반적인 오류 유형인 Throwable
에 대한 "범용" 핸들러를 등록할 수 있습니다.
중요: 이 핸들러는 위에서 정의한 더 구체적인 예외를 가로채지 않도록 마지막에 위치해야 합니다.
<?php
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
// ... (CannotDeleteEarthException 및 404에 대한 핸들러)
// 범용 핸들러 (맨 마지막)
$exceptions->render(function (Throwable $e, Request $request) {
if ($request->is('api/*')) {
// 디버그 모드에서는 실제 오류 메시지를 표시할 수 있습니다.
$message = config('app.debug')
? '오류 발생: ' . $e->getMessage()
: '선내에서 예기치 않은 오류가 발생했습니다. 엔지니어들이 이미 호출되었습니다.';
return response()->json(['message' => $message], 500);
}
});
})->create();
이제 어떤 "알 수 없는" 예외도 깔끔하게 가로채져 500 코드와 함께 JSON으로 변환될 것이며, 이는 API를 손상시키거나 사용자에게 불필요한 정보를 노출하지 않습니다.
6. 오류 로깅: 우주선의 블랙박스
config/logging.php
의 로깅 설정:
<?php
'channels' => [
'space_api' => [
'driver' => 'daily',
'path' => storage_path('logs/space_api.log'),
'level' => 'error',
'days' => 14,
],
],
로그에 기록 추가:
<?php
try {
// 오류 위험이 있는 코드
} catch (Exception $e) {
Log::channel('space_api')->error('행성 접근 오류', [
'exception' => $e,
'request' => request()->all(),
'user_id' => auth()->id()
]);
throw $e;
}
정리 퀴즈
🚀 장 요약:
귀하는 API에 강력한 복구 시스템을 갖추었습니다:
- 🛟 전역 표준 오류 가로채기
- 🪐 명확한 코드를 가진 사용자 지정 예외
- 📝 모든 오류에 대한 단일 JSON 형식
- 🔍 사고 세부 정보를 포함한 로깅
- 📡 모니터링 시스템과의 통합
우주선이 비상 상황에 대비되었습니다! 이 섹션의 마지막 장에서 모든 시스템을 테스트할 것입니다.
📌 확인:
PlanetNotFoundException
예외 생성->withExceptions
에 404 오류 처리 추가- 존재하지 않는 행성에 대한 요청 테스트
⚠️ 오류가 가로채지지 않는 경우:
is('api/*')
가 경로와 일치하는지 확인하십시오.register()
의 핸들러 순서를 확인하십시오.- 사용자 지정 예외의 경우
throw new
를 사용하십시오.