메인 콘텐츠로 건너뛰기
Laravel 11에서는 “Slim Application Skeleton”으로서 애플리케이션 구조가 대폭 바뀌었습니다. 구 구조 그대로 업그레이드한 프로젝트에 도중 참여했거나, Laravel 10 이전의 서적·튜토리얼로 학습한 경우에 당혹스러워하기 쉬운 점을 정리하고 있습니다. Laravel 11 릴리스 당시 Laracasts 포럼이나 Stack Overflow에서 자주 있었던 질문입니다.
이 FAQ는 신규 프로젝트(Laravel 11 이후)를 대상으로 하고 있습니다. 기존 프로젝트의 업그레이드 절차는 이관 가이드를 참조해 주세요.
새 구조에서는 변경 빈도가 낮은 config 파일이 프로젝트에서 삭제되어 있습니다. 이 파일들은 프레임워크 내의 config/가 사용됩니다.프로젝트 측의 config/와 프레임워크 측의 config/는 머지되어, 프로젝트 측이 우선됩니다. 커스터마이즈가 필요해진 시점에 파일을 작성하면 반영됩니다.
# フレームワーク内の config を確認する
cat vendor/laravel/framework/config/cors.php

# プロジェクト側にコピーしてカスタマイズする
php artisan config:publish cors
Laravel 11의 새 구조에서는 App\Http\Controllers\Controller가 빈 클래스로 되어 있고, Illuminate\Routing\Controller를 상속하지 않으며, ValidatesRequests / AuthorizesRequests 트레이트도 사용하고 있지 않습니다.
Laravel 10Laravel 11
Illuminate\Routing\Controller를 상속빈 클래스
ValidatesRequests 트레이트 사용없음
AuthorizesRequests 트레이트 사용없음
참고: Laravel 10 の Controller vs Laravel 11 の Controller대체 방법:
// $request->validate() を使う
public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|string|max:255',
    ]);
}

// Gate::authorize() を使う
use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post)
{
    Gate::authorize('update', $post);
}
빈번히 사용하는 경우에는 App\Http\Controllers\Controller를 Laravel 10과 마찬가지로 되돌리는 방법도 있습니다.
<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

abstract class Controller extends BaseController
{
    use AuthorizesRequests, ValidatesRequests;
}
새 구조에서는 app/Http/Kernel.php가 폐지되고, 미들웨어의 설정은 bootstrap/app.phpwithMiddleware()에서 합니다.
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    // TrustProxiesミドルウェアなどで設定していた項目はすべてMiddlewareクラスのメソッドで設定できます
    $middleware->trustProxies(at: '*');

    // グローバルミドルウェアの追加
    $middleware->append(\App\Http\Middleware\MyMiddleware::class);

    // ルートグループへのエイリアス設定
    $middleware->alias([
        'my-middleware' => \App\Http\Middleware\MyMiddleware::class,
    ]);

    // Web / API ミドルウェアグループのカスタマイズ
    $middleware->web(append: [
        \App\Http\Middleware\HandleInertiaRequests::class,
    ]);
})
커스텀 미들웨어의 파일 자체는 app/Http/Middleware/에 계속 작성합니다.
$this->middleware()Illuminate\Routing\Controller의 기능이며, 새 구조의 빈 베이스 컨트롤러에서는 사용할 수 없습니다.대신 HasMiddleware 인터페이스를 구현하여 middleware() 메서드를 정의합니다.
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;

class UserController extends Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            'auth',
            new Middleware('log', only: ['index']),
            new Middleware('subscribed', except: ['index']),
        ];
    }
}
Laravel 13 이후에서는 #[Middleware] Attribute도 사용할 수 있습니다.
use Illuminate\Routing\Controllers\HasMiddleware;
use App\Http\Middleware\EnsureTokenIsValid;

#[Middleware(EnsureTokenIsValid::class)]
class UserController extends Controller
{
    // ...
}
참고: 컨트롤러 미들웨어
authorizeResource()AuthorizesRequests 트레이트에 의존하고 있으므로, 새 구조의 빈 베이스 컨트롤러에서는 사용할 수 없습니다.대응 방법은 몇 가지 있습니다.1. 베이스 컨트롤러를 Laravel 10 사양으로 되돌린다(가장 간편)
// app/Http/Controllers/Controller.php
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Routing\Controller as BaseController;

abstract class Controller extends BaseController
{
    use AuthorizesRequests;
}
이에 의해 $this->authorizeResource()를 사용할 수 있게 됩니다.2. Laravel 13의 #[Authorize] Attribute를 각 메서드에 부여한다
use Illuminate\Routing\Attributes\Controllers\Authorize;
use App\Models\Post;

class PostController extends Controller
{
    #[Authorize('view', 'post')]
    public function show(Post $post) { /* ... */ }

