Skip to content

컨트롤러

소개

요청 처리 로직을 라우트 파일에서 클로저로 정의하는 대신, "컨트롤러" 클래스를 사용하여 이 동작을 구성할 수 있습니다. 컨트롤러는 관련된 요청 처리 로직을 단일 클래스로 그룹화할 수 있습니다. 예를 들어, 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 메서드가 호출되고 라우트 매개변수가 메서드로 전달됩니다.

lightbulb

컨트롤러는 기본 클래스를 확장할 필요가 없습니다. 그러나 모든 컨트롤러에서 공유해야 하는 메서드를 포함하는 기본 컨트롤러 클래스를 확장하는 것이 때로는 편리합니다.

단일 액션 컨트롤러

컨트롤러 액션이 특히 복잡한 경우, 해당 단일 액션에 전체 컨트롤러 클래스를 할당하는 것이 편리할 수 있습니다. 이를 위해 컨트롤러 내에서 단일 __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 옵션을 사용하여 호출 가능한 컨트롤러를 생성할 수 있습니다.

php artisan make:controller ProvisionServer --invokable
lightbulb

컨트롤러 스텁은 스텁 게시를 사용하여 사용자 정의할 수 있습니다.

컨트롤러 미들웨어

미들웨어는 라우트 파일에서 컨트롤러의 라우트에 할당할 수 있습니다.

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

또는 컨트롤러 클래스 내에서 미들웨어를 지정하는 것이 편리할 수 있습니다. 그렇게 하려면 컨트롤러는 컨트롤러가 정적 middleware 메서드를 가져야 함을 나타내는 HasMiddleware 인터페이스를 구현해야 합니다. 이 메서드에서 컨트롤러의 액션에 적용해야 하는 미들웨어 배열을 반환할 수 있습니다.

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
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: ['store']),
];
}
 
// ...
}

전체 미들웨어 클래스를 작성하지 않고 인라인 미들웨어를 정의하는 편리한 방법을 제공하는 클로저로 컨트롤러 미들웨어를 정의할 수도 있습니다.

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

리소스 컨트롤러

애플리케이션의 각 Eloquent 모델을 "리소스"라고 생각하면 일반적으로 애플리케이션의 각 리소스에 대해 동일한 작업 세트를 수행합니다. 예를 들어 애플리케이션에 Photo 모델과 Movie 모델이 있다고 가정해 보겠습니다. 사용자는 이러한 리소스를 생성, 읽기, 업데이트 또는 삭제할 수 있습니다.

이러한 일반적인 사용 사례 때문에 Laravel 리소스 라우팅은 일반적인 생성, 읽기, 업데이트 및 삭제("CRUD") 라우트를 한 줄의 코드로 컨트롤러에 할당합니다. 시작하려면 make:controller Artisan 명령의 --resource 옵션을 사용하여 이러한 작업을 처리하는 컨트롤러를 빠르게 만들 수 있습니다.

php artisan make:controller PhotoController --resource

번역:

php artisan make:controller PhotoController --resource

이 명령어는 PhotoController라는 이름의 컨트롤러를 리소스 컨트롤러로 생성합니다. 리소스 컨트롤러는 일반적으로 CRUD (Create, Read, Update, Delete) 작업을 처리하는 데 사용됩니다. 즉, 이 명령어는 PhotoController를 생성하고, index, create, store, show, edit, update, destroy 메서드를 포함하여 사진과 관련된 기본 작업을 처리할 수 있도록 준비합니다. 이 명령어는 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,
]);

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

Verb URI Action Route Name
GET /photos index photos.index
GET /photos/create create photos.create
POST /photos store photos.store
GET /photos/{photo} show photos.show
GET /photos/{photo}/edit edit photos.edit
PUT/PATCH /photos/{photo} update photos.update
DELETE /photos/{photo} destroy photos.destroy

누락된 모델 동작 사용자 정의

일반적으로, 암시적으로 바인딩된 리소스 모델을 찾을 수 없으면 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, editupdate 리소스 라우트에 대해 소프트 삭제된 모델을 허용합니다. withTrashed 메서드에 배열을 전달하여 이러한 라우트의 하위 집합을 지정할 수 있습니다.

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

리소스 모델 지정

라우트 모델 바인딩을 사용하고 리소스 컨트롤러의 메서드에서 모델 인스턴스를 타입 힌트하고 싶다면 컨트롤러를 생성할 때 --model 옵션을 사용할 수 있습니다.

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

폼 요청 생성

