HTTP 클라이언트
업데이트됨번역일: 2026년 7월 28일
이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.
- 원문 수정
- 2026년 7월 28일
- 번역 갱신
- 2026년 7월 28일
HTTP 클라이언트
소개
Laravel은 Guzzle HTTP 클라이언트를 기반으로 한 간결하고 직관적인 HTTP 클라이언트를 제공합니다. 외부 API나 웹 서비스에 HTTP 요청을 보낼 때 번거로운 설정 없이 빠르게 시작할 수 있도록 설계되어 있습니다. 자주 사용하는 패턴들(JSON 전송, 인증 헤더 추가, 오류 처리 등)을 깔끔한 API로 감싸두었기 때문에, Guzzle을 직접 다루는 것보다 훨씬 편리합니다.
HTTP 클라이언트
소개
Laravel은 Guzzle HTTP 클라이언트를 기반으로 간결하고 표현력 있는 API를 제공합니다. 이를 통해 외부 웹 서비스나 API에 HTTP 요청을 손쉽게 보낼 수 있습니다. Laravel의 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, $flags = 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 ErrorURI 템플릿
URI 템플릿 명세(RFC 6570)를 사용해 요청 URL을 동적으로 구성할 수 있습니다. withUrlParameters 메서드로 템플릿에 치환할 파라미터를 지정하세요.
Http::withUrlParameters([
'endpoint' => 'https://laravel.com',
'page' => 'docs',
'version' => '13.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에 직접 붙이거나, 두 번째 인수로 키/값 배열을 전달할 수 있습니다.
$response = Http::get('http://example.com/users', [
'name' => '홍길동',
'page' => 1,
]);withQueryParameters 메서드를 사용하는 방법도 있습니다. 특히 retry 등 다른 메서드와 체이닝할 때 유용합니다.
Http::retry(3, 100)->withQueryParameters([
'name' => '홍길동',
'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');파일 내용을 직접 전달하는 대신 스트림 리소스를 넘길 수도 있습니다.
$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' => '홍길동',
]);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' => '홍길동',
]);인증
withBasicAuth, withDigestAuth 메서드로 각각 Basic 인증과 Digest 인증 자격 증명을 지정할 수 있습니다.
// Basic 인증
$response = Http::withBasicAuth('user@example.com', 'secret')->post(/* ... */);
// Digest 인증
$response = Http::withDigestAuth('user@example.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(/* ... */);세 번째 인수로 재시도 여부를 결정하는 콜백을 전달할 수 있습니다. 예를 들어 ConnectionException이 발생한 경우에만 재시도하도록 제한할 수 있습니다.
use Illuminate\Http\Client\PendingRequest;
use Throwable;
$response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->post(/* ... */);재시도 전에 요청 내용을 변경해야 하는 경우, 콜백 내에서 $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이 발생합니다.
오류 처리
Guzzle의 기본 동작과 달리, Laravel HTTP 클라이언트는 4xx, 5xx 응답이 반환되어도 자동으로 예외를 발생시키지 않습니다. successful, clientError, serverError 등의 메서드로 오류 여부를 직접 확인해야 합니다.
// 상태 코드가 200 이상 300 미만인지 확인
$response->successful();
// 상태 코드가 400 이상인지 확인
$response->failed();
// 4xx 클라이언트 오류인지 확인
$response->clientError();
// 5xx 서버 오류인지 확인
$response->serverError();
// 클라이언트 또는 서버 오류 발생 시 즉시 콜백 실행
$response->onError(callable $callback);예외 발생시키기
4xx 또는 5xx 응답일 때 명시적으로 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);
// 서버 오류(5xx)일 때 예외 발생
$response->throwIfServerError();
// 클라이언트 오류(4xx)일 때 예외 발생
$response->throwIfClientError();
return $response['user']['id'];Illuminate\Http\Client\RequestException 인스턴스에는 $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.php의 registered 콜백에서 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');전역 미들웨어
모든 요청과 응답에 공통으로 적용할 미들웨어가 필요하다면 globalRequestMiddleware와 globalResponseMiddleware 메서드를 사용하세요. 보통 AppServiceProvider의 boot 메서드에서 등록합니다.
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 메서드를 사용하세요. 마찬가지로 AppServiceProvider의 boot 메서드에서 호출하는 것이 일반적입니다.
use Illuminate\Support\Facades\Http;
/**
* 애플리케이션 서비스를 부트스트랩합니다.
*/
public function boot(): void
{
Http::globalOptions([
'allow_redirects' => false,
]);
}동시 요청(Concurrent Requests)
여러 HTTP API를 순차적으로 호출하면 각 요청의 응답을 기다리는 시간이 쌓여 전체 처리 시간이 길어집니다. 동시 요청을 사용하면 여러 요청을 동시에 발송해 대기 시간을 크게 줄일 수 있습니다.
요청 풀(Request Pool)
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 인수로 제한할 수 있습니다. 이 값을 초과하는 요청은 앞선 요청이 완료된 후 순차적으로 처리됩니다.
$responses = Http::pool(fn (Pool $pool) => [
// ...
], concurrency: 5);동시 요청 커스터마이징
pool 메서드는 withHeaders나 middleware 같은 다른 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 Batch)
동시 요청을 처리하는 또 다른 방법은 batch 메서드입니다. pool과 마찬가지로 클로저를 인수로 받지만, Illuminate\Http\Client\Batch 인스턴스를 통해 요청을 추가하고 요청 완료 시 실행할 콜백도 정의할 수 있습니다.
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)
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나 자주 연동하는 외부 서비스(예: 카카오, 네이버 등)가 있다면, 각 서비스별로 매크로를 정의해두면 반복적인 설정 코드를 크게 줄일 수 있습니다.
테스트
Laravel의 HTTP 클라이언트도 다른 Laravel 서비스와 마찬가지로 테스트를 쉽고 명확하게 작성할 수 있도록 지원합니다. Http 파사드의 fake 메서드를 사용하면 실제 HTTP 요청 대신 미리 지정한 가짜(stub) 응답을 반환하도록 클라이언트를 설정할 수 있습니다.
응답 페이크
모든 요청에 대해 빈 본문의 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']),
]);간단한 응답은 문자열, 배열, 또는 정수를 직접 전달하는 방식으로도 생성할 수 있습니다:
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::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\Request와 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();
[$request, $response] = $recorded[0];recorded 메서드에 클로저를 전달하면 요청/응답 쌍을 조건에 따라 필터링할 수도 있습니다:
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();
});의도치 않은 실제 요청 방지
테스트 중 페이크되지 않은 URL로 실제 HTTP 요청이 전송되는 것을 막으려면 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 클라이언트는 HTTP 요청을 처리하는 과정에서 세 가지 이벤트를 발생시킵니다.
| 이벤트 | 발생 시점 |
|---|---|
RequestSending | 요청이 전송되기 직전 |
ResponseReceived | 요청에 대한 응답을 수신한 직후 |
ConnectionFailed | 응답을 받지 못한 경우 (연결 실패) |
RequestSending과 ConnectionFailed 이벤트에는 공개(public) $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
이벤트 리스너를 등록하는 방법은 이벤트 문서를 참고하세요. EventServiceProvider 또는 #[On] 어트리뷰트를 사용해 리스너를 연결할 수 있습니다.