> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel 11 이후의 새 앱 구조 FAQ

> Laravel 11 이후의 Slim Application Skeleton으로 이관했을 때 자주 있는 의문과 답변을 정리했습니다. 구 구조 그대로 업그레이드한 프로젝트에 도중 참여했거나, 오래된 서적·튜토리얼로 학습한 분들 대상입니다.

Laravel 11에서는 "Slim Application Skeleton"으로서 애플리케이션 구조가 대폭 바뀌었습니다. 구 구조 그대로 업그레이드한 프로젝트에 도중 참여했거나, Laravel 10 이전의 서적·튜토리얼로 학습한 경우에 당혹스러워하기 쉬운 점을 정리하고 있습니다. Laravel 11 릴리스 당시 Laracasts 포럼이나 Stack Overflow에서 자주 있었던 질문입니다.

<Info>
  이 FAQ는 **신규 프로젝트**(Laravel 11 이후)를 대상으로 하고 있습니다. 기존 프로젝트의 업그레이드 절차는 [이관 가이드](/ko/advanced/app-structure-migration)를 참조해 주세요.
</Info>

<AccordionGroup>
  <Accordion title="config 파일이 적다">
    새 구조에서는 변경 빈도가 낮은 config 파일이 프로젝트에서 삭제되어 있습니다. 이 파일들은 프레임워크 내의 `config/`가 사용됩니다.

    프로젝트 측의 `config/`와 프레임워크 측의 `config/`는 머지되어, **프로젝트 측이 우선**됩니다. 커스터마이즈가 필요해진 시점에 파일을 작성하면 반영됩니다.

    ```bash theme={null}
    # フレームワーク内の config を確認する
    cat vendor/laravel/framework/config/cors.php

    # プロジェクト側にコピーしてカスタマイズする
    php artisan config:publish cors
    ```
  </Accordion>

  <Accordion title="컨트롤러에서 $this->validate()나 $this->authorize()를 사용할 수 없다">
    Laravel 11의 새 구조에서는 `App\Http\Controllers\Controller`가 빈 클래스로 되어 있고, `Illuminate\Routing\Controller`를 상속하지 않으며, `ValidatesRequests` / `AuthorizesRequests` 트레이트도 사용하고 있지 않습니다.

    | Laravel 10                          | Laravel 11 |
    | ----------------------------------- | ---------- |
    | `Illuminate\Routing\Controller`를 상속 | 빈 클래스      |
    | `ValidatesRequests` 트레이트 사용         | 없음         |
    | `AuthorizesRequests` 트레이트 사용        | 없음         |

    참고: [Laravel 10 の Controller](https://github.com/laravel/laravel/blob/10.x/app/Http/Controllers/Controller.php) vs [Laravel 11 の Controller](https://github.com/laravel/laravel/blob/11.x/app/Http/Controllers/Controller.php)

    **대체 방법:**

    ```php theme={null}
    // $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 theme={null}
    <?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;
    }
    ```
  </Accordion>

  <Accordion title="app/Http/Middleware가 없어서 미들웨어 설정을 변경할 수 없다 / app/Http/Kernel.php가 없다">
    새 구조에서는 `app/Http/Kernel.php`가 폐지되고, 미들웨어의 설정은 `bootstrap/app.php`의 `withMiddleware()`에서 합니다.

    ```php theme={null}
    // 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/`에 계속 작성합니다.
  </Accordion>

  <Accordion title="컨트롤러 내에서 $this->middleware()를 사용할 수 없다">
    `$this->middleware()`는 `Illuminate\Routing\Controller`의 기능이며, 새 구조의 빈 베이스 컨트롤러에서는 사용할 수 없습니다.

    대신 `HasMiddleware` 인터페이스를 구현하여 `middleware()` 메서드를 정의합니다.

    ```php theme={null}
    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도 사용할 수 있습니다.

    ```php theme={null}
    use Illuminate\Routing\Controllers\HasMiddleware;
    use App\Http\Middleware\EnsureTokenIsValid;

    #[Middleware(EnsureTokenIsValid::class)]
    class UserController extends Controller
    {
        // ...
    }
    ```

    참고: [컨트롤러 미들웨어](https://laravel.com/docs/13.x/controllers#controller-middleware)
  </Accordion>

  <Accordion title="$this->authorizeResource()가 없다">
    `authorizeResource()`는 `AuthorizesRequests` 트레이트에 의존하고 있으므로, 새 구조의 빈 베이스 컨트롤러에서는 사용할 수 없습니다.

    **대응 방법은 몇 가지 있습니다.**

    **1. 베이스 컨트롤러를 Laravel 10 사양으로 되돌린다(가장 간편)**

    ```php theme={null}
    // 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를 각 메서드에 부여한다**

    ```php theme={null}
    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) { /* ... */ }
    }
    ```
  </Accordion>

  <Accordion title="app/Console/Kernel.php가 없어서 Schedule을 설정할 수 없다">
    새 구조에서는 `app/Console/Kernel.php`가 폐지되고, 스케줄의 설정 장소가 바뀌었습니다.

    **`routes/console.php`에 기술한다(권장):**

    ```php theme={null}
    // routes/console.php
    use Illuminate\Support\Facades\Schedule;

    Schedule::command('emails:send')->daily();
    Schedule::job(new SendEmails)->everyFiveMinutes();
    ```

    **`bootstrap/app.php`에 기술한다:**

    ```php theme={null}
    ->withSchedule(function (Schedule $schedule) {
        $schedule->command('emails:send')->daily();
    })
    ```
  </Accordion>

  <Accordion title="이벤트와 리스너의 등록 방법을 모르겠다">
    새 구조에서는 `EventServiceProvider`가 폐지되고, 이벤트와 리스너의 수동 등록이 불필요해졌습니다.

    **자동 등록의 구조:** 리스너 클래스의 `handle()` 메서드의 인수에 이벤트 클래스를 타입 선언하면 자동으로 등록됩니다.

    ```php theme={null}
    namespace App\Listeners;

    use App\Events\OrderShipped;

    class SendShipmentNotification
    {
        // handle() の引数にイベントクラスを指定するだけで自動登録される
        public function handle(OrderShipped $event): void
        {
            // ...
        }
    }
    ```

    수동 등록이 필요한 경우에는 `AppServiceProvider::boot()`에서 합니다.

    ```php theme={null}
    use Illuminate\Support\Facades\Event;

    public function boot(): void
    {
        Event::listen(
            OrderShipped::class,
            SendShipmentNotification::class,
        );
    }
    ```
  </Accordion>

  <Accordion title="config/app.php에의 서비스 프로바이더 추가 방법을 모르겠다">
    새 구조에서는, 서비스 프로바이더의 목록 관리가 `config/app.php`에서 `bootstrap/providers.php`로 바뀌었습니다.

    ```php theme={null}
    // bootstrap/providers.php
    return [
        App\Providers\AppServiceProvider::class,
        App\Providers\MyCustomServiceProvider::class, // ← ここに追加
    ];
    ```

    `artisan make:provider`로 작성한 프로바이더는 자동적으로 여기에 추가 기재됩니다.
  </Accordion>

  <Accordion title="app/Exceptions/Handler.php가 없다">
    새 구조에서는 `app/Exceptions/Handler.php`가 폐지되고, 예외 처리의 설정은 `bootstrap/app.php`의 `withExceptions()`에서 합니다.

    ```php theme={null}
    // 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/*');
        });
    })
    ```
  </Accordion>

  <Accordion title="라우팅을 커스터마이즈하고 싶다">
    라우트의 커스터마이즈는 `bootstrap/app.php`의 `withRouting()`에서 합니다.

    **라우트 파일을 추가한다:**

    ```php theme={null}
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        then: function () {
            Route::middleware('web')
                ->prefix('admin')
                ->group(base_path('routes/admin.php'));
        },
    )
    ```

    **완전히 제어한다(Laravel 10의 `RouteServiceProvider`와 같음):**

    `using`을 지정하면 Laravel의 기본 라우트 등록이 완전히 무효가 되고, 모두 스스로 제어할 수 있습니다.

    ```php theme={null}
    ->withRouting(
        using: function () {
            Route::middleware('web')
                ->group(base_path('routes/web.php'));

            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));
        },
    )
    ```
  </Accordion>

  <Accordion title="Laravel 기본의 ServiceProvider를 대체하고 싶다">
    `config/app.php`에 `providers` 키를 추가하면, 프레임워크 내의 `config/app.php`와 머지되어 반영됩니다.

    ```php theme={null}
    // config/app.php
    use Illuminate\Support\ServiceProvider;

    return [
        // ...
        'providers' => ServiceProvider::defaultProviders()->replace([
            Illuminate\Foo\FooServiceProvider::class => Bar\BarServiceProvider::class,
        ])->toArray(),
    ];
    ```

    `replace()` 메서드로 기본 프로바이더를 독자 구현으로 바꿔놓을 수 있습니다.
  </Accordion>

  <Accordion title="routes/api.php가 없다">
    Laravel 11 이후, API 기능은 기본으로는 포함되어 있지 않고, 필요한 경우에 개별 설치합니다.

    ```bash theme={null}
    php artisan install:api
    ```

    이 커맨드로 이하가 작성·설정됩니다.

    * `routes/api.php`
    * API 인증용 [Laravel Sanctum](https://laravel.com/docs/sanctum)의 설정
    * `bootstrap/app.php`에의 API 라우트 등록
  </Accordion>

  <Accordion title="routes/channels.php가 없다">
    Laravel 11 이후, 브로드캐스트 기능도 기본으로는 포함되어 있지 않고, 필요한 경우에 개별 설치합니다.

    ```bash theme={null}
    php artisan install:broadcasting
    ```

    이 커맨드로 이하가 작성·설정됩니다.

    * `routes/channels.php`
    * [Laravel Reverb](https://laravel.com/docs/reverb)의 설치 설정
    * 브로드캐스트 설정 파일
  </Accordion>

  <Accordion title="도중 참여한 프로젝트가 신구 어느 쪽인지 판별하고 싶다">
    `bootstrap/app.php`의 내용을 확인해 주세요.

    **Laravel 11 이후의 새 구조(또는 이관 완료):**

    ```php theme={null}
    // bootstrap/app.php
    return Application::configure(basePath: dirname(__DIR__))
        ->withRouting(...)
        ->withMiddleware(...)
        ->withExceptions(...)
        ->create();
    ```

    **Laravel 10 이전의 구 구조 그대로 업그레이드한 프로젝트:**

    ```php theme={null}
    // 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(...` 형식이라면 새 구조, 그렇지 않으면 구 구조 그대로 업그레이드한 프로젝트입니다. 구 구조의 프로젝트를 새 구조로 이관하는 절차는 [이관 가이드](/ko/advanced/app-structure-migration)를 참조해 주세요.
  </Accordion>
</AccordionGroup>

## 관련 페이지

<Card title="Laravel 11 이후의 애플리케이션 구조" icon="sitemap" href="/ko/advanced/app-structure">
  새로운 애플리케이션 구조의 전모와 `ApplicationBuilder`의 내부 구현을 해설합니다.
</Card>

<Card title="구 구조에서 새 구조로의 이관 가이드" icon="arrow-right" href="/ko/advanced/app-structure-migration">
  Laravel 10의 구 앱 구조에서 Laravel 11 이후의 새 구조로 이관하는 절차를 해설합니다.
</Card>


## Related topics

- [Laravel 11 이후의 애플리케이션 구조](/ko/advanced/app-structure.md)
- [구 구조에서 새 구조로의 이관 가이드](/ko/advanced/app-structure-migration.md)
- [엔진 API 연동 앱 개발 가이드 - VOICEVOX for Laravel](/ko/packages/laravel-voicevox/app-guide.md)
- [디렉터리 구조](/ko/directory-structure.md)
- [큐와 잡](/ko/queues.md)
