HTTP 클라이언트

번역일: 2026년 6월 27일

HTTP 클라이언트

소개

Laravel은 Guzzle HTTP 클라이언트를 기반으로 하는 간결하고 직관적인 HTTP 클라이언트를 제공합니다. 외부 API나 웹 서비스에 HTTP 요청을 보낼 때 복잡한 Guzzle 설정 없이 편리하게 사용할 수 있습니다. 자주 쓰이는 사용 패턴에 맞춰 설계되어 있어 개발 생산성을 높여줍니다.

HTTP 클라이언트

소개

Laravel은 Guzzle HTTP 클라이언트를 기반으로 간결하고 표현력 있는 API를 제공합니다. 외부 웹 애플리케이션과 HTTP 통신이 필요할 때 복잡한 Guzzle 설정 없이 가장 흔한 사용 패턴을 쉽고 직관적으로 처리할 수 있도록 설계되어 있습니다.

요청 보내기

Http 파사드가 제공하는 head, get, post, put, patch, delete 메서드로 HTTP 요청을 보낼 수 있습니다. 가장 기본적인 GET 요청부터 살펴보겠습니다.

use Illuminate\Support\Facades\Http; $response = Http::get('http://example.com');

get 메서드는 Illuminate\Http\Client\Response 인스턴스를 반환하며, 응답을 확인하는 다양한 메서드를 제공합니다.

$response->body() : string; $response->json($key = null, $default = null) : mixed; $response->object() : object; $response->collect($key = null) : Illuminate\Support\Collection; $response->resource() : resource; $response->status() : int; $response->successful() : bool; $response->redirect(): bool; $response->failed() : bool; $response->clientError() : bool; $response->header($header) : string; $response->headers() : array;

Illuminate\Http\Client\Response 객체는 PHP의 ArrayAccess 인터페이스를 구현하므로, JSON 응답 데이터에 배열처럼 직접 접근할 수 있습니다.

return Http::get('http://example.com/users/1')['name'];

위의 메서드 외에도 아래 메서드로 특정 HTTP 상태 코드 여부를 간편하게 확인할 수 있습니다.

$response->ok() : bool; // 200 OK $response->created() : bool; // 201 Created $response->accepted() : bool; // 202 Accepted $response->noContent() : bool; // 204 No Content $response->movedPermanently() : bool; // 301 Moved Permanently $response->found() : bool; // 302 Found $response->badRequest() : bool; // 400 Bad Request $response->unauthorized() : bool; // 401 Unauthorized $response->paymentRequired() : bool; // 402 Payment Required $response->forbidden() : bool; // 403 Forbidden $response->notFound() : bool; // 404 Not Found $response->requestTimeout() : bool; // 408 Request Timeout $response->conflict() : bool; // 409 Conflict $response->unprocessableEntity() : bool; // 422 Unprocessable Entity $response->tooManyRequests() : bool; // 429 Too Many Requests $response->serverError() : bool; // 500 Internal Server Error

URI 템플릿

URI 템플릿 명세(RFC 6570)를 활용해 요청 URL을 동적으로 구성할 수 있습니다. withUrlParameters 메서드로 URL에 삽입할 파라미터를 정의하세요.

Http::withUrlParameters([ 'endpoint' => 'https://laravel.com', 'page' => 'docs', 'version' => '12.x', 'topic' => 'validation', ])->get('{+endpoint}/{page}/{version}/{topic}');

요청 디버깅

요청을 실제로 전송하기 전에 내용을 덤프하고 스크립트 실행을 중단하고 싶다면, 요청 체인 앞에 dd 메서드를 추가하세요.

return Http::dd()->get('http://example.com');

요청 데이터 전송

POST, PUT, PATCH 요청을 보낼 때는 두 번째 인수로 데이터 배열을 전달합니다. 기본적으로 데이터는 application/json 콘텐츠 타입으로 전송됩니다.

use Illuminate\Support\Facades\Http; $response = Http::post('http://example.com/users', [ 'name' => '홍길동', 'role' => '관리자', ]);

GET 요청 쿼리 파라미터