    #[Authorize('update', 'post')]
    public function update(Request $request, Post $post) { /* ... */ }
}
새 구조에서는 app/Console/Kernel.php가 폐지되고, 스케줄의 설정 장소가 바뀌었습니다.routes/console.php에 기술한다(권장):
// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send')->daily();
Schedule::job(new SendEmails)->everyFiveMinutes();
bootstrap/app.php에 기술한다:
->withSchedule(function (Schedule $schedule) {
    $schedule->command('emails:send')->daily();
})
새 구조에서는 EventServiceProvider가 폐지되고, 이벤트와 리스너의 수동 등록이 불필요해졌습니다.자동 등록의 구조: 리스너 클래스의 handle() 메서드의 인수에 이벤트 클래스를 타입 선언하면 자동으로 등록됩니다.
namespace App\Listeners;

use App\Events\OrderShipped;

class SendShipmentNotification
{
    // handle() の引数にイベントクラスを指定するだけで自動登録される
    public function handle(OrderShipped $event): void
    {
        // ...
    }
}
수동 등록이 필요한 경우에는 AppServiceProvider::boot()에서 합니다.
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(
        OrderShipped::class,
        SendShipmentNotification::class,
    );
}
새 구조에서는, 서비스 프로바이더의 목록 관리가 config/app.php에서 bootstrap/providers.php로 바뀌었습니다.
// bootstrap/providers.php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\MyCustomServiceProvider::class, // ← ここに追加
];
artisan make:provider로 작성한 프로바이더는 자동적으로 여기에 추가 기재됩니다.
새 구조에서는 app/Exceptions/Handler.php가 폐지되고, 예외 처리의 설정은 bootstrap/app.phpwithExceptions()에서 합니다.
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    // 特定の例外をレポートしない
    $exceptions->dontReport(InvalidOrderException::class);

    // カスタムレンダリング
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });

    // 未認証時のリダイレクト先変更
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        return $request->is('api/*');
    });
})
라우트의 커스터마이즈는 bootstrap/app.phpwithRouting()에서 합니다.라우트 파일을 추가한다:
->withRouting(
    web: __DIR__.'/../routes/web.php',
    then: function () {
        Route::middleware('web')
            ->prefix('admin')
            ->group(base_path('routes/admin.php'));
    },
)
완전히 제어한다(Laravel 10의 RouteServiceProvider와 같음):using을 지정하면 Laravel의 기본 라우트 등록이 완전히 무효가 되고, 모두 스스로 제어할 수 있습니다.
->withRouting(
    using: function () {
        Route::middleware('web')
            ->group(base_path('routes/web.php'));

        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));
    },
)
config/app.phpproviders 키를 추가하면, 프레임워크 내의 config/app.php와 머지되어 반영됩니다.
// config/app.php
use Illuminate\Support\ServiceProvider;

return [
    // ...
    'providers' => ServiceProvider::defaultProviders()->replace([
        Illuminate\Foo\FooServiceProvider::class => Bar\BarServiceProvider::class,
    ])->toArray(),
];
replace() 메서드로 기본 프로바이더를 독자 구현으로 바꿔놓을 수 있습니다.
Laravel 11 이후, API 기능은 기본으로는 포함되어 있지 않고, 필요한 경우에 개별 설치합니다.
php artisan install:api
이 커맨드로 이하가 작성·설정됩니다.
  • routes/api.php
  • API 인증용 Laravel Sanctum의 설정
  • bootstrap/app.php에의 API 라우트 등록
Laravel 11 이후, 브로드캐스트 기능도 기본으로는 포함되어 있지 않고, 필요한 경우에 개별 설치합니다.
php artisan install:broadcasting
이 커맨드로 이하가 작성·설정됩니다.
  • routes/channels.php
  • Laravel Reverb의 설치 설정
  • 브로드캐스트 설정 파일
bootstrap/app.php의 내용을 확인해 주세요.Laravel 11 이후의 새 구조(또는 이관 완료):
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withMiddleware(...)
    ->withExceptions(...)
    ->create();
Laravel 10 이전의 구 구조 그대로 업그레이드한 프로젝트:
// bootstrap/app.php
$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
// ...
return Application::configure(... 형식이라면 새 구조, 그렇지 않으면 구 구조 그대로 업그레이드한 프로젝트입니다. 구 구조의 프로젝트를 새 구조로 이관하는 절차는 이관 가이드를 참조해 주세요.

관련 페이지

Laravel 11 이후의 애플리케이션 구조

새로운 애플리케이션 구조의 전모와 ApplicationBuilder의 내부 구현을 해설합니다.

구 구조에서 새 구조로의 이관 가이드

Laravel 10의 구 앱 구조에서 Laravel 11 이후의 새 구조로 이관하는 절차를 해설합니다.
마지막 수정일 2026년 7월 13일