HTTP 응답

업데이트됨

번역일: 2026년 8월 10일

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

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

HTTP 응답

응답 생성

문자열 및 배열 반환

모든 라우트와 컨트롤러는 사용자의 브라우저로 전송할 응답을 반환해야 합니다. Laravel은 응답을 반환하는 여러 가지 방법을 제공합니다. 가장 기본적인 방법은 라우트나 컨트롤러에서 문자열을 반환하는 것입니다. Laravel이 이를 자동으로 완전한 HTTP 응답으로 변환해 줍니다.

Route::get('/', function () { return '안녕하세요, Laravel!'; });

문자열뿐만 아니라 배열도 반환할 수 있습니다. Laravel은 배열을 자동으로 JSON 응답으로 변환합니다.

Route::get('/', function () { return [1, 2, 3]; });

NOTE

라우트나 컨트롤러에서 Eloquent 컬렉션을 반환해도 자동으로 JSON으로 변환됩니다.

Response 객체

실제 개발에서는 단순한 문자열이나 배열만 반환하는 경우가 많지 않습니다. HTTP 상태 코드를 직접 지정하거나, 커스텀 헤더를 추가하거나, 특정 형식의 응답을 보내야 할 때는 Illuminate\Http\Response 인스턴스를 반환합니다.

Response 인스턴스는 Symfony\Component\HttpFoundation\Response 클래스를 상속하며, HTTP 응답을 구성하는 다양한 메서드를 제공합니다.

Route::get('/home', function () { return response('안녕하세요', 200) ->header('Content-Type', 'text/plain'); });

Eloquent 모델과 컬렉션

라우트나 컨트롤러에서 Eloquent 모델이나 컬렉션을 직접 반환할 수도 있습니다. 이 경우 Laravel이 모델의 hidden 속성을 자동으로 제외하면서 JSON으로 직렬화해 줍니다.

use App\Models\User; Route::get('/user/{user}', function (User $user) { return $user; });

응답에 헤더 추가하기

대부분의 응답 메서드는 메서드 체이닝을 지원하므로, 유연하게 응답을 구성할 수 있습니다. 예를 들어, header 메서드를 여러 번 체이닝하여 응답에 헤더를 여러 개 추가할 수 있습니다.

return response($content) ->header('Content-Type', $type) ->header('X-Header-One', 'Header Value') ->header('X-Header-Two', 'Header Value');

또는 withHeaders 메서드를 사용하면 배열로 한 번에 여러 헤더를 지정할 수 있습니다.

return response($content) ->withHeaders([ 'Content-Type' => $type, 'X-Header-One' => 'Header Value', 'X-Header-Two' => 'Header Value', ]);

캐시 제어 미들웨어