GET 요청 시 쿼리 스트링은 URL에 직접 붙이거나, get 메서드의 두 번째 인수로 키/값 배열을 전달하는 방법 중 하나를 선택할 수 있습니다.

$response = Http::get('http://example.com/users', [ 'name' => 'Taylor', 'page' => 1, ]);

또는 withQueryParameters 메서드를 사용할 수도 있습니다. retry 등 다른 메서드와 체이닝할 때 특히 유용합니다.

Http::retry(3, 100)->withQueryParameters([ 'name' => 'Taylor', 'page' => 1, ])->get('http://example.com/users');

Form URL 인코딩 요청 전송

application/x-www-form-urlencoded 형식으로 데이터를 전송하려면 요청 전에 asForm 메서드를 호출하세요.

$response = Http::asForm()->post('http://example.com/users', [ 'name' => '김철수', 'role' => '개인정보 보호 담당자', ]);

Raw 요청 바디 전송

요청 바디를 직접 지정해야 할 때는 withBody 메서드를 사용합니다. 두 번째 인수로 콘텐츠 타입을 지정합니다.

$response = Http::withBody( base64_encode($photo), 'image/jpeg' )->post('http://example.com/photo');

멀티파트 요청

파일을 멀티파트 형식으로 전송하려면 요청 전에 attach 메서드를 호출하세요. 첫 번째 인수는 필드 이름, 두 번째는 파일 내용입니다. 세 번째 인수로 파일명, 네 번째 인수로 해당 파일의 헤더를 지정할 수 있습니다.

$response = Http::attach( 'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg'] )->post('http://example.com/attachments');

파일의 raw 내용 대신 스트림 리소스를 전달할 수도 있습니다.

$photo = fopen('photo.jpg', 'r'); $response = Http::attach( 'attachment', $photo, 'photo.jpg' )->post('http://example.com/attachments');

헤더

withHeaders 메서드에 키/값 배열을 전달해 요청 헤더를 추가할 수 있습니다.

$response = Http::withHeaders([ 'X-First' => 'foo', 'X-Second' => 'bar' ])->post('http://example.com/users', [ 'name' => 'Taylor', ]);

응답으로 기대하는 콘텐츠 타입을 지정하려면 accept 메서드를 사용하세요.

$response = Http::accept('application/json')->get('http://example.com/users');

application/json 응답을 기대하는 경우가 많다면, acceptJson 메서드로 간편하게 처리할 수 있습니다.

$response = Http::acceptJson()->get('http://example.com/users');

withHeaders는 기존 헤더에 새 헤더를 병합합니다. 기존 헤더를 모두 덮어쓰고 싶다면 replaceHeaders 메서드를 사용하세요.

$response = Http::withHeaders([ 'X-Original' => 'foo', ])->replaceHeaders([ 'X-Replacement' => 'bar', ])->post('http://example.com/users', [ 'name' => 'Taylor', ]);

인증

Basic 인증과 Digest 인증은 각각 withBasicAuth, withDigestAuth 메서드로 설정합니다.

// Basic 인증... $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // Digest 인증... $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */);

Bearer 토큰

Authorization 헤더에 Bearer 토큰을 빠르게 추가하려면 withToken 메서드를 사용하세요.

$response = Http::withToken('token')->post(/* ... */);

타임아웃

timeout 메서드로 응답 대기 최대 시간(초)을 지정할 수 있습니다. 기본값은 30초입니다.

$response = Http::timeout(3)->get(/* ... */);

지정한 시간을 초과하면 Illuminate\Http\Client\ConnectionException이 발생합니다.

서버 연결 시도의 최대 대기 시간은 connectTimeout 메서드로 별도로 지정할 수 있습니다. 기본값은 10초입니다.

$response = Http::connectTimeout(3)->get(/* ... */);

재시도

클라이언트 또는 서버 오류 발생 시 자동으로 요청을 재시도하려면 retry 메서드를 사용하세요. 첫 번째 인수는 최대 시도 횟수, 두 번째 인수는 시도 간 대기 시간(밀리초)입니다.

$response = Http::retry(3, 100)->post(/* ... */);

