본문 바로가기

컨트롤러

업데이트됨

번역일: 2026년 10월 2일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 10월 2일
번역 갱신
2026년 10월 2일

컨트롤러

소개

요청을 처리하는 모든 로직을 라우트 파일에 클로저로 작성하는 대신, "컨트롤러" 클래스를 이용해 체계적으로 정리할 수 있습니다. 컨트롤러는 서로 연관된 요청 처리 로직을 하나의 클래스로 묶어줍니다. 예를 들어 UserController 클래스는 사용자 조회, 생성, 수정, 삭제 등 사용자와 관련된 모든 요청을 처리할 수 있습니다. 컨트롤러는 기본적으로 app/Http/Controllers 디렉터리에 저장됩니다.

컨트롤러 작성하기

기본 컨트롤러

새 컨트롤러를 빠르게 생성하려면 make:controller Artisan 명령어를 실행하면 됩니다. 애플리케이션의 모든 컨트롤러는 기본적으로 app/Http/Controllers 디렉터리에 저장됩니다.

php artisan make:controller UserController

간단한 컨트롤러 예제를 살펴보겠습니다. 컨트롤러는 들어오는 HTTP 요청에 응답할 수 있는 여러 개의 public 메서드를 가질 수 있습니다.

<?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\View\View; class UserController extends Controller { /** * 주어진 사용자의 프로필을 보여줍니다. */ public function show(string $id): View { return view('user.profile', [ 'user' => User::findOrFail($id) ]); } }

컨트롤러 클래스와 메서드를 작성했다면, 다음과 같이 해당 컨트롤러 메서드로 연결되는 라우트를 정의할 수 있습니다.

use App\Http\Controllers\UserController; Route::get('/user/{id}', [UserController::class, 'show']);

들어온 요청이 지정된 라우트 URI와 일치하면 App\Http\Controllers\UserController 클래스의 show 메서드가 호출되고, 라우트 파라미터가 해당 메서드로 전달됩니다.

NOTE

컨트롤러가 반드시 기본 클래스를 상속해야 하는 것은 아닙니다. 다만 모든 컨트롤러에서 공통으로 사용할 메서드를 담은 기본 컨트롤러 클래스를 만들어 상속하면 편리한 경우가 많습니다.

단일 액션 컨트롤러

컨트롤러의 특정 액션이 유독 복잡하다면, 그 액션 하나만을 위한 별도의 컨트롤러 클래스를 만드는 것이 편리할 수 있습니다. 이를 위해 컨트롤러 안에 __invoke 메서드 하나만 정의하면 됩니다.

