1 개요[ | ]
- Controllers
- 컨트롤러
https://laravel.com/docs/11.x/controllers
2 소개[ | ]
라우트 파일에서 모든 요청 처리 로직을 클로저로 정의하는 대신 "컨트롤러" 클래스를 사용하여 이러한 동작을 조직화할 수 있습니다. 컨트롤러는 관련된 요청 처리 로직을 하나의 클래스로 그룹화할 수 있습니다. 예를 들어, UserController
클래스는 사용자와 관련된 모든 들어오는 요청을 처리할 수 있습니다. 사용자의 표시, 생성, 업데이트, 삭제와 같은 작업이 포함될 수 있습니다. 기본적으로 컨트롤러는 app/Http/Controllers
디렉토리에 저장됩니다.
3 컨트롤러 작성[ | ]
3.1 기본 컨트롤러[ | ]
새로운 컨트롤러를 빠르게 생성하려면, 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
컨트롤러는 기본 클래스를 확장할 필요는 없습니다. 그러나 모든 컨트롤러에서 공유되어야 하는 메소드를 포함하는 기본 컨트롤러 클래스를 확장하는 것이 편리할 때도 있습니다.
3.2 단일 액션 컨트롤러[ | ]
컨트롤러 액션이 특히 복잡한 경우, 단일 액션을 위해 전체 컨트롤러 클래스를 전담하는 것이 편리할 수 있습니다. 이를 위해 컨트롤러 내에 단일 __invoke
메소드를 정의할 수 있습니다:
<?php
namespace App\Http\Controllers;
class ProvisionServer extends Controller
{
/**
* 새로운 웹 서버를 프로비저닝합니다.
*/
public function __invoke()
{
// ...
}
}
단일 액션 컨트롤러에 대한 라우트를 등록할 때는 컨트롤러 메소드를 지정할 필요가 없습니다. 대신, 컨트롤러의 이름만 라우터에 전달하면 됩니다:
use App\Http\Controllers\ProvisionServer;
Route::post('/server', ProvisionServer::class);
make:contoller
Artisan 명령어의 --invokable
옵션을 사용하여 invokable 컨트롤러를 생성할 수 있습니다:
php artisan make:controller ProvisionServer --invokable
Note
컨트롤러 스텁은 스텁 퍼블리싱을 통해 커스터마이징할 수 있습니다.
4 컨트롤러 미들웨어[ | ]
미들웨어는 라우트 파일에서 컨트롤러의 라우트에 할당할 수 있습니다:
Route::get('profile', [UserController::class, 'show'])->middleware('auth');
또는, 컨트롤러 클래스 내에서 미들웨어를 지정하는 것이 편리할 수 있습니다. 이를 위해 컨트롤러는 HasMiddleware
인터페이스를 구현해야 하며, 이 인터페이스는 컨트롤러가 정적 middleware
메소드를 가져야 함을 명시합니다. 이 메소드에서 컨트롤러의 동작에 적용될 미들웨어 배열을 반환할 수 있습니다:
<?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);
},
];
}
5 리소스 컨트롤러[ | ]
애플리케이션의 각 Eloquent 모델을 "리소스"로 생각하면, 애플리케이션의 각 리소스에 대해 동일한 작업 세트를 수행하는 것이 일반적입니다. 예를 들어, 애플리케이션에 Photo
모델과 Movie
모델이 있다고 가정해 보겠습니다. 사용자는 이러한 리소스를 생성, 읽기, 업데이트, 삭제할 수 있습니다.
이러한 일반적인 사용 사례 때문에, 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,
]);
- 리소스 컨트롤러가 처리하는 작업
동사 | URI | 액션 | 라우트 이름 |
---|---|---|---|
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 |
위 표는 각 작업에 대해 HTTP 메소드(동사), URI, 액션, 라우트 이름을 정리한 것입니다.
- 모델을 찾지 못했을 때의 동작 커스터마이징
암시적으로 바인딩된 리소스 모델을 찾지 못한 경우, 일반적으로 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
리소스 라우트에서 소프트 삭제된 모델을 허용합니다. 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
5.1 부분 리소스 라우트[ | ]
리소스 라우트를 선언할 때, 기본 설정된 전체 액션 집합 대신 컨트롤러가 처리해야 하는 액션의 부분집합을 지정할 수 있습니다:
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
5.2 중첩 리소스[ | ]
때로는 중첩된 리소스에 대한 라우트를 정의해야 할 때가 있습니다. 예를 들어, 사진 리소스에는 사진에 첨부될 수 있는 여러 개의 댓글이 있을 수 있습니다. 리소스 컨트롤러를 중첩하려면 라우트 선언에서 "점(dot)" 표기법을 사용할 수 있습니다:
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class);
이 라우트는 다음과 같은 URI로 접근할 수 있는 중첩된 리소스를 등록합니다:
/photos/{photo}/comments/{comment}
- 중첩된 리소스 스코프 지정
Laravel의 Laravel 라우팅#암시적 모델 바인딩 기능은 중첩된 바인딩을 자동으로 스코프 지정하여 해결된 자식 모델이 부모 모델에 속하는지 확인할 수 있습니다. 중첩된 리소스를 정의할 때 scoped
메소드를 사용하여 자동 스코프 지정을 활성화하고 자식 리소스를 조회할 필드를 Laravel에 지시할 수 있습니다. 이를 달성하는 방법에 대한 자세한 내용은 리소스 라우트 스코프 지정에 대한 문서를 참조하세요.
- 얕은 중첩
자식 ID가 이미 고유 식별자인 경우 URI에 부모와 자식 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 |
5.3 리소스 라우트 이름지정[ | ]
기본적으로 모든 리소스 컨트롤러 액션은 라우트 이름을 갖고 있습니다. 그러나, 원하는 라우트 이름을 가진 names
배열을 전달하여 이러한 이름을 재정의할 수 있습니다:
use App\Http\Controllers\PhotoController;
Route::resource('photos', PhotoController::class)->names([
'create' => 'photos.build'
]);
5.4 자원 라우트 매개변수 이름지정[ | ]
기본적으로 Route::resource
는 리소스 이름의 "단수형"을 기반으로 라우트 매개변수를 생성합니다. parameters
메소드를 사용하여 리소스별로 이를 쉽게 재정의할 수 있습니다. parameters
메소드에 전달된 배열은 리소스 이름과 매개변수 이름의 연관 배열이어야 합니다:
use App\Http\Controllers\AdminUserController;
Route::resource('users', AdminUserController::class)->parameters([
'users' => 'admin_user'
]);
위 예제는 리소스의 show
라우트에 대해 다음 URI를 생성합니다:
/users/{admin_user}
5.5 리소스 라우트 스코프지정[ | ]
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
라는 관계(라우트 매개변수 이름의 복수형)를 가지고 있다고 가정합니다.
5.6 리소스 URI 현지화[ | ]
기본적으로, Route::resource
는 영어 동사와 복수형 규칙을 사용하여 리소스 URI를 생성합니다. create
와 edit
동사를 현지화해야 하는 경우, 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
5.7 리소스 컨트롤러 보충[ | ]
기본 리소스 라우트 집합 외에 리소스 컨트롤러에 추가 라우트를 추가해야 하는 경우, Route::resource
메소드를 호출하기 전에 해당 라우트를 정의해야 합니다. 그렇지 않으면 resource
메소드가 정의한 라우트가 의도치 않게 보충 라우트보다 우선할 수 있습니다.
use App\Http\Controller\PhotoController;
Route::get('/photos/popular', [PhotoController::class, 'popular']);
Route::resource('photos', PhotoController::class);
Note
컨트롤러를 집중적으로 유지하는 것을 기억하세요. 일반적인 리소스 작업 집합 외의 메소드가 필요할 경우, 컨트롤러를 두 개의 더 작은 컨트롤러로 나누는 것을 고려해 보세요.
5.8 싱글턴 리소스 컨트롤러[ | ]
때로는 애플리케이션에는 단일 인스턴스만 존재할 수 있는 리소스가 있을 수 있습니다. 예를 들어, 사용자의 "프로필"은 편집 또는 업데이트할 수 있지만 사용자는 하나의 "프로필"만 가질 수 있습니다. 마찬가지로 이미지에는 단일 "썸네일"이 있을 수 있습니다. 이러한 리소스를 "싱글턴 리소스"라고 하며, 이는 하나의 인스턴스만 존재할 수 있음을 의미합니다. 이러한 경우 "싱글턴" 리소스 컨트롤러를 등록할 수 있습니다:
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 |
싱글턴 리소스는 또한 표준 리소스 내에 중첩(nested)될 수도 있습니다:
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
라우트도 등록됩니다:
동사 | URI | 액션 | 라우트 이름 |
---|---|---|---|
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 |
Laravel이 싱글턴 리소스에 대해 DELETE
라우트를 등록하도록 하고 생성 또는 저장 라우트를 등록하지 않도록 하려면, destroyable
메소드를 사용할 수 있습니다:
Route::singleton(...)->destroyable();
- API 싱글턴 리소스
apiSingleton
메소드는 API를 통해 조작할 싱글턴 리소스를 등록하는 데 사용할 수 있으며, 이로 인해 create
와 edit
라우트가 필요하지 않게 됩니다:
Route::apiSingleton('profile', ProfileController::class);
물론, API 싱글턴 리소스는 createble
(생성가능)일 수도 있으며, 그런 경우 리소스에 대해 store
와 destroy
라우트가 등록됩니다:
Route::apiSingleton('photos.thumbnail', ProfileController::class)->creatable();
6 의존성 주입과 컨트롤러[ | ]
- 생성자 주입
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');
}
}