시도 간 대기 시간을 동적으로 계산하려면 두 번째 인수로 클로저를 전달하세요.

use Exception; $response = Http::retry(3, function (int $attempt, Exception $exception) { return $attempt * 100; })->post(/* ... */);

시도 횟수별 대기 시간을 배열로 지정할 수도 있습니다.

$response = Http::retry([100, 200])->post(/* ... */);

재시도 여부를 직접 제어해야 한다면 세 번째 인수로 callable을 전달하세요. 예를 들어 ConnectionException이 발생한 경우에만 재시도하도록 설정할 수 있습니다.

use Illuminate\Http\Client\PendingRequest; use Throwable; $response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) { return $exception instanceof ConnectionException; })->post(/* ... */);

재시도 전에 요청을 수정해야 하는 경우, callable 내에서 $request 인수를 직접 변경하면 됩니다. 예를 들어 첫 번째 시도에서 인증 오류(401)가 반환됐을 때 새 토큰으로 교체하는 패턴은 아래와 같습니다.

use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\RequestException; use Throwable; $response = Http::withToken($this->getToken())->retry(2, 0, function (Throwable $exception, PendingRequest $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { return false; } $request->withToken($this->getNewToken()); return true; })->post(/* ... */);

모든 시도가 실패하면 Illuminate\Http\Client\RequestException이 발생합니다. 예외를 던지지 않고 마지막 응답을 그대로 반환하려면 throw: false를 전달하세요.

$response = Http::retry(3, 100, throw: false)->post(/* ... */);

WARNING

throw: false로 설정하더라도, 연결 자체가 실패한 경우(즉, 모든 시도가 연결 문제로 실패한 경우)에는 Illuminate\Http\Client\ConnectionException이 여전히 발생합니다.

에러 처리

Laravel HTTP 클라이언트는 Guzzle의 기본 동작과 달리, 400번대나 500번대 응답이 오더라도 예외를 자동으로 던지지 않습니다. 오류 여부는 아래 메서드로 직접 확인해야 합니다.

// 상태 코드가 200 이상 300 미만인지 확인... $response->successful(); // 상태 코드가 400 이상인지 확인... $response->failed(); // 400번대 상태 코드인지 확인... $response->clientError(); // 500번대 상태 코드인지 확인... $response->serverError(); // 클라이언트 또는 서버 오류 발생 시 즉시 콜백 실행... $response->onError(callable $callback);

예외 직접 던지기

응답 상태 코드가 클라이언트/서버 오류를 나타낼 때 Illuminate\Http\Client\RequestException을 명시적으로 던지려면 throw 또는 throwIf 메서드를 사용하세요.

use Illuminate\Http\Client\Response; $response = Http::post(/* ... */); // 클라이언트 또는 서버 오류 시 예외 던지기... $response->throw(); // 조건이 true일 때 오류가 있으면 예외 던지기... $response->throwIf($condition); // 클로저가 true를 반환할 때 오류가 있으면 예외 던지기... $response->throwIf(fn (Response $response) => true); // 조건이 false일 때 오류가 있으면 예외 던지기... $response->throwUnless($condition); // 클로저가 false를 반환할 때 오류가 있으면 예외 던지기... $response->throwUnless(fn (Response $response) => false); // 특정 상태 코드일 때 예외 던지기... $response->throwIfStatus(403); // 특정 상태 코드가 아닐 때 예외 던지기... $response->throwUnlessStatus(200); return $response['user']['id'];

Illuminate\Http\Client\RequestException 인스턴스의 public 프로퍼티 $response로 반환된 응답을 확인할 수 있습니다.

throw 메서드는 오류가 없으면 응답 인스턴스를 그대로 반환하므로, 메서드 체이닝이 가능합니다.

return Http::post(/* ... */)->throw()->json();

예외가 던져지기 전에 추가 로직을 실행하려면 throw에 클로저를 전달하세요. 클로저 실행 후 예외는 자동으로 던져지므로, 클로저 안에서 다시 예외를 던질 필요는 없습니다.

