HTTP 응답
번역일: 2026년 6월 21일
HTTP 응답
응답 생성
문자열과 배열
모든 라우트와 컨트롤러는 사용자의 브라우저에 돌려줄 응답을 반환해야 합니다. Laravel은 응답을 반환하는 다양한 방법을 제공합니다. 가장 간단한 형태는 라우트나 컨트롤러에서 문자열을 반환하는 것입니다. 프레임워크가 이 문자열을 자동으로 완전한 HTTP 응답으로 변환해 줍니다.
Route::get('/', function () {
return 'Hello World';
});문자열뿐만 아니라 배열도 반환할 수 있습니다. 배열은 자동으로 JSON 응답으로 변환됩니다.
Route::get('/', function () {
return [1, 2, 3];
});NOTE
라우트나 컨트롤러에서 Eloquent 컬렉션을 직접 반환할 수도 있습니다. 컬렉션도 자동으로 JSON으로 변환됩니다.
응답 객체
실제 개발에서는 단순한 문자열이나 배열을 반환하는 것보다 Illuminate\Http\Response 인스턴스나 뷰를 반환하는 경우가 훨씬 많습니다.
Response 인스턴스를 반환하면 HTTP 상태 코드와 헤더를 자유롭게 설정할 수 있습니다. Response는 Symfony\Component\HttpFoundation\Response 클래스를 상속하며, HTTP 응답 구성에 필요한 다양한 메서드를 제공합니다.
Route::get('/home', function () {
return response('Hello World', 200)
->header('Content-Type', 'text/plain');
});Eloquent 모델과 컬렉션
Eloquent ORM 모델과 컬렉션도 라우트나 컨트롤러에서 직접 반환할 수 있습니다. 이 경우 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 지시어의 스네이크 케이스 형태로 작성하며, 세미콜론으로 구분합니다. 디렉티브 목록에 etag를 포함하면 응답 내용의 MD5 해시가 ETag 식별자로 자동 설정됩니다.
Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () {
Route::get('/privacy', function () {
// ...
});
Route::get('/terms', function () {
// ...
});
});쿠키 추가
cookie 메서드를 사용하면 Illuminate\Http\Response 인스턴스에 쿠키를 첨부할 수 있습니다. 이름, 값, 유효 기간(분 단위)을 인수로 전달합니다.
return response('Hello World')->cookie(
'name', 'value', $minutes
);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 메서드를 사용하면 쿠키를 즉시 만료시켜 삭제할 수 있습니다.
return response('Hello World')->withoutCookie('name');응답 인스턴스가 아직 없는 경우에는 Cookie 파사드의 expire 메서드를 사용하세요.
Cookie::expire('name');쿠키와 암호화
기본적으로 Illuminate\Cookie\Middleware\EncryptCookies 미들웨어 덕분에, Laravel이 생성하는 모든 쿠키는 암호화되고 서명됩니다. 따라서 클라이언트가 쿠키 값을 읽거나 변조할 수 없습니다.
특정 쿠키의 암호화를 비활성화하려면 bootstrap/app.php 파일에서 encryptCookies 메서드를 사용하세요.
->withMiddleware(function (Middleware $middleware) {
$middleware->encryptCookies(except: [
'cookie_name',
]);
})리다이렉트
리다이렉트 응답은 Illuminate\Http\RedirectResponse 클래스의 인스턴스이며, 사용자를 다른 URL로 이동시키는 데 필요한 헤더를 포함합니다. 가장 간단한 방법은 전역 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 모델로 파라미터 채우기
ID 파라미터를 포함하는 라우트로 리다이렉트할 때 Eloquent 모델을 직접 전달하면, 모델의 기본 키가 자동으로 추출됩니다.
// 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']);파라미터가 필요한 경우 두 번째 인수로 전달합니다.
return redirect()->action(
[UserController::class, 'profile'], ['id' => 1]
);외부 도메인으로 리다이렉트
애플리케이션 외부의 도메인으로 리다이렉트해야 할 때는 away 메서드를 사용하세요. 이 메서드는 별도의 URL 인코딩, 유효성 검사, 검증 없이 RedirectResponse를 생성합니다.
return redirect()->away('https://www.google.com');세션 플래시 데이터와 함께 리다이렉트
리다이렉트와 세션에 데이터 플래시는 보통 함께 사용됩니다. 예를 들어, 어떤 작업이 성공한 후 성공 메시지를 세션에 담고 리다이렉트하는 패턴이 일반적입니다. 메서드 체이닝으로 한 번에 처리할 수 있습니다.
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 헬퍼를 인수 없이 호출하면 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 함수로 변환합니다.
return response()->json([
'name' => '홍길동',
'state' => 'CA',
]);JSONP 응답이 필요하다면 json 메서드와 withCallback 메서드를 함께 사용하세요.
return response()
->json(['name' => '홍길동', 'state' => 'CA'])
->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);스트리밍 응답
데이터를 생성하는 즉시 클라이언트로 전송하면 메모리 사용량을 크게 줄이고 성능을 향상시킬 수 있습니다. 특히 응답 크기가 매우 클 때 효과적입니다. 스트리밍 응답을 사용하면 서버가 데이터를 모두 생성하기 전에 클라이언트가 먼저 수신을 시작할 수 있습니다.
function streamedContent(): Generator {
yield 'Hello, ';
yield 'World!';
}
Route::get('/stream', function () {
return response()->stream(function (): void {
foreach (streamedContent() as $chunk) {
echo $chunk;
ob_flush();
flush();
sleep(2); // 청크 사이의 지연 시뮬레이션...
}
}, 200, ['X-Accel-Buffering' => 'no']);
});NOTE
Laravel은 내부적으로 PHP의 출력 버퍼링을 활용합니다. 위 예시처럼 버퍼링된 내용을 클라이언트로 즉시 전달하려면 ob_flush와 flush 함수를 함께 호출해야 합니다.
스트리밍 JSON 응답
JSON 데이터를 점진적으로 스트리밍해야 한다면 streamJson 메서드를 사용하세요. JavaScript에서 파싱하기 쉬운 형태로 대용량 데이터셋을 순차적으로 전송할 때 특히 유용합니다.
use App\Models\User;
Route::get('/users.json', function () {
return response()->streamJson([
'users' => User::cursor(),
]);
});이벤트 스트림 (SSE)
eventStream 메서드를 사용하면 text/event-stream 콘텐츠 타입을 사용하는 서버 전송 이벤트(SSE) 응답을 반환할 수 있습니다. 클로저를 전달하며, 해당 클로저 안에서 데이터가 준비될 때마다 yield로 응답을 스트림에 전달합니다.
Route::get('/chat', function () {
return response()->eventStream(function () {
$stream = OpenAI::client()->chat()->createStreamed(...);
foreach ($stream as $response) {
yield $response->choices[0];
}
});
});프론트엔드에서는 EventSource 객체로 이 이벤트 스트림을 수신할 수 있습니다. eventStream 메서드는 스트림이 종료되면 자동으로 </stream> 업데이트를 전송합니다.
const source = new EventSource('/chat');
source.addEventListener('update', (event) => {
if (event.data === '</stream>') {
source.close();
return;
}
console.log(event.data);
})스트리밍 다운로드
어떤 작업의 결과를 디스크에 저장하지 않고 바로 다운로드 가능한 응답으로 만들고 싶을 때 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');