리소스 컨트롤러를 생성할 때 --requests 옵션을 제공하여 Artisan에게 컨트롤러의 저장 및 업데이트 메소드에 대한 폼 요청 클래스를 생성하도록 지시할 수 있습니다:

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에서 사용될 리소스 라우트를 선언할 때, 일반적으로 createedit과 같은 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

중첩된 리소스

때로는 중첩된 리소스에 대한 라우트를 정의해야 할 수 있습니다. 예를 들어, 사진 리소스에는 사진에 첨부될 수 있는 여러 개의 댓글이 있을 수 있습니다. 리소스 컨트롤러를 중첩하려면 라우트 선언에서 "점(.)" 표기법을 사용할 수 있습니다.

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

이 라우트는 다음과 같은 URI로 액세스할 수 있는 중첩된 리소스를 등록합니다.

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

중첩된 리소스 범위 지정

Laravel의 암시적 모델 바인딩 기능은 해결된 하위 모델이 상위 모델에 속하는지 확인하도록 중첩된 바인딩의 범위를 자동으로 지정할 수 있습니다. 중첩된 리소스를 정의할 때 scoped 메서드를 사용하면 자동 범위 지정이 활성화될 뿐만 아니라 Laravel이 하위 리소스를 검색해야 하는 필드를 지정할 수 있습니다. 이를 수행하는 방법에 대한 자세한 내용은 리소스 라우트 범위 지정에 대한 설명서를 참조하십시오.

얕은 중첩

종종 하위 ID가 이미 고유 식별자이기 때문에 URI 내에 상위 ID와 하위 ID를 모두 포함할 필요는 없습니다. 자동 증가 기본 키와 같은 고유 식별자를 사용하여 URI 세그먼트에서 모델을 식별하는 경우 "얕은 중첩"을 사용할 수 있습니다.

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

이 라우트 정의는 다음 라우트를 정의합니다.

동사 URI 액션 라우트 이름
GET /photos/{photo}/comments index photos.comments.index
GET /photos/{photo}/comments/create create photos.comments.create
POST /photos/{photo}/comments store photos.comments.store
GET /comments/{comment} show comments.show
GET /comments/{comment}/edit edit comments.edit
PUT/PATCH /comments/{comment} update comments.update
DELETE /comments/{comment} destroy comments.destroy

리소스 라우트 이름 지정

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

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

리소스 라우트 매개변수 이름 지정

기본적으로 Route::resource는 리소스 이름의 "단수화된" 버전을 기반으로 리소스 라우트의 라우트 매개변수를 생성합니다. parameters 메서드를 사용하여 리소스별로 이를 쉽게 재정의할 수 있습니다. parameters 메서드에 전달된 배열은 리소스 이름과 매개변수 이름의 연관 배열이어야 합니다.

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

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

/users/{admin_user}

리소스 라우트 범위 지정

Laravel의 범위가 지정된 암시적 모델 바인딩 기능은 해결된 하위 모델이 상위 모델에 속하는지 확인하도록 중첩된 바인딩의 범위를 자동으로 지정할 수 있습니다. 중첩된 리소스를 정의할 때 scoped 메서드를 사용하면 자동 범위 지정이 활성화될 뿐만 아니라 Laravel이 하위 리소스를 검색해야 하는 필드를 지정할 수 있습니다.

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

이 라우트는 다음과 같은 URI로 액세스할 수 있는 범위가 지정된 중첩된 리소스를 등록합니다.

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

사용자 지정 키가 있는 암시적 바인딩을 중첩된 라우트 매개변수로 사용할 때 Laravel은 규칙을 사용하여 상위 모델의 관계 이름을 추측하여 중첩된 모델을 검색하기 위해 쿼리 범위를 자동으로 지정합니다. 이 경우 Photo 모델에는 Comment 모델을 검색하는 데 사용할 수 있는 comments(라우트 매개변수 이름의 복수형)라는 관계가 있다고 가정합니다.

리소스 URI 현지화

기본적으로 Route::resource는 영어 동사 및 복수 규칙을 사용하여 리소스 URI를 생성합니다. createedit 액션 동사를 현지화해야 하는 경우 Route::resourceVerbs 메서드를 사용할 수 있습니다. 이는 애플리케이션의 App\Providers\AppServiceProvider 내의 boot 메서드 시작 부분에서 수행할 수 있습니다.

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

Laravel의 복수화 도구는 필요에 따라 구성할 수 있는 여러 다른 언어를 지원합니다. 동사 및 복수화 언어가 사용자 지정되면 Route::resource('publicacion', PublicacionController::class)와 같은 리소스 라우트 등록은 다음 URI를 생성합니다.

