> ## 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의 응답 종류와 컨트롤러에서 적절한 응답을 반환하는 방법을 설명합니다.

## 응답이란

컨트롤러나 라우트는 사용자의 브라우저에 어떤 형태로든 **응답**을 반환해야 합니다.
Laravel은 문자열·배열·뷰·리다이렉트·JSON 등 다양한 형식의 응답을 단순하게 반환할 수 있는 구조를 제공합니다.

## 기본 응답

### 문자열 반환

가장 단순한 응답은 문자열 반환입니다.
Laravel은 자동으로 적절한 HTTP 응답으로 변환합니다.

```php theme={null}
Route::get('/', function () {
    return 'Hello World';
});
```

### 배열 반환

배열을 반환하면 Laravel은 그것을 JSON 형식으로 자동 변환하여 응답합니다.

```php theme={null}
Route::get('/users', function () {
    return [
        ['id' => 1, 'name' => '김철수'],
        ['id' => 2, 'name' => '박영희'],
    ];
});
```

<Info>
  Eloquent 모델이나 컬렉션을 그대로 return하면 JSON 형식으로 자동 변환됩니다. API를 손쉽게 만들 수 있어 자주 사용되는 패턴입니다.
</Info>

### Eloquent 모델 반환

```php theme={null}
use App\Models\User;

Route::get('/user/{user}', function (User $user) {
    return $user;
});
```

## Response 객체

`response()` 헬퍼를 사용하면 HTTP 상태 코드나 헤더를 지정한 상세한 응답을 반환할 수 있습니다.

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

namespace App\Http\Controllers;

use Illuminate\View\View;

class HomeController extends Controller
{
    public function index(): View
    {
        $users = [
            ['name' => '김철수', 'email' => 'tanaka@example.com'],
            ['name' => '박영희', 'email' => 'suzuki@example.com'],
        ];

        return view('users.index', ['users' => $users]);
    }
}
```

`view()`의 첫 번째 인수는 뷰 이름, 두 번째 인수는 뷰로 전달할 데이터 배열입니다.

<Tip>
  반환 값 타입 힌트에 `Illuminate\View\View`를 지정하면 코드의 의도가 명확해집니다.
</Tip>

### `response()->view()`로 상세 제어

상태 코드나 헤더도 함께 지정하고 싶은 경우에는 `response()->view()`를 사용합니다.

```php theme={null}
return response()
    ->view('errors.404', ['message' => '페이지를 찾을 수 없습니다'], 404);
```

## 리다이렉트

### 기본 리다이렉트

`redirect()` 헬퍼는 리다이렉트 응답을 반환합니다.
폼 전송 후 다른 페이지로 유도할 때 자주 사용합니다.

```php theme={null}
use Illuminate\Http\RedirectResponse;

Route::get('/old-page', function (): RedirectResponse {
    return redirect('/new-page');
});
```

컨트롤러에서의 사용 예시입니다.

```php theme={null}
<?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이 바뀌어도 수정이 불필요해지므로 이름 지정 라우트를 활용하는 것이 베스트 프랙티스입니다.

```php theme={null}
// 라우트에 이름 부여
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
```

```php theme={null}
// 이름 지정 라우트로 리다이렉트
return redirect()->route('posts.index');

// 라우트 파라미터를 전달하는 경우
return redirect()->route('posts.show', ['post' => $post->id]);
```

### 직전 페이지로 돌아가기

`back()`으로 사용자가 직전에 있던 페이지로 돌아갈 수 있습니다.
유효성 검증 실패 시의 리다이렉트에 자주 사용됩니다.

```php theme={null}
return back();

// 입력 데이터를 보존하고 돌아간다
return back()->withInput();
```

### 플래시 메시지가 포함된 리다이렉트

`with()`로 세션에 메시지를 붙여 리다이렉트할 수 있습니다.
폼 전송 성공 후의 메시지 표시에 편리합니다.

```php theme={null}
return redirect('/posts')->with('success', '게시물을 생성했습니다.');
```

Blade 템플릿에서 메시지를 표시합니다.

```blade theme={null}
@if (session('success'))
    <div class="alert alert-success">
        {{ session('success') }}
    </div>
@endif
```

## JSON 응답

API를 만드는 경우에는 `response()->json()`을 사용해 JSON 응답을 반환합니다.
`Content-Type: application/json` 헤더가 자동으로 설정됩니다.

```php theme={null}
use Illuminate\Http\JsonResponse;

Route::get('/api/users', function (): JsonResponse {
    $users = [
        ['id' => 1, 'name' => '김철수'],
        ['id' => 2, 'name' => '박영희'],
    ];

    return response()->json($users);
});
```

상태 코드도 지정할 수 있습니다.

```php theme={null}
return response()->json(['message' => '생성했습니다'], 201);
```

컨트롤러에서의 사용 예시입니다.

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

<Info>
  단순히 배열이나 Eloquent 모델을 return하는 것만으로도 JSON 응답이 되지만, `response()->json()`을 사용하면 상태 코드나 헤더를 세밀하게 제어할 수 있습니다.
</Info>

## 응답 헤더

`header()` 메서드로 응답에 HTTP 헤더를 추가할 수 있습니다.

```php theme={null}
return response('Hello World')
    ->header('Content-Type', 'text/plain')
    ->header('X-Custom-Header', 'MyValue');
```

여러 헤더를 함께 설정하는 경우에는 `withHeaders()`를 사용합니다.

```php theme={null}
return response('Hello World')
    ->withHeaders([
        'Content-Type' => 'text/plain',
        'X-Custom-Header' => 'MyValue',
        'Cache-Control' => 'no-cache',
    ]);
```

## 실전 예시: 컨트롤러에서의 구분 사용

용도에 맞게 응답을 구분 사용하는 예시를 정리한 것입니다.

<Steps>
  <Step title="라우트 정의">
    ```php theme={null}
    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');
    ```
  </Step>

  <Step title="컨트롤러 구현">
    ```php theme={null}
    <?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', '게시물을 삭제했습니다.');
        }
    }
    ```
  </Step>
</Steps>

<Tip>
  웹 앱에서는 `View`와 `RedirectResponse`의 구분 사용이 기본입니다. 표시 계열의 액션은 `View`를, 변경 계열(생성·갱신·삭제)의 액션은 처리 후 `RedirectResponse`를 반환하는 것이 일반적인 패턴입니다.
</Tip>

## 다음 단계

<Card title="유효성 검증" icon="check-circle" href="/ko/validation">
  응답을 반환하기 전에 입력 데이터를 유효성 검증으로 검사하는 방법을 확인합니다.
</Card>


## Related topics

- [요청 라이프사이클](/ko/lifecycle.md)
- [레이트 리밋 커스터마이즈](/ko/advanced/rate-limiting.md)
- [Laravel Chisel — 스타터 킷의 포스트 인스톨 스크립트 라이브러리](/ko/blog/chisel-introduction.md)
- [테스트 입문](/ko/testing.md)
- [페이지네이션](/ko/pagination.md)