Laravel에는 cache.headers 미들웨어가 내장되어 있어, 라우트 그룹에 Cache-Control 헤더를 손쉽게 적용할 수 있습니다. 지시어는 해당 Cache-Control 지시어의 "snake_case" 형태로 작성하고, 세미콜론으로 구분합니다. etag 지시어를 목록에 포함하면 응답 콘텐츠의 MD5 해시가 ETag 식별자로 자동 설정됩니다.

Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () { Route::get('/privacy', function () { // ... }); Route::get('/terms', function () { // ... }); });

응답에 쿠키 추가하기

cookie 메서드를 사용하면 응답에 쿠키를 첨부할 수 있습니다. 쿠키 이름, 값, 유효 시간(분 단위)을 인수로 전달합니다.

return response('안녕하세요')->cookie( 'name', 'value', $minutes );

cookie 메서드는 추가적으로 사용 빈도가 낮은 몇 가지 인수를 더 받습니다. 일반적으로 이 인수들은 PHP 기본 내장 함수인 setcookie의 인수와 동일한 의미를 가집니다.

return response('안녕하세요')->cookie( 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly );

아직 응답 인스턴스가 없는 상태에서 쿠키를 응답과 함께 전송하고 싶다면, Cookie 파사드를 사용해 쿠키를 "큐"에 등록할 수 있습니다. queue 메서드는 쿠키 인스턴스 또는 쿠키 생성에 필요한 인수를 받으며, 등록된 쿠키는 브라우저로 응답을 전송하기 전에 자동으로 첨부됩니다.

use Illuminate\Support\Facades\Cookie; Cookie::queue('name', 'value', $minutes);

나중에 응답에 첨부할 수 있는 Symfony\Component\HttpFoundation\Cookie 인스턴스를 미리 만들고 싶다면, 전역 헬퍼 함수 cookie를 사용합니다. 이 쿠키는 응답에 직접 첨부하지 않으면 클라이언트에 전송되지 않습니다.

$cookie = cookie('name', 'value', $minutes); return response('안녕하세요')->cookie($cookie);

쿠키 만료 처리하기

응답에서 withoutCookie 메서드를 호출하면 특정 쿠키를 만료시켜 삭제할 수 있습니다.

return response('안녕하세요')->withoutCookie('name');

아직 응답 인스턴스가 없는 상황이라면, Cookie 파사드의 expire 메서드를 사용할 수 있습니다.

Cookie::expire('name');

쿠키와 암호화

기본적으로 Laravel은 Illuminate\Cookie\Middleware\EncryptCookies 미들웨어 덕분에 앱이 생성하는 모든 쿠키를 암호화하고 서명합니다. 따라서 클라이언트가 쿠키 값을 임의로 변조하거나 읽을 수 없습니다.

특정 쿠키에 대해 암호화를 비활성화하고 싶다면, 애플리케이션의 bootstrap/app.php 파일에서 encryptCookies 메서드를 사용합니다.

->withMiddleware(function (Middleware $middleware) { $middleware->encryptCookies(except: [ 'cookie_name', ]); })

리다이렉트

리다이렉트 응답은 Illuminate\Http\RedirectResponse 클래스의 인스턴스이며, 사용자를 다른 URL로 이동시키는 데 필요한 적절한 헤더가 포함되어 있습니다. RedirectResponse 인스턴스를 생성하는 방법은 여러 가지가 있습니다. 가장 간단한 방법은 전역 헬퍼 함수 redirect를 사용하는 것입니다.

Route::get('/dashboard', function () { return redirect('/home/dashboard'); });

사용자가 폼을 잘못 제출했을 때처럼 이전 페이지로 되돌려 보내야 하는 경우, 전역 헬퍼 함수 back을 사용합니다. 이 기능은 세션을 활용하므로, back 함수를 호출하는 라우트가 반드시 web 미들웨어 그룹을 사용하고 있어야 합니다.

Route::post('/user/profile', function () { // 유효성 검사 처리... return back()->withInput(); });

이름이 지정된 라우트로 리다이렉트

redirect 헬퍼를 인수 없이 호출하면 Illuminate\Routing\Redirector 인스턴스가 반환되어 다양한 메서드를 사용할 수 있습니다. 예를 들어, 이름이 지정된 라우트로 리다이렉트하려면 route 메서드를 사용합니다.

return redirect()->route('login');

라우트에 파라미터가 있다면 두 번째 인수로 전달합니다.

// 다음과 같은 URI를 가진 라우트: /profile/{id} return redirect()->route('profile', ['id' => 1]);

Eloquent 모델로 파라미터 채우기

Eloquent 모델에서 "ID" 파라미터를 채워 리다이렉트하는 경우, 모델 인스턴스 자체를 전달하면 됩니다. ID가 자동으로 추출됩니다.

// 다음과 같은 URI를 가진 라우트: /profile/{id} return redirect()->route('profile', [$user]);

라우트 파라미터에 들어갈 값을 커스터마이즈하고 싶다면, Eloquent 모델에서 getRouteKey 메서드를 오버라이드합니다.

/** * 모델의 라우트 키 값을 반환합니다. */ public function getRouteKey(): mixed { return $this->slug; }

컨트롤러 액션으로 리다이렉트

컨트롤러 액션으로 리다이렉트를 생성할 수도 있습니다. 컨트롤러 클래스명과 액션 메서드명을 action 메서드에 전달하면 됩니다.

use App\Http\Controllers\UserController; return redirect()->action([UserController::class, 'index']);

컨트롤러 라우트에 파라미터가 필요하다면 두 번째 인수로 전달합니다.

return redirect()->action( [UserController::class, 'profile'], ['id' => 1] );

외부 도메인으로 리다이렉트

애플리케이션 외부 도메인으로 리다이렉트해야 할 때는 away 메서드를 사용합니다. 이 메서드는 URL 인코딩, 유효성 검사, 검증 없이 RedirectResponse를 생성합니다.

return redirect()->away('https://www.google.com');

세션 플래시 데이터와 함께 리다이렉트

새 URL로 리다이렉트하면서 세션에 데이터를 플래시하는 작업은 보통 함께 이루어집니다. 주로 어떤 동작을 성공적으로 처리한 후 성공 메시지를 세션에 담아 다음 페이지로 넘길 때 사용합니다. 편의를 위해 RedirectResponse 인스턴스를 생성하면서 메서드 체이닝으로 데이터를 세션에 플래시할 수 있습니다.

Route::post('/user/profile', function () { // 프로필 업데이트 처리... return redirect('/dashboard')->with('status', '프로필이 업데이트되었습니다!'); });

사용자가 리다이렉트된 후에는 세션에서 플래시된 메시지를 꺼내 표시할 수 있습니다. 예를 들어 Blade 문법으로 이렇게 작성합니다.

@if (session('status')) <div class="alert alert-success"> {{ session('status') }} </div> @endif

입력값과 함께 리다이렉트

RedirectResponse 인스턴스의 withInput 메서드를 사용하면 현재 요청의 입력 데이터를 세션에 플래시한 후 리다이렉트할 수 있습니다. 이는 주로 유효성 검사에 실패했을 때 사용자가 입력한 값을 폼에 다시 채워 넣기 위해 사용합니다. 입력값이 세션에 플래시된 후에는 다음 요청에서 이전 입력값을 손쉽게 가져올 수 있습니다.

return back()->withInput();

기타 응답 타입

response 헬퍼를 사용하면 다양한 종류의 응답 인스턴스를 생성할 수 있습니다. 인수 없이 response 헬퍼를 호출하면 Illuminate\Contracts\Routing\ResponseFactory 컨트랙트의 구현체가 반환됩니다. 이 팩토리는 응답을 생성하는 여러 유용한 메서드를 제공합니다.

뷰 응답

응답의 상태 코드와 헤더를 제어하면서 를 응답 콘텐츠로 반환하고 싶다면 view 메서드를 사용합니다.

return response() ->view('hello', $data, 200) ->header('Content-Type', $type);

상태 코드나 커스텀 헤더를 따로 지정할 필요가 없다면, 전역 헬퍼 함수 view를 바로 사용하면 됩니다.

JSON 응답

json 메서드는 Content-Type 헤더를 자동으로 application/json으로 설정하고, PHP의 json_encode 함수를 사용해 전달된 배열을 JSON으로 변환합니다.

return response()->json([ 'name' => '홍길동', 'state' => '서울', ]);

JSONP 응답을 생성하려면 json 메서드와 함께 withCallback 메서드를 사용합니다.

return response() ->json(['name' => '홍길동', 'state' => '서울']) ->withCallback($request->input('callback'));

파일 다운로드

download 메서드를 사용하면 브라우저가 지정한 경로의 파일을 다운로드하도록 강제하는 응답을 생성할 수 있습니다. 두 번째 인수로 다운로드 파일명을 지정할 수 있으며, 세 번째 인수로는 HTTP 헤더 배열을 전달할 수 있습니다.

return response()->download($pathToFile); return response()->download($pathToFile, $name, $headers);

WARNING

파일 다운로드를 관리하는 Symfony HttpFoundation은 다운로드되는 파일의 파일명이 ASCII 문자여야 합니다.

파일 응답

file 메서드는 파일을 다운로드하지 않고, 브라우저에서 직접 표시할 때 사용합니다. PDF나 이미지 파일 같은 경우에 유용합니다. 첫 번째 인수로 파일의 절대 경로를, 두 번째 인수로 헤더 배열을 전달할 수 있습니다.

return response()->file($pathToFile); return response()->file($pathToFile, $headers);

스트리밍 응답

생성되는 즉시 데이터를 클라이언트로 스트리밍하면 특히 대용량 응답이나 장시간 실행되는 작업에서 메모리 사용량을 크게 줄이고 체감 성능을 높일 수 있습니다.

Route::get('/stream', function () { return response()->stream(function (): void { foreach (['개발', '테스트', '배포'] as $event) { echo $event; ob_flush(); flush(); sleep(2); // 데이터 스트리밍 사이의 지연 시뮬레이션 } }, 200, ['X-Accel-Buffering' => 'no']); });

스트리밍 응답 소비하기

스트리밍 응답은 Laravel의 HTTP 클라이언트인 Http 파사드를 사용해 소비할 수 있습니다. 자세한 내용은 HTTP 클라이언트 문서의 스트리밍 응답 섹션을 참고하세요.

use Illuminate\Support\Facades\Http; $response = Http::withOptions(['stream' => true])->get('https://example.com/stream'); foreach ($response->chunks(1024) as $chunk) { echo $chunk; }

스트리밍 JSON 응답

점진적으로 JSON 데이터를 스트리밍해야 한다면 streamJson 메서드를 사용합니다. 이 방법은 JavaScript에서 비동기로 처리하거나 대용량 데이터셋을 스트리밍 방식으로 전송해야 할 때 특히 유용합니다.

use App\Models\User; Route::get('/users.json', function () { return response()->streamJson([ 'users' => User::cursor(), ]); });

이벤트 스트림 (SSE)

eventStream 메서드를 사용하면 text/event-stream 콘텐츠 타입을 사용한 서버-전송 이벤트(Server-Sent Events, SSE) 응답을 반환할 수 있습니다. 콜백 내에서 yield를 사용해 이벤트를 스트리밍합니다.

Route::get('/chat', function () { return response()->eventStream(function () { $stream = OpenAI::client()->chat()->createStreamed(...); foreach ($stream as $response) { yield $response->choices[0]; } }); });

StreamedEvent 클래스를 사용하면 이벤트 객체를 직접 생성해 yield할 수도 있습니다.

use Illuminate\Http\StreamedEvent; yield new StreamedEvent( event: 'update', data: 'Hello, World!', );

SSE 스트림은 JavaScript의 EventSource API를 통해 소비할 수 있습니다. 스트리밍이 완료되면 eventStream은 자동으로 </stream> 이벤트를 전송합니다. EventSource에서 close 이벤트 리스너로 이를 감지해 연결을 종료할 수 있습니다.

const source = new EventSource('/chat'); source.addEventListener('update', (event) => { console.log(event.data); }); source.addEventListener('close', () => { source.close(); });

스트리밍이 완료되기 전에 커스텀 이벤트를 전송하고 싶다면, eventStream 메서드의 endEvent 인수를 통해 종료 이벤트 이름을 직접 지정할 수 있습니다.

return response()->eventStream(function () { // ... }, endEvent: 'end');

종료 이벤트를 아예 전송하지 않으려면 endEventfalse를 전달합니다.

return response()->eventStream(function () { // ... }, endEvent: false);

스트리밍 다운로드

특정 작업의 응답 내용을 디스크에 저장하지 않고 바로 다운로드 가능한 응답으로 전환하고 싶을 때는 streamDownload 메서드를 사용합니다. 이 메서드는 콜백, 파일명, 그리고 선택적 헤더 배열을 인수로 받습니다.

use App\Services\GitHub; return response()->streamDownload(function () { echo GitHub::api('repo') ->contents() ->readme('laravel', 'laravel')['contents']; }, 'laravel-readme.md');

응답 매크로

여러 라우트나 컨트롤러에서 재사용할 커스텀 응답을 정의하고 싶다면, Response 파사드의 macro 메서드를 사용합니다. 일반적으로 이 메서드는 애플리케이션의 서비스 프로바이더 중 하나, 예를 들어 App\Providers\AppServiceProviderboot 메서드에서 호출합니다.

<?php namespace App\Providers; use Illuminate\Support\Facades\Response; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Response::macro('caps', function (string $value) { return Response::make(strtoupper($value)); }); } }

macro 메서드는 첫 번째 인수로 매크로 이름을, 두 번째 인수로 클로저를 받습니다. 매크로의 클로저는 ResponseFactory 구현체나 response 헬퍼에서 해당 매크로 이름을 호출할 때 실행됩니다.

return response()->caps('foo');

응답 생성

문자열과 배열

모든 라우트와 컨트롤러는 사용자의 브라우저로 보낼 응답을 반환해야 합니다. Laravel은 응답을 반환하는 여러 방법을 제공합니다. 가장 간단한 방법은 라우트나 컨트롤러에서 문자열을 반환하는 것입니다. 프레임워크가 자동으로 완전한 HTTP 응답으로 변환합니다:

Route::get('/', function () { return 'Hello World'; });

문자열 외에도 배열을 반환할 수 있습니다. 배열은 자동으로 JSON 응답으로 변환됩니다:

Route::get('/', function () { return [1, 2, 3]; });

NOTE

라우트나 컨트롤러에서 Eloquent 컬렉션을 반환하면 자동으로 JSON으로 변환됩니다. 바로 활용해 보세요!

응답 객체

실제 애플리케이션에서는 단순한 문자열이나 배열을 반환하는 경우보다, 완전한 Illuminate\Http\Response 인스턴스나 를 반환하는 경우가 더 많습니다.

Response 인스턴스를 반환하면 HTTP 상태 코드와 헤더를 직접 설정할 수 있습니다. ResponseSymfony\Component\HttpFoundation\Response 클래스를 상속하며, HTTP 응답 구성에 필요한 다양한 메서드를 제공합니다:

Route::get('/home', function () { return response('Hello World', 200) ->header('Content-Type', 'text/plain'); });

Eloquent 모델과 컬렉션

Eloquent ORM 모델과 컬렉션을 라우트나 컨트롤러에서 직접 반환할 수도 있습니다. 이 경우 Laravel은 모델의 숨김 속성 설정을 존중하면서 자동으로 JSON 응답으로 변환합니다:

use App\Models\User; Route::get('/user/{user}', function (User $user) { return $user; });

응답에 헤더 추가하기

대부분의 응답 메서드는 메서드 체이닝을 지원하므로, 응답 인스턴스를 유연하게 구성할 수 있습니다. 예를 들어 header 메서드를 연속으로 호출하여 여러 헤더를 한 번에 추가할 수 있습니다:

return response($content) ->header('Content-Type', $type) ->header('X-Header-One', 'Header Value') ->header('X-Header-Two', 'Header Value');

여러 헤더를 배열로 한 번에 지정하려면 withHeaders 메서드를 사용하세요:

return response($content) ->withHeaders([ 'Content-Type' => $type, 'X-Header-One' => 'Header Value', 'X-Header-Two' => 'Header Value', ]);

응답에서 특정 헤더를 제거하려면 withoutHeader 메서드를 사용합니다:

return response($content)->withoutHeader('X-Debug'); return response($content)->withoutHeader(['X-Debug', 'X-Powered-By']);

Cache Control 미들웨어

Laravel에는 cache.headers 미들웨어가 내장되어 있어, 라우트 그룹에 Cache-Control 헤더를 손쉽게 설정할 수 있습니다. 각 캐시 제어 지시자는 스네이크 케이스로 작성하고, 세미콜론으로 구분합니다. 지시자 목록에 etag를 포함하면 응답 내용의 MD5 해시값이 자동으로 ETag 식별자로 설정됩니다:

Route::middleware('cache.headers:public;max_age=30;s_maxage=300;stale_while_revalidate=600;etag')->group(function () { Route::get('/privacy', function () { // ... }); Route::get('/terms', function () { // ... }); });

응답에 쿠키 추가하기

응답 인스턴스의 cookie 메서드를 사용해 쿠키를 추가할 수 있습니다. 쿠키 이름, 값, 유효 시간(분 단위)을 전달합니다:

return response('Hello World')->cookie( 'name', 'value', $minutes );

cookie 메서드는 추가적인 인수도 받습니다. 이 인수들은 PHP 기본 함수인 setcookie의 인수와 동일한 역할을 합니다:

return response('Hello World')->cookie( 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly );

응답 인스턴스가 아직 없는 상황에서 쿠키를 보내야 할 경우, Cookie 파사드의 queue 메서드를 사용하여 쿠키를 큐에 등록할 수 있습니다. 등록된 쿠키는 응답이 브라우저로 전송되기 전에 자동으로 첨부됩니다:

use Illuminate\Support\Facades\Cookie; Cookie::queue('name', 'value', $minutes);

쿠키 인스턴스 생성하기

나중에 응답에 첨부할 Symfony\Component\HttpFoundation\Cookie 인스턴스를 미리 만들고 싶다면 전역 cookie 헬퍼를 사용하세요. 이렇게 생성된 쿠키는 응답 인스턴스에 직접 첨부하기 전까지는 클라이언트로 전송되지 않습니다:

$cookie = cookie('name', 'value', $minutes); return response('Hello World')->cookie($cookie);

쿠키 조기 만료

응답에서 특정 쿠키를 삭제하려면 withoutCookie 또는 withoutCookies 메서드를 사용합니다:

return response('Hello World')->withoutCookie('name'); return response('Hello World')->withoutCookies([ 'name', 'email', 'preferences', ]);

응답 인스턴스가 없는 경우에는 Cookie 파사드의 expire 메서드로 쿠키를 만료시킬 수 있습니다:

Cookie::expire('name');

쿠키와 암호화

기본적으로 Laravel은 Illuminate\Cookie\Middleware\EncryptCookies 미들웨어를 통해 생성되는 모든 쿠키를 암호화하고 서명합니다. 이 덕분에 클라이언트가 쿠키 값을 임의로 읽거나 변조할 수 없습니다. 일부 쿠키의 암호화를 비활성화하려면 bootstrap/app.php 파일에서 encryptCookies 메서드를 사용하세요:

->withMiddleware(function (Middleware $middleware): void { $middleware->encryptCookies(except: [ 'cookie_name', ]); })

NOTE

쿠키 암호화를 비활성화하면 클라이언트 측에서 쿠키 내용을 직접 읽거나 변조할 수 있게 됩니다. 특별한 이유가 없다면 암호화를 비활성화하지 않는 것을 강력히 권장합니다.

리다이렉트

리다이렉트 응답은 Illuminate\Http\RedirectResponse 클래스의 인스턴스이며, 사용자를 다른 URL로 이동시키는 데 필요한 헤더를 포함합니다. RedirectResponse 인스턴스를 생성하는 방법은 여러 가지가 있습니다. 가장 간단한 방법은 전역 redirect 헬퍼를 사용하는 것입니다.

Route::get('/dashboard', function () { return redirect('/home/dashboard'); });

폼 유효성 검사가 실패했을 때처럼, 사용자를 이전 페이지로 되돌려야 할 때는 전역 back 헬퍼를 사용합니다. 이 기능은 세션을 활용하므로, back 함수를 호출하는 라우트가 반드시 web 미들웨어 그룹을 사용하고 있어야 합니다.

Route::post('/user/profile', function () { // 요청 유효성 검사... return back()->withInput(); });

이름이 지정된 라우트로 리다이렉트

redirect 헬퍼를 인자 없이 호출하면 Illuminate\Routing\Redirector 인스턴스가 반환되며, 이 인스턴스의 다양한 메서드를 체이닝하여 사용할 수 있습니다. 예를 들어, 이름이 지정된 라우트로 리다이렉트하려면 route 메서드를 사용합니다.

return redirect()->route('login');

라우트에 파라미터가 있는 경우, route 메서드의 두 번째 인자로 전달합니다.

// URI가 /profile/{id}인 라우트로 리다이렉트 return redirect()->route('profile', ['id' => 1]);

Eloquent 모델로 파라미터 채우기

id와 같은 파라미터를 Eloquent 모델에서 가져오는 경우, 모델 인스턴스를 그대로 전달할 수 있습니다. Laravel이 자동으로 ID를 추출합니다.

// URI가 /profile/{id}인 라우트로 리다이렉트 return redirect()->route('profile', [$user]);

라우트 파라미터에 들어갈 값을 커스터마이즈하려면, 라우트 파라미터 정의에서 컬럼을 지정하거나(/profile/{id:slug}), Eloquent 모델에서 getRouteKey 메서드를 오버라이드합니다.

/** * 모델의 라우트 키 값을 반환합니다. */ public function getRouteKey(): mixed { return $this->slug; }

컨트롤러 액션으로 리다이렉트

컨트롤러 액션으로 직접 리다이렉트할 수도 있습니다. action 메서드에 컨트롤러 클래스와 메서드 이름을 배열로 전달하면 됩니다.

use App\Http\Controllers\UserController; return redirect()->action([UserController::class, 'index']);

컨트롤러 라우트에 파라미터가 필요한 경우, action 메서드의 두 번째 인자로 전달합니다.

return redirect()->action( [UserController::class, 'profile'], ['id' => 1] );

외부 도메인으로 리다이렉트

애플리케이션 외부의 도메인으로 리다이렉트해야 할 때는 away 메서드를 사용합니다. 이 메서드는 URL 인코딩, 유효성 검사, 검증 없이 RedirectResponse를 생성합니다.

return redirect()->away('https://www.google.com');

세션 플래시 데이터와 함께 리다이렉트

리다이렉트와 동시에 세션에 데이터를 플래시하는 경우가 많습니다. 주로 어떤 작업이 성공적으로 완료된 후 성공 메시지를 세션에 저장할 때 활용합니다. RedirectResponse 인스턴스에서 메서드를 체이닝하면 리다이렉트와 플래시를 한 번에 처리할 수 있습니다.

Route::post('/user/profile', function () { // ... return redirect('/dashboard')->with('status', '프로필이 업데이트되었습니다!'); });

리다이렉트 후 세션에 저장된 플래시 메시지를 Blade 문법으로 다음과 같이 표시할 수 있습니다.

@if (session('status')) <div class="alert alert-success"> {{ session('status') }} </div> @endif

입력값과 함께 리다이렉트

RedirectResponsewithInput 메서드를 사용하면, 리다이렉트 전에 현재 요청의 입력값을 세션에 플래시할 수 있습니다. 유효성 검사 오류가 발생했을 때 폼 입력값을 유지하는 데 주로 사용합니다. 입력값이 세션에 플래시되면, 다음 요청에서 이전 입력값을 쉽게 가져와 폼을 다시 채울 수 있습니다.

return back()->withInput();

다양한 응답 타입

response 헬퍼를 활용하면 다양한 형태의 응답 인스턴스를 생성할 수 있습니다. 인수 없이 response()를 호출하면 Illuminate\Contracts\Routing\ResponseFactory 컨트랙트의 구현체가 반환되며, 이 컨트랙트는 여러 종류의 응답을 생성하는 유용한 메서드들을 제공합니다.

뷰 응답

HTTP 상태 코드나 헤더를 직접 지정하면서 를 응답 본문으로 반환하고 싶을 때는 view 메서드를 사용합니다.

return response() ->view('hello', $data, 200) ->header('Content-Type', $type);

상태 코드나 커스텀 헤더가 필요 없다면, 전역 view 헬퍼 함수를 그대로 사용해도 충분합니다.

JSON 응답

json 메서드는 Content-Type 헤더를 자동으로 application/json으로 설정하고, 전달된 배열을 PHP의 json_encode 함수를 사용해 JSON으로 변환합니다.

return response()->json([ 'name' => '홍길동', 'city' => '서울', ]);

JSONP 응답이 필요하다면 json 메서드와 withCallback 메서드를 함께 사용합니다.

return response() ->json(['name' => '홍길동', 'city' => '서울']) ->withCallback($request->input('callback'));

파일 다운로드

download 메서드를 사용하면 지정한 경로의 파일을 브라우저가 다운로드하도록 강제하는 응답을 생성할 수 있습니다. 두 번째 인수로 사용자에게 보여질 파일명을, 세 번째 인수로 HTTP 헤더 배열을 전달할 수 있습니다.

return response()->download($pathToFile); return response()->download($pathToFile, $name, $headers);

WARNING

파일 다운로드를 처리하는 Symfony HttpFoundation은 다운로드 파일명이 ASCII 문자로 구성되어 있어야 합니다. 한글 파일명을 사용할 경우 브라우저에 따라 파일명이 깨질 수 있으므로, 한글 파일명은 별도로 인코딩하거나 영문으로 변환하는 처리를 권장합니다.

파일 응답 (브라우저 직접 표시)

file 메서드는 이미지나 PDF처럼 다운로드를 유도하지 않고 브라우저에서 바로 파일을 표시할 때 사용합니다. 첫 번째 인수로 파일의 절대 경로를, 두 번째 인수로 헤더 배열을 전달합니다.

return response()->file($pathToFile); return response()->file($pathToFile, $headers);

NOTE

downloadfile의 차이를 정리하면: download는 브라우저가 파일을 저장하도록 유도하고, file은 브라우저가 파일을 직접 렌더링합니다. PDF나 이미지처럼 브라우저가 기본적으로 열 수 있는 파일은 file을 사용하는 것이 자연스럽습니다.

스트리밍 응답

데이터를 생성하는 즉시 클라이언트로 전송하면 메모리 사용량을 크게 줄이고 성능을 향상시킬 수 있습니다. 특히 응답 크기가 매우 클 때 효과적입니다. 스트리밍 응답을 사용하면 서버가 전송을 완료하기 전에 클라이언트가 데이터를 먼저 처리하기 시작할 수 있습니다:

Route::get('/stream', function () { return response()->stream(function (): void { foreach (['developer', 'admin'] as $string) { echo $string; ob_flush(); flush(); sleep(2); // 청크 사이의 지연 시뮬레이션... } }, 200, ['X-Accel-Buffering' => 'no']); });

stream 메서드에 전달한 클로저가 Generator를 반환하는 경우, Laravel은 Generator가 반환하는 각 문자열 사이에 자동으로 출력 버퍼를 플러시하고 Nginx 출력 버퍼링도 비활성화합니다:

Route::post('/chat', function () { return response()->stream(function (): Generator { $stream = OpenAI::client()->chat()->createStreamed(...); foreach ($stream as $response) { yield $response->choices[0]; } }); });

스트리밍 응답 소비하기

스트리밍 응답은 Laravel의 stream npm 패키지를 통해 편리하게 사용할 수 있습니다. 이 패키지는 Laravel 응답 스트림 및 이벤트 스트림과 상호작용하기 위한 API를 제공합니다. 시작하려면 @laravel/stream-react, @laravel/stream-vue, 또는 @laravel/stream-svelte 패키지를 설치합니다:

React

npm install @laravel/stream-react

Vue

npm install @laravel/stream-vue

Svelte

npm install @laravel/stream-svelte

이후 useStream 훅을 사용해 이벤트 스트림을 소비할 수 있습니다. 스트림 URL을 전달하면 Laravel 애플리케이션에서 콘텐츠가 반환될 때 data가 자동으로 누적된 응답으로 업데이트됩니다:

React

import { useStream } from "@laravel/stream-react"; function App() { const { data, isFetching, isStreaming, send } = useStream("chat"); const sendMessage = () => { send({ message: `현재 타임스탬프: ${Date.now()}`, }); }; return ( <div> <div>{data}</div> {isFetching && <div>연결 중...</div>} {isStreaming && <div>생성 중...</div>} <button onClick={sendMessage}>메시지 전송</button> </div> ); }

Vue

<script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; const { data, isFetching, isStreaming, send } = useStream("chat"); const sendMessage = () => { send({ message: `현재 타임스탬프: ${Date.now()}`, }); }; </script> <template> <div> <div>{{ data }}</div> <div v-if="isFetching">연결 중...</div> <div v-if="isStreaming">생성 중...</div> <button @click="sendMessage">메시지 전송</button> </div> </template>

Svelte

<script> import { useStream } from "@laravel/stream-svelte"; const stream = useStream("chat"); const sendMessage = () => { stream.send({ message: `현재 타임스탬프: ${Date.now()}`, }); }; </script> <div> <div>{$stream.data}</div> {#if $stream.isFetching} <div>연결 중...</div> {/if} {#if $stream.isStreaming} <div>생성 중...</div> {/if} <button onclick={sendMessage}>메시지 전송</button> </div>

send를 통해 데이터를 다시 스트림으로 전송하면, 새 데이터를 보내기 전에 현재 활성화된 스트림 연결이 취소됩니다. 모든 요청은 JSON POST 요청으로 전송됩니다.

WARNING

useStream 훅은 애플리케이션에 POST 요청을 보내므로 유효한 CSRF 토큰이 필요합니다. 가장 간단한 방법은 애플리케이션 레이아웃의 <head>에 메타 태그로 CSRF 토큰을 포함하는 것입니다.

useStream의 두 번째 인수로 옵션 객체를 전달하여 스트림 소비 동작을 커스터마이징할 수 있습니다. 각 옵션의 기본값은 아래와 같습니다:

React

import { useStream } from "@laravel/stream-react"; function App() { const { data } = useStream("chat", { id: undefined, initialInput: undefined, headers: undefined, csrfToken: undefined, onResponse: (response: Response) => void, onData: (data: string) => void, onCancel: () => void, onFinish: () => void, onError: (error: Error) => void, }); return <div>{data}</div>; }

Vue

<script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; const { data } = useStream("chat", { id: undefined, initialInput: undefined, headers: undefined, csrfToken: undefined, onResponse: (response: Response) => void, onData: (data: string) => void, onCancel: () => void, onFinish: () => void, onError: (error: Error) => void, }); </script> <template> <div>{{ data }}</div> </template>

Svelte

<script> import { useStream } from "@laravel/stream-svelte"; const stream = useStream("chat", { id: undefined, initialInput: undefined, headers: undefined, csrfToken: undefined, onResponse: (response) => {}, onData: (data) => {}, onCancel: () => {}, onFinish: () => {}, onError: (error) => {}, }); </script> <div>{$stream.data}</div>

각 콜백의 동작:

  • onResponse — 스트림으로부터 최초 응답이 성공적으로 도착했을 때 호출되며, 원시 Response 객체가 전달됩니다.
  • onData — 각 청크가 수신될 때마다 호출되며, 현재 청크가 전달됩니다.
  • onFinish — 스트림이 완료되었을 때, 또는 fetch/read 사이클 중 에러가 발생했을 때 호출됩니다.

기본적으로 초기화 시점에는 스트림으로 요청이 전송되지 않습니다. initialInput 옵션을 사용하면 초기 페이로드를 스트림에 전달할 수 있습니다:

React

import { useStream } from "@laravel/stream-react"; function App() { const { data } = useStream("chat", { initialInput: { message: "자기소개를 해주세요.", }, }); return <div>{data}</div>; }

Vue

<script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; const { data } = useStream("chat", { initialInput: { message: "자기소개를 해주세요.", }, }); </script> <template> <div>{{ data }}</div> </template>

Svelte

<script> import { useStream } from "@laravel/stream-svelte"; const stream = useStream("chat", { initialInput: { message: "자기소개를 해주세요.", }, }); </script> <div>{$stream.data}</div>

스트림을 수동으로 취소하려면 훅에서 반환되는 cancel 메서드를 사용합니다:

React

import { useStream } from "@laravel/stream-react"; function App() { const { data, cancel } = useStream("chat"); return ( <div> <div>{data}</div> <button onClick={cancel}>취소</button> </div> ); }

Vue

<script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; const { data, cancel } = useStream("chat"); </script> <template> <div> <div>{{ data }}</div> <button @click="cancel">취소</button> </div> </template>

Svelte

<script> import { useStream } from "@laravel/stream-svelte"; const stream = useStream("chat"); </script> <div> <div>{$stream.data}</div> <button onclick={() => stream.cancel()}>취소</button> </div>

useStream 훅을 사용할 때마다 스트림을 식별하기 위한 랜덤 id가 생성됩니다. 이 값은 매 요청마다 X-STREAM-ID 헤더로 서버에 전달됩니다. 동일한 스트림을 여러 컴포넌트에서 공유하려면, 직접 id를 지정하여 스트림을 읽고 쓸 수 있습니다:

React

// App.tsx import { useStream } from "@laravel/stream-react"; function App() { const { data, id } = useStream("chat"); return ( <div> <div>{data}</div> <StreamStatus id={id} /> </div> ); } // StreamStatus.tsx import { useStream } from "@laravel/stream-react"; function StreamStatus({ id }) { const { isFetching, isStreaming } = useStream("chat", { id }); return ( <div> {isFetching && <div>연결 중...</div>} {isStreaming && <div>생성 중...</div>} </div> ); }

Vue

<!-- App.vue --> <script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; import StreamStatus from "./StreamStatus.vue"; const { data, id } = useStream("chat"); </script> <template> <div> <div>{{ data }}</div> <StreamStatus :id="id" /> </div> </template> <!-- StreamStatus.vue --> <script setup lang="ts"> import { useStream } from "@laravel/stream-vue"; const props = defineProps<{ id: string; }>(); const { isFetching, isStreaming } = useStream("chat", { id: props.id }); </script> <template> <div> <div v-if="isFetching">연결 중...</div> <div v-if="isStreaming">생성 중...</div> </div> </template>

Svelte

<!-- App.svelte --> <script> import { useStream } from "@laravel/stream-svelte"; import StreamStatus from "./StreamStatus.svelte"; const stream = useStream("chat"); </script> <div> <div>{$stream.data}</div> <StreamStatus id={stream.id} /> </div> <!-- StreamStatus.svelte --> <script> import { useStream } from "@laravel/stream-svelte"; let { id } = $props(); const stream = useStream("chat", { id }); </script> <div> {#if $stream.isFetching} <div>연결 중...</div> {/if} {#if $stream.isStreaming} <div>생성 중...</div> {/if} </div>

스트리밍 JSON 응답

JSON 데이터를 점진적으로 스트리밍해야 하는 경우 streamJson 메서드를 사용할 수 있습니다. 이 메서드는 JavaScript에서 파싱하기 쉬운 형식으로 대용량 데이터셋을 브라우저에 순차적으로 전송할 때 특히 유용합니다:

use App\Models\User; Route::get('/users.json', function () { return response()->streamJson([ 'users' => User::cursor(), ]); });

useJsonStream 훅은 useStream 훅과 동일하게 동작하지만, 스트리밍이 완료된 후 데이터를 JSON으로 파싱한다는 점이 다릅니다:

React

import { useJsonStream } from "@laravel/stream-react"; type User = { id: number; name: string; email: string; }; function App() { const { data, send } = useJsonStream<{ users: User[] }>("users"); const loadUsers = () => { send({ query: "김", }); }; return ( <div> <ul> {data?.users.map((user) => ( <li> {user.id}: {user.name} </li> ))} </ul> <button onClick={loadUsers}>사용자 불러오기</button> </div> ); }

Vue

<script setup lang="ts"> import { useJsonStream } from "@laravel/stream-vue"; type User = { id: number; name: string; email: string; }; const { data, send } = useJsonStream<{ users: User[] }>("users"); const loadUsers = () => { send({ query: "김", }); }; </script> <template> <div> <ul> <li v-for="user in data?.users" :key="user.id"> {{ user.id }}: {{ user.name }} </li> </ul> <button @click="loadUsers">사용자 불러오기</button> </div> </template>

Svelte

<script> import { useJsonStream } from "@laravel/stream-svelte"; const stream = useJsonStream("users"); const loadUsers = () => { stream.send({ query: "김", }); }; </script> <div> <ul> {#if $stream.data?.users} {#each $stream.data.users as user (user.id)} <li>{user.id}: {user.name}</li> {/each} {/if} </ul> <button onclick={loadUsers}>사용자 불러오기</button> </div>

이벤트 스트림 (SSE)

eventStream 메서드를 사용하면 text/event-stream 콘텐츠 타입으로 서버 전송 이벤트(SSE) 스트리밍 응답을 반환할 수 있습니다. eventStream 메서드는 응답이 준비될 때마다 yield로 스트림에 전달하는 클로저를 인수로 받습니다:

Route::get('/chat', function () { return response()->eventStream(function () { $stream = OpenAI::client()->chat()->createStreamed(...); foreach ($stream as $response) { yield $response->choices[0]; } }); });

이벤트 이름을 직접 지정하려면 StreamedEvent 클래스의 인스턴스를 yield하면 됩니다:

use Illuminate\Http\StreamedEvent; yield new StreamedEvent( event: 'update', data: $response->choices[0], );

이벤트 스트림 소비하기

이벤트 스트림도 Laravel의 stream npm 패키지를 통해 소비할 수 있습니다. 아직 설치하지 않았다면 @laravel/stream-react, @laravel/stream-vue, 또는 @laravel/stream-svelte 패키지를 설치합니다:

React

npm install @laravel/stream-react

Vue

npm install @laravel/stream-vue

Svelte

npm install @laravel/stream-svelte

이후 useEventStream 훅을 사용해 이벤트 스트림을 소비할 수 있습니다. 스트림 URL을 전달하면 Laravel 애플리케이션에서 메시지가 반환될 때 message가 자동으로 누적된 응답으로 업데이트됩니다:

React

import { useEventStream } from "@laravel/stream-react"; function App() { const { message } = useEventStream("/chat"); return <div>{message}</div>; }

Vue

<script setup lang="ts"> import { useEventStream } from "@laravel/stream-vue"; const { message } = useEventStream("/chat"); </script> <template> <div>{{ message }}</div> </template>

Svelte

<script> import { useEventStream } from "@laravel/stream-svelte"; const eventStream = useEventStream("/chat"); </script> <div>{$eventStream.message}</div>

useEventStream의 두 번째 인수로 옵션 객체를 전달하여 동작을 커스터마이징할 수 있습니다. 각 옵션의 기본값은 아래와 같습니다:

React

import { useEventStream } from "@laravel/stream-react"; function App() { const { message } = useEventStream("/stream", { eventName: "update", onMessage: (message) => { // }, onError: (error) => { // }, onComplete: () => { // }, endSignal: "</stream>", glue: " ", }); return <div>{message}</div>; }

Vue

<script setup lang="ts"> import { useEventStream } from "@laravel/stream-vue"; const { message } = useEventStream("/chat", { eventName: "update", onMessage: (message) => { // ... }, onError: (error) => { // ... }, onComplete: () => { // ... }, endSignal: "</stream>", glue: " ", }); </script>

Svelte

<script> import { useEventStream } from "@laravel/stream-svelte"; const eventStream = useEventStream("/chat", { eventName: "update", onMessage: (event) => { // }, onError: (error) => { // }, onComplete: () => { // }, endSignal: "</stream>", glue: " ", replace: false, }); </script>

이벤트 스트림은 프론트엔드에서 EventSource 객체를 사용해 직접 소비할 수도 있습니다. eventStream 메서드는 스트림이 완료되면 자동으로 </stream> 업데이트를 이벤트 스트림에 전송합니다:

const source = new EventSource('/chat'); source.addEventListener('update', (event) => { if (event.data === '</stream>') { source.close(); return; } console.log(event.data); });

스트림 종료 시 전송되는 마지막 이벤트를 커스터마이징하려면 eventStream 메서드의 endStreamWith 인수에 StreamedEvent 인스턴스를 전달합니다:

return response()->eventStream(function () { // ... }, endStreamWith: new StreamedEvent(event: 'update', data: '</stream>'));

스트리밍 다운로드

작업의 문자열 응답을 디스크에 저장하지 않고 바로 다운로드 가능한 응답으로 변환하고 싶을 때 streamDownload 메서드를 사용할 수 있습니다. 이 메서드는 콜백, 파일명, 그리고 선택적으로 헤더 배열을 인수로 받습니다:

use App\Services\GitHub; return response()->streamDownload(function () { echo GitHub::api('repo') ->contents() ->readme('laravel', 'laravel')['contents']; }, 'laravel-readme.md');

응답 매크로

여러 라우트나 컨트롤러에서 반복적으로 사용할 커스텀 응답 형식이 있다면, Response 파사드의 macro 메서드로 재사용 가능한 응답 매크로를 정의할 수 있습니다. 일반적으로 App\Providers\AppServiceProvider와 같은 서비스 프로바이더boot 메서드 안에서 등록합니다.

<?php namespace App\Providers; use Illuminate\Support\Facades\Response; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Response::macro('caps', function (string $value) { return Response::make(strtoupper($value)); }); } }

macro 메서드는 첫 번째 인수로 매크로 이름, 두 번째 인수로 클로저를 받습니다. 등록된 매크로는 ResponseFactory 구현체 또는 response 헬퍼에서 해당 이름으로 호출하면 클로저가 실행됩니다.

return response()->caps('foo');

NOTE

응답 매크로는 공통 응답 포맷(예: API 표준 응답 구조, 특정 헤더 조합 등)을 애플리케이션 전역에서 일관되게 사용하고 싶을 때 특히 유용합니다. 반복되는 응답 로직을 매크로로 추출하면 코드 중복을 줄이고 유지보수성을 높일 수 있습니다.

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

번역일: 2026년 8월 10일