/publicacion/crear
 
/publicacion/{publicaciones}/editar

리소스 컨트롤러 보강

기본 리소스 라우트 세트 외에 리소스 컨트롤러에 추가 라우트를 추가해야 하는 경우 Route::resource 메서드를 호출하기 전에 해당 라우트를 정의해야 합니다. 그렇지 않으면 resource 메서드로 정의된 라우트가 의도치 않게 추가 라우트보다 우선할 수 있습니다.

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

컨트롤러에 집중해야 합니다. 일반적인 리소스 액션 세트 외에 메서드가 정기적으로 필요한 경우 컨트롤러를 더 작게 두 개로 분할하는 것을 고려해 보십시오.

싱글톤 리소스 컨트롤러

경우에 따라 애플리케이션에는 단일 인스턴스만 가질 수 있는 리소스가 있습니다. 예를 들어, 사용자의 "프로필"을 편집하거나 업데이트할 수 있지만 사용자는 둘 이상의 "프로필"을 가질 수 없습니다. 마찬가지로 이미지에는 단일 "썸네일"이 있을 수 있습니다. 이러한 리소스를 "싱글톤 리소스"라고 하며, 리소스의 인스턴스가 하나만 존재할 수 있음을 의미합니다. 이러한 시나리오에서는 "싱글톤" 리소스 컨트롤러를 등록할 수 있습니다.

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

위의 싱글톤 리소스 정의는 다음 경로들을 등록합니다. 보시다시피, 싱글톤 리소스에는 "생성" 경로가 등록되지 않으며, 리소스의 인스턴스가 하나만 존재할 수 있으므로 등록된 경로들은 식별자를 허용하지 않습니다.

동사 URI 액션 라우트 이름
GET /profile show profile.show
GET /profile/edit edit profile.edit
PUT/PATCH /profile update profile.update

싱글톤 리소스는 표준 리소스 내에 중첩될 수도 있습니다:

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

이 예에서, photos 리소스는 모든 표준 리소스 경로를 받게 됩니다; 하지만 thumbnail 리소스는 다음 경로들을 갖는 싱글톤 리소스가 됩니다:

동사 URI 액션 라우트 이름
GET /photos/{photo}/thumbnail show photos.thumbnail.show
GET /photos/{photo}/thumbnail/edit edit photos.thumbnail.edit
PUT/PATCH /photos/{photo}/thumbnail update photos.thumbnail.update

생성 가능한 싱글톤 리소스

경우에 따라 싱글톤 리소스에 대한 생성 및 저장 경로를 정의하고 싶을 수 있습니다. 이를 위해 싱글톤 리소스 경로를 등록할 때 creatable 메서드를 호출할 수 있습니다:

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

이 예제에서는 다음과 같은 라우트가 등록됩니다. 보시다시피, 생성 가능한 싱글톤 리소스에 대해서는 DELETE 라우트도 등록됩니다.

Verb URI Action Route Name
GET /photos/{photo}/thumbnail/create create photos.thumbnail.create
POST /photos/{photo}/thumbnail store photos.thumbnail.store
GET /photos/{photo}/thumbnail show photos.thumbnail.show
GET /photos/{photo}/thumbnail/edit edit photos.thumbnail.edit
PUT/PATCH /photos/{photo}/thumbnail update photos.thumbnail.update
DELETE /photos/{photo}/thumbnail destroy photos.thumbnail.destroy

싱글톤 리소스에 대한 DELETE 라우트를 등록하되, 생성 또는 저장 라우트는 등록하지 않으려면 destroyable 메서드를 사용할 수 있습니다.

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

API 싱글톤 리소스

apiSingleton 메서드를 사용하여 API를 통해 조작될 싱글톤 리소스를 등록할 수 있으며, 따라서 createedit 라우트는 불필요해집니다.

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

물론, API 싱글톤 리소스도 creatable이 될 수 있으며, 이 경우 리소스에 대한 storedestroy 라우트가 등록됩니다.

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

의존성 주입과 컨트롤러

생성자 주입

Laravel 서비스 컨테이너는 모든 Laravel 컨트롤러를 확인하는 데 사용됩니다. 결과적으로 컨트롤러가 생성자에서 필요로 할 수 있는 모든 의존성을 타입 힌트할 수 있습니다. 선언된 의존성은 자동으로 확인되어 컨트롤러 인스턴스에 주입됩니다.

<?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');
}
}