use Illuminate\Http\Client\Response; use Illuminate\Http\Client\RequestException; return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) { // 추가 처리... })->json();

기본적으로 RequestException 메시지는 로깅 또는 보고 시 120자로 잘립니다. 이 동작을 변경하려면 bootstrap/app.phpregistered 콜백에서 truncateAt 또는 dontTruncate 메서드를 사용하세요.

use Illuminate\Http\Client\RequestException; ->registered(function (): void { // 예외 메시지를 240자로 제한... RequestException::truncateAt(240); // 예외 메시지 잘라내기 비활성화... RequestException::dontTruncate(); })

요청 단위로 잘라내기 길이를 지정하려면 truncateExceptionsAt 메서드를 사용하세요.

return Http::truncateExceptionsAt(240)->post(/* ... */);

Guzzle 미들웨어

Laravel HTTP 클라이언트는 내부적으로 Guzzle을 사용하므로, Guzzle 미들웨어를 활용해 송신 요청을 조작하거나 수신 응답을 검사할 수 있습니다.

송신 요청을 수정하려면 withRequestMiddleware 메서드로 미들웨어를 등록하세요.

use Illuminate\Support\Facades\Http; use Psr\Http\Message\RequestInterface; $response = Http::withRequestMiddleware( function (RequestInterface $request) { return $request->withHeader('X-Example', 'Value'); } )->get('http://example.com');

수신 응답을 검사하려면 withResponseMiddleware 메서드를 사용하세요.

use Illuminate\Support\Facades\Http; use Psr\Http\Message\ResponseInterface; $response = Http::withResponseMiddleware( function (ResponseInterface $response) { $header = $response->getHeader('X-Example'); // ... return $response; } )->get('http://example.com');

글로벌 미들웨어

모든 요청과 응답에 공통으로 적용할 미들웨어는 globalRequestMiddlewareglobalResponseMiddleware 메서드로 등록합니다. 일반적으로 AppServiceProviderboot 메서드에서 호출하는 것이 좋습니다.

use Illuminate\Support\Facades\Http; Http::globalRequestMiddleware(fn ($request) => $request->withHeader( 'User-Agent', '내 애플리케이션/1.0' )); Http::globalResponseMiddleware(fn ($response) => $response->withHeader( 'X-Finished-At', now()->toDateTimeString() ));

Guzzle 옵션

withOptions 메서드로 Guzzle 요청 옵션을 추가로 지정할 수 있습니다. 키/값 배열을 인수로 전달하세요.

$response = Http::withOptions([ 'debug' => true, ])->get('http://example.com/users');

글로벌 옵션

모든 요청에 적용할 기본 옵션은 globalOptions 메서드로 설정합니다. 이 역시 AppServiceProviderboot 메서드에서 호출하세요.

use Illuminate\Support\Facades\Http; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Http::globalOptions([ 'allow_redirects' => false, ]); }

동시 요청

여러 HTTP 요청을 순차적으로 보내는 대신, 동시에 여러 요청을 발송하면 성능을 크게 향상시킬 수 있습니다. 특히 응답이 느린 외부 API를 여러 개 호출해야 할 때 효과적입니다.

요청 풀링(Request Pooling)

pool 메서드를 사용하면 여러 요청을 동시에 발송할 수 있습니다. pool 메서드는 클로저를 받으며, 이 클로저에는 Illuminate\Http\Client\Pool 인스턴스가 전달됩니다. 이를 통해 요청 풀에 손쉽게 요청을 추가할 수 있습니다:

use Illuminate\Http\Client\Pool; use Illuminate\Support\Facades\Http; $responses = Http::pool(fn (Pool $pool) => [ $pool->get('http://localhost/first'), $pool->get('http://localhost/second'), $pool->get('http://localhost/third'), ]); return $responses[0]->ok() && $responses[1]->ok() && $responses[2]->ok();

응답은 풀에 추가된 순서대로 배열 인덱스로 접근할 수 있습니다. 필요하다면 as 메서드를 사용해 각 요청에 이름을 붙이고, 이름으로 응답에 접근할 수도 있습니다:

use Illuminate\Http\Client\Pool; use Illuminate\Support\Facades\Http; $responses = Http::pool(fn (Pool $pool) => [ $pool->as('first')->get('http://localhost/first'), $pool->as('second')->get('http://localhost/second'), $pool->as('third')->get('http://localhost/third'), ]); return $responses['first']->ok();

요청 풀의 최대 동시 실행 수는 pool 메서드의 concurrency 인수로 제어할 수 있습니다. 이 값은 풀 처리 중 동시에 진행 중인 HTTP 요청의 최대 개수를 결정합니다:

$responses = Http::pool(fn (Pool $pool) => [ // ... ], concurrency: 5);

동시 요청 커스터마이징

pool 메서드는 withHeadersmiddleware처럼 HTTP 클라이언트의 다른 메서드와 체이닝할 수 없습니다. 풀 내 요청에 커스텀 헤더나 미들웨어를 적용하려면, 각 요청에 개별적으로 옵션을 설정해야 합니다:

use Illuminate\Http\Client\Pool; use Illuminate\Support\Facades\Http; $headers = [ 'X-Example' => 'example', ]; $responses = Http::pool(fn (Pool $pool) => [ $pool->withHeaders($headers)->get('http://laravel.test/test'), $pool->withHeaders($headers)->get('http://laravel.test/test'), $pool->withHeaders($headers)->get('http://laravel.test/test'), ]);

요청 배칭(Request Batching)

동시 요청을 처리하는 또 다른 방법은 batch 메서드를 사용하는 것입니다. pool 메서드와 마찬가지로 클로저를 받으며, 클로저에는 Illuminate\Http\Client\Batch 인스턴스가 전달됩니다. pool과의 차이점은 완료 콜백을 정의할 수 있다는 점입니다. 각 요청의 성공·실패 여부에 따라 다양한 콜백을 체이닝할 수 있습니다:

use Illuminate\Http\Client\Batch; use Illuminate\Http\Client\ConnectionException; use Illuminate\Http\Client\RequestException; use Illuminate\Http\Client\Response; use Illuminate\Support\Facades\Http; $responses = Http::batch(fn (Batch $batch) => [ $batch->get('http://localhost/first'), $batch->get('http://localhost/second'), $batch->get('http://localhost/third'), ])->before(function (Batch $batch) { // 배치가 생성되었지만 아직 요청이 시작되지 않은 시점... })->progress(function (Batch $batch, int|string $key, Response $response) { // 개별 요청이 성공적으로 완료된 시점... })->then(function (Batch $batch, array $results) { // 모든 요청이 성공적으로 완료된 시점... })->catch(function (Batch $batch, int|string $key, Response|RequestException|ConnectionException $response) { // 배치 요청 중 실패가 감지된 시점... })->finally(function (Batch $batch, array $results) { // 배치 실행이 완전히 종료된 시점... })->send();

pool 메서드와 마찬가지로 as 메서드를 사용해 요청에 이름을 붙일 수 있습니다:

$responses = Http::batch(fn (Batch $batch) => [ $batch->as('first')->get('http://localhost/first'), $batch->as('second')->get('http://localhost/second'), $batch->as('third')->get('http://localhost/third'), ])->send();

NOTE

send 메서드를 호출해 배치가 시작된 이후에는 새로운 요청을 추가할 수 없습니다. 시도하면 Illuminate\Http\Client\BatchInProgressException 예외가 발생합니다.

요청 배치의 최대 동시 실행 수는 concurrency 메서드로 제어합니다:

$responses = Http::batch(fn (Batch $batch) => [ // ... ])->concurrency(5)->send();

배치 상태 확인

배치 완료 콜백에 전달되는 Illuminate\Http\Client\Batch 인스턴스는 배치의 상태를 확인하고 다루기 위한 다양한 프로퍼티와 메서드를 제공합니다:

// 배치에 할당된 총 요청 수... $batch->totalRequests; // 아직 처리되지 않은 요청 수... $batch->pendingRequests; // 실패한 요청 수... $batch->failedRequests; // 지금까지 처리된 요청 수... $batch->processedRequests(); // 배치 실행이 완료되었는지 여부... $batch->finished(); // 실패한 요청이 있는지 여부... $batch->hasFailures();

배치 지연 실행(Deferring Batches)

defer 메서드를 사용하면 배치를 즉시 실행하지 않고, 현재 애플리케이션 요청에 대한 HTTP 응답이 사용자에게 전송된 이후에 실행되도록 지연할 수 있습니다. 이를 통해 사용자가 느끼는 응답 속도를 빠르게 유지하면서 백그라운드에서 외부 요청을 처리할 수 있습니다:

use Illuminate\Http\Client\Batch; use Illuminate\Support\Facades\Http; $responses = Http::batch(fn (Batch $batch) => [ $batch->get('http://localhost/first'), $batch->get('http://localhost/second'), $batch->get('http://localhost/third'), ])->then(function (Batch $batch, array $results) { // 모든 요청이 성공적으로 완료된 시점... })->defer();

매크로

Laravel HTTP 클라이언트는 "매크로"를 정의하는 기능을 제공합니다. 매크로를 사용하면 애플리케이션 전반에서 특정 서비스와 통신할 때 공통으로 사용하는 요청 경로나 헤더 설정을 간결하고 표현력 있게 재사용할 수 있습니다.

매크로는 App\Providers\AppServiceProvider 클래스의 boot 메서드 안에서 정의합니다:

use Illuminate\Support\Facades\Http; /** * 애플리케이션 서비스를 부트스트랩합니다. */ public function boot(): void { Http::macro('github', function () { return Http::withHeaders([ 'X-Example' => 'example', ])->baseUrl('https://github.com'); }); }

매크로를 정의한 뒤에는 애플리케이션 어디서든 호출해 지정된 설정이 적용된 요청 객체를 바로 사용할 수 있습니다:

$response = Http::github()->get('/');

NOTE

매크로는 반복적으로 사용하는 외부 API 클라이언트 설정을 한곳에서 관리하는 데 유용합니다. 예를 들어 카카오, 네이버, 토스 등의 API를 여러 곳에서 호출한다면, 각각 매크로로 등록해 두면 기본 URL이나 인증 헤더를 중복 없이 관리할 수 있습니다.

테스트

Laravel의 많은 기능과 마찬가지로, HTTP 클라이언트도 테스트를 쉽고 표현력 있게 작성할 수 있도록 도와줍니다. Http 파사드의 fake 메서드를 사용하면 HTTP 클라이언트가 실제 요청 대신 미리 정의한 가짜(스텁) 응답을 반환하도록 설정할 수 있습니다.

응답 페이킹

모든 요청에 대해 빈 본문의 200 상태 코드 응답을 반환하게 하려면, 인수 없이 fake 메서드를 호출하면 됩니다.

use Illuminate\Support\Facades\Http; Http::fake(); $response = Http::post(/* ... */);

특정 URL 페이킹

배열을 fake 메서드에 전달하면 URL 패턴별로 다른 가짜 응답을 지정할 수 있습니다. 배열의 키는 페이킹할 URL 패턴이며, 와일드카드 문자 *를 사용할 수 있습니다. Http 파사드의 response 메서드로 각 엔드포인트에 대한 스텁 응답을 구성하세요.

Http::fake([ // GitHub 엔드포인트에 JSON 응답 스텁... 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers), // Google 엔드포인트에 문자열 응답 스텁... 'google.com/*' => Http::response('Hello World', 200, $headers), ]);

페이킹되지 않은 URL로 보내는 요청은 실제로 전송됩니다. 매칭되지 않는 모든 URL에 대한 폴백 응답을 지정하려면 * 단독 키를 사용하세요.

Http::fake([ // GitHub 엔드포인트에 JSON 응답 스텁... 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']), // 그 외 모든 엔드포인트에 문자열 응답 스텁... '*' => Http::response('Hello World', 200, ['Headers']), ]);

간단한 문자열, JSON, 빈 응답이 필요한 경우에는 문자열, 배열, 정수를 직접 응답값으로 사용할 수 있습니다.

Http::fake([ 'google.com/*' => 'Hello World', 'github.com/*' => ['foo' => 'bar'], 'chatgpt.com/*' => 200, ]);

예외 페이킹

HTTP 클라이언트가 요청 중 Illuminate\Http\Client\ConnectionException을 만났을 때 애플리케이션이 어떻게 동작하는지 테스트해야 할 경우, failedConnection 메서드를 사용하면 됩니다.

Http::fake([ 'github.com/*' => Http::failedConnection(), ]);

Illuminate\Http\Client\RequestException이 발생하는 상황을 테스트하려면 failedRequest 메서드를 사용하세요.

$this->mock(GithubService::class); ->shouldReceive('getUser') ->andThrow( Http::failedRequest(['code' => 'not_found'], 404) );

순차 응답 페이킹

동일한 URL에 대해 요청마다 다른 응답을 순서대로 반환해야 할 경우, Http::sequence 메서드를 사용해 응답 시퀀스를 구성할 수 있습니다.

Http::fake([ // GitHub 엔드포인트에 대해 순차 응답 스텁... 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->pushStatus(404), ]);

시퀀스에 정의된 응답이 모두 소진되면, 이후 요청은 예외를 발생시킵니다. 시퀀스가 비었을 때 반환할 기본 응답을 지정하려면 whenEmpty 메서드를 사용하세요.

Http::fake([ // GitHub 엔드포인트에 대해 순차 응답 스텁... 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->whenEmpty(Http::response()), ]);

특정 URL 패턴을 지정하지 않고 전체적으로 순차 응답을 페이킹하려면 Http::fakeSequence 메서드를 사용하세요.

Http::fakeSequence() ->push('Hello World', 200) ->whenEmpty(Http::response());

콜백을 활용한 페이킹

어떤 응답을 반환할지를 복잡한 로직으로 결정해야 한다면, fake 메서드에 클로저를 전달할 수 있습니다. 클로저는 Illuminate\Http\Client\Request 인스턴스를 받아 응답 인스턴스를 반환해야 합니다.

use Illuminate\Http\Client\Request; Http::fake(function (Request $request) { return Http::response('Hello World', 200); });

요청 검사

응답을 페이킹하는 경우, 애플리케이션이 올바른 데이터나 헤더를 전송하고 있는지 확인하기 위해 실제로 어떤 요청이 전송되었는지 검사하고 싶을 수 있습니다. Http::fake 호출 후 Http::assertSent 메서드를 사용하면 됩니다.

assertSent 메서드는 Illuminate\Http\Client\Request 인스턴스를 받아 기대를 충족하는지 여부를 불리언으로 반환하는 클로저를 인수로 받습니다. 테스트가 통과하려면 해당 조건을 충족하는 요청이 최소 하나 이상 전송되어야 합니다.

use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; Http::fake(); Http::withHeaders([ 'X-First' => 'foo', ])->post('http://example.com/users', [ 'name' => 'Taylor', 'role' => 'Developer', ]); Http::assertSent(function (Request $request) { return $request->hasHeader('X-First', 'foo') && $request->url() == 'http://example.com/users' && $request['name'] == 'Taylor' && $request['role'] == 'Developer'; });

특정 요청이 전송되지 않았음을 검사하려면 assertNotSent 메서드를 사용하세요.

use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; Http::fake(); Http::post('http://example.com/users', [ 'name' => 'Taylor', 'role' => 'Developer', ]); Http::assertNotSent(function (Request $request) { return $request->url() === 'http://example.com/posts'; });

테스트 중 전송된 요청의 총 횟수를 검사하려면 assertSentCount 메서드를 사용하세요.

Http::fake(); Http::assertSentCount(5);

테스트 중 요청이 전혀 전송되지 않았음을 검사하려면 assertNothingSent 메서드를 사용하세요.

Http::fake(); Http::assertNothingSent();

요청 및 응답 기록

recorded 메서드를 사용하면 테스트 중 발생한 모든 요청과 해당 응답을 수집할 수 있습니다. 이 메서드는 Illuminate\Http\Client\RequestIlluminate\Http\Client\Response 인스턴스의 쌍을 담은 배열 컬렉션을 반환합니다.

Http::fake([ 'https://laravel.com' => Http::response(status: 500), 'https://nova.laravel.com/' => Http::response(), ]); Http::get('https://laravel.com'); Http::get('https://nova.laravel.com/'); $recorded = Http::recorded(); [$request, $response] = $recorded[0];

또한 recorded 메서드에 클로저를 전달하면, Illuminate\Http\Client\RequestIlluminate\Http\Client\Response 인스턴스를 기반으로 원하는 조건에 맞는 요청/응답 쌍만 필터링할 수 있습니다.

use Illuminate\Http\Client\Request; use Illuminate\Http\Client\Response; Http::fake([ 'https://laravel.com' => Http::response(status: 500), 'https://nova.laravel.com/' => Http::response(), ]); Http::get('https://laravel.com'); Http::get('https://nova.laravel.com/'); $recorded = Http::recorded(function (Request $request, Response $response) { return $request->url() !== 'https://laravel.com' && $response->successful(); });

의도치 않은 실제 요청 방지

개별 테스트 또는 전체 테스트 스위트에서 페이킹되지 않은 요청이 실수로 실제 전송되는 것을 방지하고 싶다면 preventStrayRequests 메서드를 호출하세요. 이 메서드를 호출한 이후에는 대응하는 가짜 응답이 없는 요청이 발생할 경우, 실제 HTTP 요청을 보내는 대신 예외를 발생시킵니다.

use Illuminate\Support\Facades\Http; Http::preventStrayRequests(); Http::fake([ 'github.com/*' => Http::response('ok'), ]); // "ok" 응답이 반환됩니다... Http::get('https://github.com/laravel/framework'); // 예외가 발생합니다... Http::get('https://laravel.com');

NOTE

preventStrayRequests는 테스트 환경에서 외부 API 호출이 실수로 실행되는 것을 막아주는 안전장치입니다. setUp 메서드에 선언해두면 테스트 전체에 일관되게 적용됩니다.

대부분의 요청은 차단하되, 특정 요청만 실제로 통과시키고 싶은 경우에는 allowStrayRequests 메서드에 허용할 URL 패턴 배열을 전달하세요. 해당 패턴에 매칭되는 요청은 실제로 전송되며, 나머지는 예외가 발생합니다.

use Illuminate\Support\Facades\Http; Http::preventStrayRequests(); Http::allowStrayRequests([ 'http://127.0.0.1:5000/*', ]); // 이 요청은 실제로 전송됩니다... Http::get('http://127.0.0.1:5000/generate'); // 예외가 발생합니다... Http::get('https://laravel.com');

이벤트

Laravel은 HTTP 요청을 전송하는 과정에서 세 가지 이벤트를 발생시킵니다.

이벤트발생 시점
RequestSending요청이 전송되기 직전
ResponseReceived요청에 대한 응답을 수신한 직후
ConnectionFailed응답을 받지 못했을 때 (연결 실패 등)

RequestSendingConnectionFailed 이벤트에는 공개 프로퍼티 $request가 있으며, 이를 통해 Illuminate\Http\Client\Request 인스턴스를 확인할 수 있습니다. ResponseReceived 이벤트에는 $request와 함께 $response 프로퍼티도 포함되어 있어 Illuminate\Http\Client\Response 인스턴스도 함께 검사할 수 있습니다.

이 이벤트들에 대한 이벤트 리스너를 애플리케이션 내에 등록하면 요청/응답 로깅, 디버깅, 모니터링 등 다양한 용도로 활용할 수 있습니다.

use Illuminate\Http\Client\Events\RequestSending; class LogRequest { /** * 이벤트를 처리합니다. */ public function handle(RequestSending $event): void { // $event->request 를 통해 요청 정보를 확인할 수 있습니다. } }

NOTE

이벤트 리스너를 등록하는 방법은 이벤트 문서를 참고하세요. 예를 들어, 외부 API 호출 내역을 데이터베이스에 기록하거나, Slack으로 알림을 전송하는 등의 공통 처리를 리스너 하나로 일괄 적용할 수 있습니다.

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

번역일: 2026년 6월 27일