HTTP 클라이언트 (HTTP Client)
소개 (Introduction)
Laravel은 Guzzle HTTP 클라이언트를 감싸는 표현력 있고 간결한 API를 제공합니다. 이를 통해 다른 웹 애플리케이션과 통신하기 위한 외부 HTTP 요청을 빠르게 보낼 수 있습니다. Laravel의 Guzzle 래퍼는 가장 흔한 사용 사례와 훌륭한 개발자 경험에 초점을 맞추고 있습니다.
요청 보내기 (Making Requests)
요청을 보내려면 Http 파사드가 제공하는 head, get, post, put, patch, delete 메서드를 사용할 수 있습니다. 먼저 다른 URL로 기본 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'];
위에 나열된 응답 메서드 외에도, 응답이 특정 상태 코드를 가지고 있는지 확인하기 위해 다음 메서드를 사용할 수 있습니다.
$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 템플릿
HTTP 클라이언트는 URI template specification을 사용해 요청 URL을 구성할 수도 있습니다. URI 템플릿에서 확장할 수 있는 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' => 'Steve',
'role' => 'Network Administrator',
]);
GET 요청 쿼리 파라미터
GET 요청을 보낼 때는 쿼리 문자열을 URL에 직접 덧붙이거나, 키 / 값 쌍의 배열을 get 메서드의 두 번째 인수로 전달할 수 있습니다.
$response = Http::get('http://example.com/users', [
'name' => 'Taylor',
'page' => 1,
]);
또는 withQueryParameters 메서드를 사용할 수도 있습니다.
Http::retry(3, 100)->withQueryParameters([
'name' => 'Taylor',
'page' => 1,
])->get('http://example.com/users');
폼 URL 인코딩 요청 보내기
application/x-www-form-urlencoded 콘텐츠 타입으로 데이터를 보내고 싶다면, 요청을 보내기 전에 asForm 메서드를 호출해야 합니다.
$response = Http::asForm()->post('http://example.com/users', [
'name' => 'Sara',
'role' => 'Privacy Consultant',
]);
원시 요청 본문 보내기
요청을 보낼 때 원시 요청 본문을 제공하고 싶다면 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 메서드를 사용해 요청에 헤더를 추가할 수 있습니다. 이 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');
편의를 위해 acceptJson 메서드를 사용하면 요청에 대한 응답으로 애플리케이션이 application/json 콘텐츠 타입을 기대한다고 빠르게 지정할 수 있습니다.
$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',
]);
인증
withBasicAuth와 withDigestAuth 메서드를 사용해 각각 기본 인증과 다이제스트 인증 자격 증명을 지정할 수 있습니다.
// Basic authentication...
// Digest authentication...
Bearer 토큰
요청의 Authorization 헤더에 bearer token을 빠르게 추가하고 싶다면 withToken 메서드를 사용할 수 있습니다.
$response = Http::withToken('token')->post(/* ... */);
타임아웃
timeout 메서드는 응답을 기다릴 최대 초 수를 지정하는 데 사용할 수 있습니다. 기본적으로 HTTP 클라이언트는 30초 후 타임아웃됩니다.
$response = Http::timeout(3)->get(/* ... */);
지정한 타임아웃을 초과하면 Illuminate\Http\Client\ConnectionException 인스턴스가 발생합니다.
서버에 연결을 시도하는 동안 기다릴 최대 초 수는 connectTimeout 메서드를 사용해 지정할 수 있습니다. 기본값은 10초입니다.
$response = Http::connectTimeout(3)->get(/* ... */);
재시도
클라이언트 또는 서버 오류가 발생했을 때 HTTP 클라이언트가 요청을 자동으로 재시도하도록 하려면 retry 메서드를 사용할 수 있습니다. retry 메서드는 요청을 시도할 최대 횟수와 Laravel이 각 시도 사이에 기다릴 밀리초 수를 받습니다.
$response = Http::retry(3, 100)->post(/* ... */);
각 시도 사이에 대기할 밀리초 수를 직접 계산하고 싶다면, retry 메서드의 두 번째 인수로 클로저를 전달할 수 있습니다.
use Exception;
$response = Http::retry(3, function (int $attempt, Exception $exception) {
return $attempt * 100;
})->post(/* ... */);
편의를 위해 retry 메서드의 첫 번째 인수로 배열을 제공할 수도 있습니다. 이 배열은 이후 시도 사이에 대기할 밀리초 수를 결정하는 데 사용됩니다.
$response = Http::retry([100, 200])->post(/* ... */);
필요한 경우 retry 메서드에 세 번째 인수를 전달할 수 있습니다. 세 번째 인수는 실제로 재시도를 시도할지 결정하는 callable이어야 합니다. 예를 들어 최초 요청에서 ConnectionException이 발생한 경우에만 요청을 재시도하고 싶을 수 있습니다.
use Illuminate\Http\Client\PendingRequest;
use Throwable;
$response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->post(/* ... */);
요청 시도가 실패하면 새 시도가 이루어지기 전에 요청을 변경하고 싶을 수 있습니다. retry 메서드에 제공한 callable에 전달되는 요청 인수를 수정하면 이를 처리할 수 있습니다. 예를 들어 첫 번째 시도에서 인증 오류가 반환된 경우 새 인가 토큰으로 요청을 재시도하고 싶을 수 있습니다.
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(/* ... */);
모든 요청이 연결 문제 때문에 실패한 경우에는
throw인수가false로 설정되어 있어도Illuminate\Http\Client\ConnectionException이 여전히 발생합니다.
오류 처리
Guzzle의 기본 동작과 달리, Laravel의 HTTP 클라이언트 래퍼는 클라이언트 또는 서버 오류(서버에서 반환된 400 및 500 수준 응답)에 대해 예외를 발생시키지 않습니다. 이러한 오류 중 하나가 반환되었는지는 successful, clientError, serverError 메서드를 사용해 확인할 수 있습니다.
// Determine if the status code is >= 200 and < 300...
$response->successful();
// Determine if the status code is >= 400...
$response->failed();
// Determine if the response has a 400 level status code...
$response->clientError();
// Determine if the response has a 500 level status code...
$response->serverError();
// Immediately execute the given callback if there was a client or server error...
$response->onError(callable $callback);
예외 발생시키기
응답 인스턴스가 있고, 응답 상태 코드가 클라이언트 또는 서버 오류를 나타낼 때 Illuminate\Http\Client\RequestException 인스턴스를 발생시키고 싶다면 throw 또는 throwIf 메서드를 사용할 수 있습니다.
use Illuminate\Http\Client\Response;
$response = Http::post(/* ... */);
// Throw an exception if a client or server error occurred...
$response->throw();
// Throw an exception if an error occurred and the given condition is true...
$response->throwIf($condition);
// Throw an exception if an error occurred and the given closure resolves to true...
$response->throwIf(fn (Response $response) => true);
// Throw an exception if an error occurred and the given condition is false...
$response->throwUnless($condition);
// Throw an exception if an error occurred and the given closure resolves to false...
$response->throwUnless(fn (Response $response) => false);
// Throw an exception if the response has a specific status code...
$response->throwIfStatus(403);
// Throw an exception unless the response has a specific status code...
$response->throwUnlessStatus(200);
// Throw an exception if a server error occurred (status >500)...
$response->throwIfServerError();
// Throw an exception if a client error occurred (status >400 and <500)...
$response->throwIfClientError();
return $response['user']['id'];
Illuminate\Http\Client\RequestException 인스턴스에는 반환된 응답을 검사할 수 있는 public $response 속성이 있습니다.
오류가 발생하지 않은 경우 throw 메서드는 응답 인스턴스를 반환하므로, 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 파일에서 애플리케이션의 등록된 동작을 설정할 때 truncateAt 및 dontTruncate 메서드를 사용할 수 있습니다.
use Illuminate\Http\Client\RequestException;
->registered(function (): void {
// Truncate request exception messages to 240 characters...
RequestException::truncateAt(240);
// Disable request exception message truncation...
RequestException::dontTruncate();
})
또는 truncateExceptionsAt 메서드를 사용하여 요청별로 예외 잘라내기 동작을 사용자 정의할 수 있습니다.
return Http::truncateExceptionsAt(240)->post(/* ... */);
Guzzle Middleware
Laravel의 HTTP 클라이언트는 Guzzle을 기반으로 동작하므로, Guzzle Middleware를 활용하여 나가는 요청을 조작하거나 들어오는 응답을 검사할 수 있습니다. 나가는 요청을 조작하려면 withRequestMiddleware 메서드를 통해 Guzzle Middleware를 등록합니다.
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 메서드를 통해 Middleware를 등록하여 들어오는 HTTP 응답을 검사할 수 있습니다.
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');
전역 Middleware
때로는 모든 나가는 요청과 들어오는 응답에 적용되는 Middleware를 등록하고 싶을 수 있습니다. 이를 위해 globalRequestMiddleware 및 globalResponseMiddleware 메서드를 사용할 수 있습니다. 일반적으로 이 메서드들은 애플리케이션의 AppServiceProvider에 있는 boot 메서드에서 호출해야 합니다.
use Illuminate\Support\Facades\Http;
Http::globalRequestMiddleware(fn ($request) => $request->withHeader(
'User-Agent', 'Example Application/1.0'
));
Http::globalResponseMiddleware(fn ($response) => $response->withHeader(
'X-Finished-At', now()->toDateTimeString()
));
Guzzle 옵션
withOptions 메서드를 사용하여 나가는 요청에 추가 Guzzle request options를 지정할 수 있습니다. withOptions 메서드는 key / value 쌍의 배열을 인수로 받습니다.
$response = Http::withOptions([
'debug' => true,
])->get('http://example.com/users');
전역 옵션
모든 나가는 요청의 기본 옵션을 설정하려면 globalOptions 메서드를 사용할 수 있습니다. 일반적으로 이 메서드는 애플리케이션의 AppServiceProvider에 있는 boot 메서드에서 호출해야 합니다.
use Illuminate\Support\Facades\Http;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Http::globalOptions([
'allow_redirects' => false,
]);
}
동시 요청 (Concurrent Requests)
때로는 여러 HTTP 요청을 동시에 보내고 싶을 수 있습니다. 다시 말해, 요청을 순차적으로 보내는 대신 여러 요청을 같은 시점에 디스패치하고 싶은 경우입니다. 느린 HTTP API와 상호작용할 때 이는 상당한 성능 향상으로 이어질 수 있습니다.
요청 풀링
다행히 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 메서드는 withHeaders 또는 middleware 메서드와 같은 다른 HTTP 클라이언트 메서드와 체이닝할 수 없습니다. 풀링된 요청에 사용자 정의 헤더나 Middleware를 적용하려면, 풀 안의 각 요청에 해당 옵션을 설정해야 합니다.
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'),
]);
요청 배치 처리
Laravel에서 동시 요청을 다루는 또 다른 방법은 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) {
// The batch has been created but no requests have been initialized...
})->progress(function (Batch $batch, int|string $key, Response $response) {
// An individual request has completed successfully...
})->then(function (Batch $batch, array $results) {
// All requests completed successfully...
})->catch(function (Batch $batch, int|string $key, Response|RequestException|ConnectionException $response) {
// Batch request failure detected...
})->finally(function (Batch $batch, array $results) {
// The batch has finished executing...
})->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();
send 메서드를 호출하여 batch가 시작된 후에는 새 요청을 추가할 수 없습니다. 그렇게 하려고 하면 Illuminate\Http\Client\BatchInProgressException 예외가 발생합니다.
요청 배치의 최대 동시성은 concurrency 메서드를 통해 제어할 수 있습니다. 이 값은 요청 배치를 처리하는 동안 동시에 진행 중일 수 있는 HTTP 요청의 최대 개수를 결정합니다.
$responses = Http::batch(fn (Batch $batch) => [
// ...
])->concurrency(5)->send();
배치 검사
배치 완료 콜백에 제공되는 Illuminate\Http\Client\Batch 인스턴스에는 특정 요청 배치와 상호작용하고 이를 검사하는 데 도움이 되는 다양한 속성과 메서드가 있습니다.
// The number of requests assigned to the batch...
$batch->totalRequests;
// The number of requests that have not been processed yet...
$batch->pendingRequests;
// The number of requests that have failed...
$batch->failedRequests;
// The number of requests that have been processed thus far...
$batch->processedRequests();
// Indicates if the batch has finished executing...
$batch->finished();
// Indicates if the batch has request failures...
$batch->hasFailures();
배치 지연 실행
defer 메서드가 호출되면 요청 배치는 즉시 실행되지 않습니다. 대신 Laravel은 현재 애플리케이션 요청의 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) {
// All requests completed successfully...
})->defer();
매크로 (Macros)
Laravel HTTP 클라이언트에서는 "매크로"를 정의할 수 있습니다. 매크로는 애플리케이션 전반에서 여러 서비스와 상호작용할 때 공통 요청 경로와 헤더를 유창하고 표현력 있게 설정하는 메커니즘으로 사용할 수 있습니다. 시작하려면 애플리케이션의 App\Providers\AppServiceProvider 클래스에 있는 boot 메서드 안에서 매크로를 정의하면 됩니다.
use Illuminate\Support\Facades\Http;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Http::macro('github', function () {
return Http::withHeaders([
'X-Example' => 'example',
])->baseUrl('https://github.com');
});
}
매크로가 설정되면 애플리케이션 어디에서든 이를 호출하여 지정된 설정을 가진 대기 중인 요청을 생성할 수 있습니다.
$response = Http::github()->get('/');
테스트 (Testing)
많은 Laravel 서비스는 테스트를 쉽고 표현력 있게 작성할 수 있도록 돕는 기능을 제공하며, Laravel HTTP 클라이언트도 예외는 아닙니다. Http 파사드의 fake 메서드를 사용하면 요청이 만들어질 때 HTTP 클라이언트가 스텁 / 더미 응답을 반환하도록 지시할 수 있습니다.
응답 페이크 처리
예를 들어, 모든 요청에 대해 비어 있는 200 상태 코드 응답을 반환하도록 HTTP 클라이언트에 지시하려면 인수 없이 fake 메서드를 호출하면 됩니다.
use Illuminate\Support\Facades\Http;
Http::fake();
$response = Http::post(/* ... */);
특정 URL 페이크 처리
또는 fake 메서드에 배열을 전달할 수 있습니다. 배열의 키는 페이크 처리하려는 URL 패턴을 나타내고, 값은 해당 응답을 나타내야 합니다. * 문자는 와일드카드 문자로 사용할 수 있습니다. 이러한 엔드포인트에 대한 스텁 / 페이크 응답을 만들려면 Http 파사드의 response 메서드를 사용할 수 있습니다.
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
// Stub a string response for Google endpoints...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);
페이크 처리되지 않은 URL로 만들어진 모든 요청은 실제로 실행됩니다. 일치하지 않는 모든 URL을 스텁 처리할 fallback URL 패턴을 지정하려면 단일 * 문자를 사용할 수 있습니다.
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
// Stub a string response for all other endpoints...
'*' => 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 클라이언트가 연결 예외를 발생시키도록 지시할 수 있습니다.
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([
// Stub a series of responses for GitHub endpoints...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->pushStatus(404),
]);
응답 시퀀스의 모든 응답이 소비되면, 이후의 모든 요청은 응답 시퀀스가 예외를 발생시키도록 만듭니다. 시퀀스가 비어 있을 때 반환할 기본 응답을 지정하려면 whenEmpty 메서드를 사용할 수 있습니다.
Http::fake([
// Stub a series of responses for GitHub endpoints...
'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 콜백
특정 엔드포인트에 어떤 응답을 반환할지 결정하기 위해 더 복잡한 로직이 필요하다면, 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 메서드를 사용하여 모든 요청과 해당 요청에 대응하는 응답을 수집할 수 있습니다. 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 메서드는 Illuminate\Http\Client\Request와 Illuminate\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();
});
예기치 않은 요청 방지
개별 테스트나 전체 테스트 스위트에서 HTTP 클라이언트를 통해 전송되는 모든 요청이 가짜 응답으로 처리되도록 보장하고 싶다면, preventStrayRequests 메서드를 호출할 수 있습니다. 이 메서드를 호출한 후에는 대응되는 가짜 응답이 없는 요청이 실제 HTTP 요청을 보내는 대신 예외를 발생시킵니다.
use Illuminate\Support\Facades\Http;
Http::preventStrayRequests();
Http::fake([
'github.com/*' => Http::response('ok'),
]);
// An "ok" response is returned...
Http::get('https://github.com/laravel/framework');
// An exception is thrown...
Http::get('https://laravel.com');
때로는 대부분의 예기치 않은 요청은 방지하되, 특정 요청은 그대로 실행되도록 허용하고 싶을 수 있습니다. 이를 위해 allowStrayRequests 메서드에 URL 패턴 배열을 전달할 수 있습니다. 주어진 패턴 중 하나와 일치하는 요청은 허용되고, 그 외의 모든 요청은 계속 예외를 발생시킵니다.
use Illuminate\Support\Facades\Http;
Http::preventStrayRequests();
Http::allowStrayRequests([
'http://127.0.0.1:5000/*',
]);
// This request is executed...
Http::get('http://127.0.0.1:5000/generate');
// An exception is thrown...
Http::get('https://laravel.com');
이벤트 (Events)
Laravel은 HTTP 요청을 보내는 과정에서 세 가지 이벤트를 발생시킵니다. RequestSending 이벤트는 요청이 전송되기 전에 발생하며, ResponseReceived 이벤트는 주어진 요청에 대한 응답을 받은 후 발생합니다. ConnectionFailed 이벤트는 주어진 요청에 대해 응답을 받지 못한 경우 발생합니다.
RequestSending 이벤트와 ConnectionFailed 이벤트는 모두 Illuminate\Http\Client\Request 인스턴스를 검사하는 데 사용할 수 있는 public $request 속성을 포함합니다. 마찬가지로 ResponseReceived 이벤트는 $request 속성과 함께 Illuminate\Http\Client\Response 인스턴스를 검사하는 데 사용할 수 있는 $response 속성을 포함합니다. 애플리케이션 안에서 이러한 이벤트에 대한 이벤트 리스너를 만들 수 있습니다.
use Illuminate\Http\Client\Events\RequestSending;
class LogRequest
{
/**
* Handle the event.
*/
public function handle(RequestSending $event): void
{
// $event->request ...
}
}