응답이란
컨트롤러나 라우트는 사용자의 브라우저에 어떤 형태로든 응답을 반환해야 합니다.
Laravel은 문자열·배열·뷰·리다이렉트·JSON 등 다양한 형식의 응답을 단순하게 반환할 수 있는 구조를 제공합니다.
기본 응답
문자열 반환
가장 단순한 응답은 문자열 반환입니다.
Laravel은 자동으로 적절한 HTTP 응답으로 변환합니다.
Route::get('/', function () {
return 'Hello World';
});
배열 반환
배열을 반환하면 Laravel은 그것을 JSON 형식으로 자동 변환하여 응답합니다.
Route::get('/users', function () {
return [
['id' => 1, 'name' => '김철수'],
['id' => 2, 'name' => '박영희'],
];
});
Eloquent 모델이나 컬렉션을 그대로 return하면 JSON 형식으로 자동 변환됩니다. API를 손쉽게 만들 수 있어 자주 사용되는 패턴입니다.
Eloquent 모델 반환
use App\Models\User;
Route::get('/user/{user}', function (User $user) {
return $user;
});
Response 객체
response() 헬퍼를 사용하면 HTTP 상태 코드나 헤더를 지정한 상세한 응답을 반환할 수 있습니다.
Route::get('/home', function () {
return response('Hello World', 200)
->header('Content-Type', 'text/plain');
});
response()의 첫 번째 인수는 응답의 본문, 두 번째 인수는 HTTP 상태 코드입니다.
자주 사용되는 HTTP 상태 코드
| 코드 | 의미 |
|---|
200 | OK(성공) |
201 | Created(리소스 생성 성공) |
204 | No Content(콘텐츠 없음) |
301 | Moved Permanently(영구적 리다이렉트) |
302 | Found(일시적 리다이렉트) |
404 | Not Found(찾지 못함) |
422 | Unprocessable Entity(유효성 검증 오류) |
500 | Internal Server Error(서버 오류) |
뷰 응답
컨트롤러에서 Blade 템플릿을 표시하려면 view()를 사용합니다.
이것이 웹 애플리케이션에서 가장 일반적인 응답입니다.
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class HomeController extends Controller
{
public function index(): View
{
$users = [
['name' => '김철수', 'email' => '[email protected]'],
['name' => '박영희', 'email' => '[email protected]'],
];
return view('users.index', ['users' => $users]);
}
}
view()의 첫 번째 인수는 뷰 이름, 두 번째 인수는 뷰로 전달할 데이터 배열입니다.
반환 값 타입 힌트에 Illuminate\View\View를 지정하면 코드의 의도가 명확해집니다.
response()->view()로 상세 제어
상태 코드나 헤더도 함께 지정하고 싶은 경우에는 response()->view()를 사용합니다.
return response()
->view('errors.404', ['message' => '페이지를 찾을 수 없습니다'], 404);
리다이렉트
기본 리다이렉트
redirect() 헬퍼는 리다이렉트 응답을 반환합니다.
폼 전송 후 다른 페이지로 유도할 때 자주 사용합니다.
use Illuminate\Http\RedirectResponse;
Route::get('/old-page', function (): RedirectResponse {
return redirect('/new-page');
});
컨트롤러에서의 사용 예시입니다.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function store(Request $request): RedirectResponse
{
// 게시물을 저장하는 처리...
return redirect('/posts');
}
}
이름 지정 라우트로의 리다이렉트
route() 함수를 사용하면 URL을 직접 작성하지 않고 라우트 이름으로 리다이렉트 대상을 지정할 수 있습니다.
URL이 바뀌어도 수정이 불필요해지므로 이름 지정 라우트를 활용하는 것이 베스트 프랙티스입니다.
// 라우트에 이름 부여
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
// 이름 지정 라우트로 리다이렉트
return redirect()->route('posts.index');
// 라우트 파라미터를 전달하는 경우
return redirect()->route('posts.show', ['post' => $post->id]);
직전 페이지로 돌아가기
back()으로 사용자가 직전에 있던 페이지로 돌아갈 수 있습니다.
유효성 검증 실패 시의 리다이렉트에 자주 사용됩니다.
return back();
// 입력 데이터를 보존하고 돌아간다
return back()->withInput();
플래시 메시지가 포함된 리다이렉트
with()로 세션에 메시지를 붙여 리다이렉트할 수 있습니다.
폼 전송 성공 후의 메시지 표시에 편리합니다.
return redirect('/posts')->with('success', '게시물을 생성했습니다.');
Blade 템플릿에서 메시지를 표시합니다.
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
JSON 응답
API를 만드는 경우에는 response()->json()을 사용해 JSON 응답을 반환합니다.
Content-Type: application/json 헤더가 자동으로 설정됩니다.
use Illuminate\Http\JsonResponse;
Route::get('/api/users', function (): JsonResponse {
$users = [
['id' => 1, 'name' => '김철수'],
['id' => 2, 'name' => '박영희'],
];
return response()->json($users);
});
상태 코드도 지정할 수 있습니다.
return response()->json(['message' => '생성했습니다'], 201);
컨트롤러에서의 사용 예시입니다.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function index(): JsonResponse
{
$users = User::all();
return response()->json($users);
}
public function store(Request $request): JsonResponse
{
// 검증 완료된 데이터로 생성(폼 리퀘스트 또는 $request->validate() 이후)
$user = User::create($request->validated());
return response()->json($user, 201);
}
}
단순히 배열이나 Eloquent 모델을 return하는 것만으로도 JSON 응답이 되지만, response()->json()을 사용하면 상태 코드나 헤더를 세밀하게 제어할 수 있습니다.
응답 헤더
header() 메서드로 응답에 HTTP 헤더를 추가할 수 있습니다.
return response('Hello World')
->header('Content-Type', 'text/plain')
->header('X-Custom-Header', 'MyValue');
여러 헤더를 함께 설정하는 경우에는 withHeaders()를 사용합니다.
return response('Hello World')
->withHeaders([
'Content-Type' => 'text/plain',
'X-Custom-Header' => 'MyValue',
'Cache-Control' => 'no-cache',
]);
실전 예시: 컨트롤러에서의 구분 사용
용도에 맞게 응답을 구분 사용하는 예시를 정리한 것입니다.
라우트 정의
use App\Http\Controllers\PostController;
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('posts.destroy');
컨트롤러 구현
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller
{
// 목록 표시: 뷰를 반환
public function index(): View
{
$posts = Post::latest()->get();
return view('posts.index', ['posts' => $posts]);
}
// 생성 폼: 뷰를 반환
public function create(): View
{
return view('posts.create');
}
// 저장: 리다이렉트를 반환
public function store(Request $request): RedirectResponse
{
// 검증 완료된 데이터로 생성(폼 리퀘스트를 사용하고 있는 경우)
$post = Post::create($request->validated());
return redirect()
->route('posts.show', ['post' => $post->id])
->with('success', '게시물을 생성했습니다.');
}
// 상세 표시: 뷰를 반환
public function show(Post $post): View
{
return view('posts.show', ['post' => $post]);
}
// 삭제: 리다이렉트를 반환
public function destroy(Post $post): RedirectResponse
{
$post->delete();
return redirect()
->route('posts.index')
->with('success', '게시물을 삭제했습니다.');
}
}
웹 앱에서는 View와 RedirectResponse의 구분 사용이 기본입니다. 표시 계열의 액션은 View를, 변경 계열(생성·갱신·삭제)의 액션은 처리 후 RedirectResponse를 반환하는 것이 일반적인 패턴입니다.
다음 단계
유효성 검증
응답을 반환하기 전에 입력 데이터를 유효성 검증으로 검사하는 방법을 확인합니다.