<?php namespace App\Http\Controllers; class ProvisionServer extends Controller { /** * 새 웹 서버를 프로비저닝합니다. */ public function __invoke() { // ... } }

단일 액션 컨트롤러에 대한 라우트를 등록할 때는 컨트롤러 메서드 이름을 지정할 필요가 없습니다. 대신 컨트롤러 클래스 이름만 라우터에 전달하면 됩니다.

use App\Http\Controllers\ProvisionServer; Route::post('/server', ProvisionServer::class);

make:controller Artisan 명령어에 --invokable 옵션을 사용하면 invokable 컨트롤러를 바로 생성할 수 있습니다.

php artisan make:controller ProvisionServer --invokable

NOTE

컨트롤러 스텁(stub)은 스텁 퍼블리싱을 통해 커스터마이징할 수 있습니다.

컨트롤러 미들웨어

미들웨어는 라우트 파일에서 컨트롤러의 라우트에 다음과 같이 지정할 수 있습니다.

Route::get('/profile', [UserController::class, 'show'])->middleware('auth');

또는 컨트롤러 클래스 안에서 직접 미들웨어를 지정하는 것이 더 편리할 수도 있습니다. 이를 위해서는 컨트롤러가 HasMiddleware 인터페이스를 구현해야 하며, 이 인터페이스는 정적(static) middleware 메서드를 요구합니다. 이 메서드에서 컨트롤러의 액션에 적용할 미들웨어 배열을 반환하면 됩니다.

<?php namespace App\Http\Controllers; use Illuminate\Routing\Controllers\HasMiddleware; use Illuminate\Routing\Controllers\Middleware; class UserController implements HasMiddleware { /** * 컨트롤러에 지정할 미들웨어를 가져옵니다. */ public static function middleware(): array { return [ 'auth', new Middleware('log', only: ['index']), new Middleware('subscribed', except: ['store']), ]; } // ... }

컨트롤러 미들웨어를 클로저로 정의할 수도 있는데, 이는 별도의 미들웨어 클래스를 만들지 않고 인라인 방식으로 간편하게 미들웨어를 정의하는 방법입니다.

use Closure; use Illuminate\Http\Request; /** * 컨트롤러에 지정할 미들웨어를 가져옵니다. */ public static function middleware(): array { return [ function (Request $request, Closure $next) { return $next($request); }, ]; }

미들웨어 속성(Attribute)

PHP 속성(attribute)을 사용해서도 컨트롤러에 미들웨어를 지정할 수 있습니다.

<?php namespace App\Http\Controllers; use Illuminate\Routing\Attributes\Controllers\Middleware; #[Middleware('auth')] #[Middleware('log', only: ['index'])] #[Middleware('subscribed', except: ['store'])] class UserController { // ... }

미들웨어 속성은 컨트롤러 메서드 각각에도 지정할 수 있습니다. 메서드에 지정된 미들웨어는 클래스 레벨에 지정된 미들웨어와 함께 병합됩니다.

<?php namespace App\Http\Controllers; use Closure; use Illuminate\Http\Request; use Illuminate\Routing\Attributes\Controllers\Middleware; #[Middleware('auth')] class UserController { #[Middleware('log')] #[Middleware('subscribed')] public function index() { // ... } #[Middleware(static function (Request $request, Closure $next) { // ... return $next($request); })] public function store() { // ... } }

컨트롤러 전체나 특정 메서드에서 미들웨어를 제외하려면 WithoutMiddleware 속성을 사용하세요. only와 except 인자를 사용하면 클래스 레벨 속성의 적용 범위를 특정 메서드로 제한할 수 있습니다.

<?php namespace App\Http\Controllers; use App\Http\Middleware\EnsureTokenIsValid; use Illuminate\Routing\Attributes\Controllers\WithoutMiddleware; #[WithoutMiddleware('subscribed', except: ['index'])] class UserController { #[WithoutMiddleware(EnsureTokenIsValid::class)] public function index() { // ... } public function show() { // ... } }

클래스 레벨의 WithoutMiddleware 속성은 자식 컨트롤러에도 상속됩니다. 단, 이 속성은 라우트 미들웨어만 제거할 수 있으며 전역 미들웨어에는 적용되지 않습니다.

권한 부여 속성(Attribute)

정책(Policy)을 통해 컨트롤러 액션을 권한 검사(authorize)하고 있다면, can 미들웨어 대신 더 간편한 Authorize 속성을 사용할 수 있습니다.

<?php namespace App\Http\Controllers; use App\Models\Comment; use App\Models\Post; use Illuminate\Routing\Attributes\Controllers\Authorize; class CommentController { #[Authorize('create', [Comment::class, 'post'])] public function store(Post $post) { // ... } #[Authorize('delete', 'comment')] public function destroy(Comment $comment) { // ... } }

첫 번째 인자는 검사할 권한(ability)이고, 두 번째 인자는 정책(Policy)에 전달할 모델 클래스, 라우트 파라미터, 또는 여러 파라미터입니다.

리소스 컨트롤러

애플리케이션 내의 각 Eloquent 모델을 하나의 "리소스"로 생각해보면, 보통 각 리소스에 대해 동일한 유형의 작업을 반복하게 됩니다. 예를 들어 Photo 모델과 Movie 모델이 있다면, 사용자는 이 리소스들을 생성(create), 조회(read), 수정(update), 삭제(delete)할 수 있을 것입니다.

이런 흔한 사용 패턴 때문에 Laravel의 리소스 라우팅은 단 한 줄의 코드로 일반적인 CRUD 라우트를 컨트롤러에 할당해줍니다. 먼저 make:controller Artisan 명령어의 --resource 옵션을 사용해 이러한 작업을 처리할 컨트롤러를 빠르게 생성할 수 있습니다.

php artisan make:controller PhotoController --resource

이 명령어는 app/Http/Controllers/PhotoController.php 경로에 컨트롤러를 생성합니다. 이 컨트롤러에는 사용 가능한 각 리소스 작업에 대한 메서드가 이미 작성되어 있습니다. 이제 해당 컨트롤러를 가리키는 리소스 라우트를 등록하면 됩니다.

use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class);

이 한 줄의 라우트 선언만으로 리소스에 대한 다양한 작업을 처리하는 여러 라우트가 생성됩니다. 생성된 컨트롤러에는 이미 각 작업에 대응하는 메서드가 작성되어 있습니다. 언제든지 route:list Artisan 명령어를 실행하면 애플리케이션의 라우트 목록을 빠르게 확인할 수 있다는 점을 기억하세요.

resources 메서드에 배열을 전달하면 여러 리소스 컨트롤러를 한 번에 등록할 수도 있습니다.

Route::resources([ 'photos' => PhotoController::class, 'posts' => PostController::class, ]);

softDeletableResources 메서드는 모두 withTrashed 메서드가 적용된 여러 리소스 컨트롤러를 한 번에 등록합니다.

Route::softDeletableResources([ 'photos' => PhotoController::class, 'posts' => PostController::class, ]);

리소스 컨트롤러가 처리하는 작업

HTTP 메서드URI액션라우트 이름
GET/photosindexphotos.index
GET/photos/createcreatephotos.create
POST/photosstorephotos.store
GET/photos/{photo}showphotos.show
GET/photos/{photo}/editeditphotos.edit
PUT/PATCH/photos/{photo}updatephotos.update
DELETE/photos/{photo}destroyphotos.destroy

모델을 찾지 못했을 때의 동작 커스터마이징

일반적으로 암묵적 바인딩(implicit binding)된 리소스 모델을 찾지 못하면 404 HTTP 응답이 생성됩니다. 하지만 리소스 라우트를 정의할 때 missing 메서드를 호출하면 이 동작을 커스터마이징할 수 있습니다. missing 메서드는 리소스의 어떤 라우트에서든 암묵적 바인딩 모델을 찾지 못했을 때 호출될 클로저를 인자로 받습니다.

use App\Http\Controllers\PhotoController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Redirect; Route::resource('photos', PhotoController::class) ->missing(function (Request $request) { return Redirect::route('photos.index'); });

소프트 삭제된 모델

일반적으로 암묵적 모델 바인딩은 소프트 삭제된 모델을 조회하지 않고 404 HTTP 응답을 반환합니다. 하지만 리소스 라우트를 정의할 때 withTrashed 메서드를 호출하면 소프트 삭제된 모델도 허용하도록 지정할 수 있습니다.

use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->withTrashed();

withTrashed를 인자 없이 호출하면 show, edit, update 리소스 라우트에서 소프트 삭제된 모델을 허용합니다. 배열을 전달하면 일부 라우트에만 적용할 수도 있습니다.

Route::resource('photos', PhotoController::class)->withTrashed(['show']);

리소스 모델 지정하기

라우트 모델 바인딩을 사용 중이고 리소스 컨트롤러의 메서드에서 모델 인스턴스를 타입힌트로 받고 싶다면, 컨트롤러를 생성할 때 --model 옵션을 사용하세요.

php artisan make:controller PhotoController --model=Photo --resource

폼 리퀘스트 생성하기

리소스 컨트롤러를 생성할 때 --requests 옵션을 추가하면, Artisan이 해당 컨트롤러의 저장(store) 및 수정(update) 메서드를 위한 폼 리퀘스트 클래스를 함께 생성해줍니다.

php artisan make:controller PhotoController --model=Photo --resource --requests

부분 리소스 라우트

리소스 라우트를 선언할 때, 기본 작업 전체가 아니라 컨트롤러가 처리할 작업의 일부만 지정할 수도 있습니다.

use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->only([ 'index', 'show' ]); Route::resource('photos', PhotoController::class)->except([ 'create', 'store', 'update', 'destroy' ]);

API 리소스 라우트

API에서 사용할 리소스 라우트를 선언할 때는 create, edit처럼 HTML 템플릿을 보여주는 라우트를 제외하고 싶은 경우가 많습니다. 이럴 때는 apiResource 메서드를 사용하면 이 두 라우트가 자동으로 제외됩니다.

use App\Http\Controllers\PhotoController; Route::apiResource('photos', PhotoController::class);

apiResources 메서드에 배열을 전달하면 여러 API 리소스 컨트롤러를 한 번에 등록할 수 있습니다.

use App\Http\Controllers\PhotoController; use App\Http\Controllers\PostController; Route::apiResources([ 'photos' => PhotoController::class, 'posts' => PostController::class, ]);

create나 edit 메서드가 포함되지 않은 API 리소스 컨트롤러를 빠르게 생성하려면 make:controller 명령어 실행 시 --api 스위치를 사용하세요.

php artisan make:controller PhotoController --api

중첩 리소스

때로는 중첩된 리소스에 대한 라우트를 정의해야 할 수도 있습니다. 예를 들어 사진(photo) 리소스에는 그 사진에 달린 여러 댓글(comment)이 있을 수 있습니다. 리소스 컨트롤러를 중첩하려면 라우트를 선언할 때 "점(dot) 표기법"을 사용하면 됩니다.

use App\Http\Controllers\PhotoCommentController; Route::resource('photos.comments', PhotoCommentController::class);

이 라우트는 다음과 같은 URI로 접근 가능한 중첩 리소스를 등록합니다.

/photos/{photo}/comments/{comment}

중첩 리소스의 범위 지정

Laravel의 암묵적 모델 바인딩 기능을 사용하면, 해결된 자식 모델이 실제로 부모 모델에 속하는지 자동으로 확인하도록 중첩 바인딩의 범위를 지정할 수 있습니다. 중첩 리소스를 정의할 때 scoped 메서드를 사용하면 자동 범위 지정을 활성화할 수 있고, 자식 리소스를 어떤 필드로 조회할지도 지정할 수 있습니다. 자세한 내용은 리소스 라우트 범위 지정 문서를 참고하세요.

얕은 중첩(Shallow Nesting)

자식 ID가 이미 고유한 식별자라면, URI에 부모와 자식 ID를 모두 포함시킬 필요가 없는 경우가 많습니다. 자동 증가(auto-increment) 기본 키처럼 고유 식별자를 사용해 URI 세그먼트에서 모델을 식별하는 경우, "얕은 중첩(shallow nesting)"을 사용할 수 있습니다.

use App\Http\Controllers\CommentController; Route::resource('photos.comments', CommentController::class)->shallow();

이 라우트 정의는 다음과 같은 라우트를 생성합니다.

HTTP 메서드URI액션라우트 이름
GET/photos/{photo}/commentsindexphotos.comments.index
GET/photos/{photo}/comments/createcreatephotos.comments.create
POST/photos/{photo}/commentsstorephotos.comments.store
GET/comments/{comment}showcomments.show
GET/comments/{comment}/editeditcomments.edit
PUT/PATCH/comments/{comment}updatecomments.update
DELETE/comments/{comment}destroycomments.destroy

리소스 라우트 이름 지정

기본적으로 모든 리소스 컨트롤러 액션에는 라우트 이름이 자동으로 부여됩니다. 하지만 원하는 라우트 이름을 담은 names 배열을 전달해서 이 이름들을 재정의할 수 있습니다.

use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->names([ 'create' => 'photos.build' ]);

리소스 라우트 파라미터 이름 지정

기본적으로 Route::resource는 리소스 이름을 단수형으로 바꾼 값을 기준으로 라우트 파라미터를 생성합니다. parameters 메서드를 사용하면 리소스별로 이를 쉽게 재정의할 수 있습니다. parameters 메서드에 전달하는 배열은 리소스 이름과 파라미터 이름의 연관 배열(associative array)이어야 합니다.

use App\Http\Controllers\AdminUserController; Route::resource('users', AdminUserController::class)->parameters([ 'users' => 'admin_user' ]);

위 예제는 리소스의 show 라우트에 대해 다음과 같은 URI를 생성합니다.

/users/{admin_user}

리소스 라우트 범위 지정

Laravel의 범위가 지정된 암묵적 모델 바인딩 기능을 사용하면, 해결된 자식 모델이 실제로 부모 모델에 속하는지 자동으로 확인하도록 중첩 바인딩의 범위를 지정할 수 있습니다. 중첩 리소스를 정의할 때 scoped 메서드를 사용하면 자동 범위 지정을 활성화할 수 있고, 자식 리소스를 어떤 필드로 조회할지도 지정할 수 있습니다.

use App\Http\Controllers\PhotoCommentController; Route::resource('photos.comments', PhotoCommentController::class)->scoped([ 'comment' => 'slug', ]);

이 라우트는 다음과 같은 URI로 접근 가능한, 범위가 지정된 중첩 리소스를 등록합니다.

/photos/{photo}/comments/{comment:slug}

중첩 라우트 파라미터로 커스텀 키를 사용하는 암묵적 바인딩을 지정하면, Laravel은 부모 모델에서 라우트 파라미터 이름의 복수형(예: comments)에 해당하는 관계(relationship) 이름을 추론하여, 이 관계를 통해 자식 모델을 부모 모델로 범위를 좁혀 조회합니다. 즉 이 예제에서는 Photo 모델에 comments라는 관계가 있다고 가정하고, 이를 사용해 Comment 모델을 조회합니다.

리소스 URI 현지화

기본적으로 Route::resource는 영어 동사와 영어의 복수형 규칙을 사용해 리소스 URI를 생성합니다. create, edit 액션에 사용되는 동사를 현지화하고 싶다면 Route::resourceVerbs 메서드를 사용하세요. 보통 App\Providers\AppServiceProvider의 boot 메서드 시작 부분에서 호출합니다.

/** * 애플리케이션의 서비스를 부트스트랩합니다. */ public function boot(): void { Route::resourceVerbs([ 'create' => 'crear', 'edit' => 'editar', ]); }

Laravel의 복수형 변환기(pluralizer)는 필요에 따라 설정할 수 있는 여러 언어를 지원합니다. 동사와 복수형 변환 언어를 커스터마이징한 뒤 Route::resource('publicacion', PublicacionController::class)처럼 리소스 라우트를 등록하면 다음과 같은 URI가 생성됩니다.

/publicacion/crear

/publicacion/{publicacion}/editar

리소스 컨트롤러 보완하기

기본 리소스 라우트 외에 추가 라우트를 리소스 컨트롤러에 더해야 한다면, 반드시 Route::resource 메서드를 호출하기 전에 해당 라우트들을 정의해야 합니다. 그렇지 않으면 resource 메서드가 생성한 라우트가 의도치 않게 추가 라우트보다 먼저 매칭되어 우선 적용될 수 있습니다.

use App\Http\Controllers\PhotoController; Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class);

NOTE

컨트롤러는 가능한 한 하나의 역할에 집중하도록 유지하세요. 일반적인 리소스 작업 범위를 벗어나는 메서드가 자꾸 필요하다면, 컨트롤러를 더 작은 단위의 두 개로 분리하는 것을 고려해보세요.

싱글톤 리소스 컨트롤러

애플리케이션에서 단 하나의 인스턴스만 존재할 수 있는 리소스가 있을 수 있습니다. 예를 들어 사용자의 "프로필"은 수정하거나 갱신할 수는 있지만, 한 사용자가 여러 개의 "프로필"을 가질 수는 없습니다. 마찬가지로 하나의 이미지는 하나의 "썸네일"만 가질 수 있습니다. 이렇게 오직 하나의 인스턴스만 존재할 수 있는 리소스를 "싱글톤 리소스"라고 부릅니다. 이런 경우에는 "싱글톤" 리소스 컨트롤러를 등록할 수 있습니다.

use App\Http\Controllers\ProfileController; use Illuminate\Support\Facades\Route; Route::singleton('profile', ProfileController::class);

위의 싱글톤 리소스 정의는 다음 라우트들을 등록합니다. 보시다시피 싱글톤 리소스에는 "생성(creation)" 라우트가 등록되지 않으며, 리소스 인스턴스가 하나뿐이므로 등록된 라우트도 식별자(identifier)를 받지 않습니다.

HTTP 메서드URI액션라우트 이름
GET/profileshowprofile.show
GET/profile/editeditprofile.edit
PUT/PATCH/profileupdateprofile.update

싱글톤 리소스는 일반 리소스 안에 중첩해서 정의할 수도 있습니다.

Route::singleton('photos.thumbnail', ThumbnailController::class);

이 예제에서 photos 리소스는 표준 리소스 라우트 전체를 가지며, thumbnail 리소스는 다음과 같은 라우트를 가진 싱글톤 리소스가 됩니다.

HTTP 메서드URI액션라우트 이름
GET/photos/{photo}/thumbnailshowphotos.thumbnail.show
GET/photos/{photo}/thumbnail/editeditphotos.thumbnail.edit
PUT/PATCH/photos/{photo}/thumbnailupdatephotos.thumbnail.update

생성 가능한 싱글톤 리소스

경우에 따라 싱글톤 리소스에도 생성(create) 및 저장(store) 라우트를 정의하고 싶을 수 있습니다. 이럴 때는 싱글톤 리소스 라우트를 등록할 때 creatable 메서드를 호출하면 됩니다.

Route::singleton('photos.thumbnail', ThumbnailController::class)->creatable();

이 예제에서는 다음 라우트들이 등록됩니다. 보시다시피 생성 가능한(creatable) 싱글톤 리소스에는 DELETE 라우트도 함께 등록됩니다.

HTTP 메서드URI액션라우트 이름
GET/photos/{photo}/thumbnail/createcreatephotos.thumbnail.create
POST/photos/{photo}/thumbnailstorephotos.thumbnail.store
GET/photos/{photo}/thumbnailshowphotos.thumbnail.show
GET/photos/{photo}/thumbnail/editeditphotos.thumbnail.edit
PUT/PATCH/photos/{photo}/thumbnailupdatephotos.thumbnail.update
DELETE/photos/{photo}/thumbnaildestroyphotos.thumbnail.destroy

싱글톤 리소스에 DELETE 라우트만 등록하고 생성·저장 라우트는 등록하고 싶지 않다면 destroyable 메서드를 사용하세요.

Route::singleton(...)->destroyable();

API 싱글톤 리소스

apiSingleton 메서드는 API를 통해 조작되는 싱글톤 리소스를 등록할 때 사용하며, 이 경우 create와 edit 라우트는 필요 없으므로 등록되지 않습니다.

Route::apiSingleton('profile', ProfileController::class);

API 싱글톤 리소스도 creatable로 지정할 수 있으며, 이 경우 store와 destroy 라우트가 함께 등록됩니다.

Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();

미들웨어와 리소스 컨트롤러

Laravel에서는 middleware, middlewareFor, withoutMiddlewareFor 메서드를 사용해 리소스 라우트의 전체 메서드 또는 특정 메서드에만 미들웨어를 지정할 수 있습니다. 이 메서드들을 활용하면 각 리소스 액션에 적용되는 미들웨어를 세밀하게 제어할 수 있습니다.

모든 메서드에 미들웨어 적용하기

middleware 메서드를 사용하면 리소스 또는 싱글톤 리소스 라우트에서 생성되는 모든 라우트에 미들웨어를 지정할 수 있습니다.

Route::resource('users', UserController::class) ->middleware(['auth', 'verified']); Route::singleton('profile', ProfileController::class) ->middleware('auth');

특정 메서드에만 미들웨어 적용하기

middlewareFor 메서드를 사용하면 특정 리소스 컨트롤러의 하나 이상의 메서드에만 미들웨어를 지정할 수 있습니다.

Route::resource('users', UserController::class) ->middlewareFor('show', 'auth'); Route::apiResource('users', UserController::class) ->middlewareFor(['show', 'update'], 'auth'); Route::resource('users', UserController::class) ->middlewareFor('show', 'auth') ->middlewareFor('update', 'auth'); Route::apiResource('users', UserController::class) ->middlewareFor(['show', 'update'], ['auth', 'verified']);

middlewareFor 메서드는 싱글톤 및 API 싱글톤 리소스 컨트롤러와 함께 사용할 수도 있습니다.

Route::singleton('profile', ProfileController::class) ->middlewareFor('show', 'auth'); Route::apiSingleton('profile', ProfileController::class) ->middlewareFor(['show', 'update'], 'auth');

특정 메서드에서 미들웨어 제외하기

withoutMiddlewareFor 메서드를 사용하면 리소스 컨트롤러의 특정 메서드에서 미들웨어를 제외할 수 있습니다.

Route::middleware(['auth', 'verified', 'subscribed'])->group(function () { Route::resource('users', UserController::class) ->withoutMiddlewareFor('index', ['auth', 'verified']) ->withoutMiddlewareFor(['create', 'store'], 'verified') ->withoutMiddlewareFor('destroy', 'subscribed'); });

의존성 주입과 컨트롤러

생성자 주입

Laravel 서비스 컨테이너는 모든 Laravel 컨트롤러를 해석(resolve)하는 데 사용됩니다. 그 덕분에 컨트롤러의 생성자에서 필요한 의존성을 자유롭게 타입힌트로 지정할 수 있습니다. 선언된 의존성은 자동으로 해석되어 컨트롤러 인스턴스에 주입됩니다.

<?php namespace App\Http\Controllers; use App\Repositories\UserRepository; class UserController extends Controller { /** * 새 컨트롤러 인스턴스를 생성합니다. */ public function __construct( protected UserRepository $users, ) {} }

메서드 주입

생성자 주입뿐만 아니라 컨트롤러의 메서드에서도 의존성을 타입힌트로 지정할 수 있습니다. 메서드 주입의 대표적인 사례는 컨트롤러 메서드에 Illuminate\Http\Request 인스턴스를 주입받는 것입니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class UserController extends Controller { /** * 새로운 사용자를 저장합니다. */ public function store(Request $request): RedirectResponse { $name = $request->name; // 사용자 저장 로직... return redirect('/users'); } }

컨트롤러 메서드가 라우트 파라미터로부터 입력값도 함께 받아야 한다면, 다른 의존성들 뒤에 라우트 인자를 나열하면 됩니다. 예를 들어 다음과 같이 라우트가 정의되어 있다고 가정해봅시다.

use App\Http\Controllers\UserController; Route::put('/user/{id}', [UserController::class, 'update']);

이 경우에도 다음과 같이 컨트롤러 메서드를 작성하면 Illuminate\Http\Request를 타입힌트로 지정하면서 동시에 id 파라미터에도 접근할 수 있습니다.

<?php namespace App\Http\Controllers; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class UserController extends Controller { /** * 주어진 사용자 정보를 수정합니다. */ public function update(Request $request, string $id): RedirectResponse { // 사용자 수정 로직... return redirect('/users'); } }

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

번역일: 2026년 10월 2일