본문 바로가기

HTTP 클라이언트

업데이트됨

번역일: 2026년 10월 3일

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

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

HTTP 클라이언트

소개

라라벨은 Guzzle HTTP 클라이언트를 감싼 표현력 있고 간결한 API를 제공합니다. 이를 이용하면 다른 웹 애플리케이션과 통신하는 아웃바운드(outbound) HTTP 요청을 빠르게 작성할 수 있습니다. 외부 API와 연동하거나 서드파티 서비스에서 데이터를 가져와야 할 때, 라라벨이 감싸놓은 Guzzle 덕분에 개발자 경험이 훨씬 쾌적해집니다.

NOTE

외부 API나 서비스 하나하나를 직접 HTTP 클라이언트로 호출하는 코드를 작성하는 대신, 특정 서비스 전용 SDK가 필요하다면 Saloon을 살펴보는 것도 좋습니다. Saloon은 라라벨 애플리케이션에서 서드파티 API 연동 코드를 구조화하는 데 도움을 주는 패키지입니다.

HTTP 클라이언트

요청 보내기

HTTP 요청을 보내려면 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 템플릿 명세를 사용하여 요청 URL을 구성할 수도 있습니다. withUrlParameters 메서드를 사용하면 URI 템플릿에서 확장될 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' => 'admin', ]);

GET 요청 쿼리 파라미터

GET 요청을 보낼 때는 URL에 쿼리 문자열을 직접 덧붙이거나, get 메서드의 두 번째 인수로 키-값 쌍의 배열을 전달할 수 있습니다.

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

또는 withQueryParameters 메서드를 사용할 수도 있습니다.

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

Form URL 인코딩 요청 전송하기

데이터를 application/x-www-form-urlencoded 콘텐츠 타입으로 전송하고 싶다면, 요청을 보내기 전에 asForm 메서드를 호출해야 합니다.

$response = Http::asForm()->post('http://example.com/users', [ 'name' => '철수', 'role' => 'admin', ]);

Raw 요청 본문 전송하기

요청을 보낼 때 원시(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');

간편하게, acceptJson 메서드를 사용하면 요청으로 보낼 때 응답으로 application/json 콘텐츠 타입을 기대한다고 빠르게 지정할 수 있습니다.

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

withHeaders 메서드는 새로운 헤더를 요청에 병합합니다. 필요하다면 replaceHeaders 메서드를 사용하여 모든 헤더를 완전히 교체할 수도 있습니다.

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

인증

withBasicAuth와 withDigestAuth 메서드를 사용하면 각각 기본 인증(Basic Authentication)과 다이제스트 인증(Digest Authentication) 자격 증명을 지정할 수 있습니다.

// 기본 인증(Basic Authentication)... $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // 다이제스트 인증(Digest Authentication)... $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */);

Bearer 토큰

요청의 Authorization 헤더에 Bearer 토큰을 빠르게 추가하고 싶다면 withToken 메서드를 사용할 수 있습니다.

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

타임아웃

timeout 메서드를 사용하면 응답을 기다릴 최대 시간(초)을 지정할 수 있습니다. 기본적으로 HTTP 클라이언트는 30초가 지나면 타임아웃됩니다.

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

지정한 타임아웃을 초과하면 Illuminate\Http\Client\ConnectionException 인스턴스가 발생(throw)합니다.

서버에 연결을 시도하는 동안 대기할 최대 시간(초)은 connectTimeout 메서드로 지정할 수 있습니다.

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

재시도

HTTP 클라이언트가 클라이언트 또는 서버 오류 발생 시 요청을 자동으로 재시도하도록 하려면 retry 메서드를 사용하면 됩니다. retry 메서드는 요청을 시도할 최대 횟수와 Laravel이 재시도 사이에 대기할 밀리초(ms)를 인수로 받습니다.

$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 메서드의 세 번째 인수로 실제 재시도를 수행할지 여부를 결정하는 콜러블을 전달할 수 있습니다. 예를 들어 최초 요청이 ConnectionException을 만났을 때만 재시도하도록 할 수 있습니다.

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

요청 시도가 실패한 경우, 다음 시도를 하기 전에 요청을 변경하고 싶을 수도 있습니다. 이는 retry 메서드에 전달한 콜러블에 전달되는 request 인수를 수정하여 처리할 수 있습니다. 예를 들어, 첫 번째 시도에서 인증 오류가 발생했을 경우 새로운 인증 토큰으로 재시도하고 싶다면 다음과 같이 작성할 수 있습니다.

use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\RequestException; $response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { return false; } $request->withToken($this->refreshToken()); 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 클라이언트 래퍼는 클라이언트 오류(400번대 응답)나 서버 오류(500번대 응답)가 발생해도 예외를 던지지 않습니다. 이러한 오류가 반환되었는지는 successful, clientError, serverError 메서드를 사용해 확인할 수 있습니다.

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

예외 발생시키기

응답 인스턴스를 가지고 있을 때, 응답 상태 코드가 클라이언트 또는 서버 오류를 나타내는 경우 Illuminate\Http\Client\RequestException의 인스턴스를 발생시키고 싶다면 throw, throwIf, throwUnless 메서드를 사용하면 됩니다.

$response = Http::post(/* ... */); // 클라이언트 또는 서버 오류 발생 시 예외를 발생시킴... $response->throw(); // 오류가 발생했고 주어진 조건이 참이면 예외를 발생시킴... $response->throwIf($condition); // 오류가 발생했고 주어진 클로저 결과가 참이면 예외를 발생시킴... $response->throwIf(fn (Response $response) => true); // 오류가 발생했고 주어진 조건이 거짓이면 예외를 발생시킴... $response->throwUnless($condition); // 오류가 발생했고 주어진 클로저 결과가 거짓이면 예외를 발생시킴... $response->throwUnless(fn (Response $response) => false); // 응답이 특정 상태 코드를 가지면 예외를 발생시킴... $response->throwIfStatus(403); // 응답이 특정 상태 코드를 가지지 않으면 예외를 발생시킴... $response->throwUnlessStatus(200); 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 메시지는 로깅하거나 리포트할 때 200자로 잘립니다. 이 동작을 조정하거나 비활성화하려면, 애플리케이션의 bootstrap/app.php 파일에서 truncateRequestExceptionsAt와 dontTruncateRequestExceptions 메서드를 사용할 수 있습니다.

->withExceptions(function (Exceptions $exceptions) { // 예외 메시지를 240자로 자르기... $exceptions->truncateRequestExceptionsAt(240); // 예외 메시지 자르지 않기... $exceptions->dontTruncateRequestExceptions(); })

Guzzle 미들웨어

Laravel의 HTTP 클라이언트는 Guzzle을 기반으로 동작하기 때문에, Guzzle 미들웨어를 활용하여 나가는 요청을 조작하거나 들어오는 응답을 검사할 수 있습니다. 나가는 요청을 조작하려면 withRequestMiddleware 메서드를 사용하여 Guzzle 미들웨어를 등록하면 됩니다.

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 메서드에 미들웨어를 등록하여 들어오는 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');

전역 미들웨어

때로는 발생시키는 모든 요청과 수신되는 모든 응답에 미들웨어를 적용하고 싶을 수 있습니다. 이런 경우 globalRequestMiddleware와 globalResponseMiddleware 메서드를 사용할 수 있습니다. 일반적으로 이 메서드들은 애플리케이션의 AppServiceProvider의 boot 메서드에서 호출합니다.

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

Guzzle 옵션

withOptions 메서드를 사용하면 외부 요청에 대해 추가적인 Guzzle 요청 옵션을 지정할 수 있습니다. withOptions 메서드는 키-값 쌍의 배열을 받습니다.

$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, ]); }

동시 요청

때로는 여러 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 메서드는 withHeaders나 middleware 같은 다른 HTTP 클라이언트 메서드와 체이닝할 수 없습니다. 풀에 포함된 각 요청에 대해 사용자 지정 헤더나 미들웨어를 적용하고 싶다면, 풀에 추가되는 각 요청마다 개별적으로 해당 옵션을 설정해야 합니다.

use Illuminate\Http\Client\Pool; use Illuminate\Support\Facades\Http; $responses = Http::pool(fn (Pool $pool) => [ $pool->withHeaders(['X-Test-Header' => 'Test'])->get('http://localhost/first'), $pool->withHeaders(['X-Test-Header' => 'Test'])->get('http://localhost/second'), ]);

매크로

Laravel HTTP 클라이언트를 사용하면 애플리케이션 전반에서 서비스와 상호작용할 때 공통된 요청 경로와 헤더를 유연하게 설정할 수 있는 "매크로(macro)"를 정의할 수 있습니다. 시작하려면, 애플리케이션의 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'); }); }

매크로를 정의하고 나면, 애플리케이션 어디에서든 이를 호출하여 지정된 설정이 적용된 대기 중인 요청(pending request)을 생성할 수 있습니다.

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

테스트

Laravel의 많은 서비스는 테스트를 쉽고 직관적으로 작성할 수 있도록 하는 기능을 제공하고 있으며, Laravel의 HTTP 클라이언트도 예외는 아닙니다. Http 파사드의 fake 메서드를 사용하면, HTTP 클라이언트가 요청을 받을 때 가짜(더미)의 스텁 응답을 반환하도록 지시할 수 있습니다.

응답 가짜로 만들기(Faking)

예를 들어, 모든 요청에 대해 빈 본문과 상태 코드 200을 가진 응답을 반환하도록 HTTP 클라이언트에 지시하려면, fake 메서드를 인수 없이 호출하면 됩니다.

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

특정 URL 가짜로 만들기(Faking)

또는, 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, ]);

연결 예외 가짜로 만들기

요청을 시도하는 동안 애플리케이션이 Illuminate\Http\Client\ConnectionException을 처리하는 경우를 테스트해야 할 때가 있습니다. failedConnection 메서드를 사용하면 HTTP 클라이언트가 연결 예외를 발생시키도록 지시할 수 있습니다.

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

요청 예외 가짜로 만들기

Illuminate\Http\Client\Response가 Illuminate\Http\Client\RequestException을 발생시키는 상황, 즉 응답이 클라이언트 또는 서버 오류를 나타내는 경우를 테스트해야 할 때는, failedRequest 메서드를 사용할 수 있습니다.

Http::fake([ 'github.com/*' => 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 클라이언트를 통해 전송된 모든 요청이 개별 테스트 또는 전체 테스트 스위트 전반에 걸쳐 가짜로 처리되었는지 확인하고 싶다면, 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');

요청 검사하기

응답을 가짜로 만들 때, 클라이언트가 수신한 요청을 검사하여 애플리케이션이 올바른 데이터나 헤더를 보내고 있는지 확인하고 싶을 수 있습니다. 이는 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' => '철수', 'role' => 'developer', ]); Http::assertSent(function (Request $request) { return $request->hasHeader('X-First', 'foo') && $request->url() == 'http://example.com/users' && $request->data() == [ 'name' => '철수', 'role' => 'developer', ]; });

필요하다면, assertNotSent 메서드를 사용하여 특정 요청이 전송되지 않았음을 단언할 수 있습니다.

use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\Http; Http::fake(); Http::post('http://example.com/users', [ 'name' => '철수', '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 메서드는 요청과 응답 인스턴스를 인수로 받는 클로저를 받을 수 있으며, 이를 이용해 기대한 조건에 따라 요청/응답 쌍을 필터링할 수 있습니다.

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(); });

이벤트

Laravel은 HTTP 요청을 보내는 과정에서 세 가지 이벤트를 발생시킵니다. RequestSending 이벤트는 요청이 전송되기 전에 발생하며, ResponseReceived 이벤트는 특정 요청에 대한 응답을 받은 후에 발생합니다. 또한 특정 요청에 대한 응답을 받지 못한 경우에는 ConnectionFailed 이벤트가 발생합니다.

RequestSending과 ConnectionFailed 이벤트는 모두 $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 ... } }

HTTP 클라이언트

요청 보내기

Http 파사드는 head, get, post, put, patch, delete 메서드를 제공하며, 이를 이용해 다른 서비스로 요청을 보낼 수 있습니다. 먼저 가장 기본적인 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'];

위에서 소개한 메서드 외에도, 응답이 특정 상태 코드인지 바로 확인할 수 있는 다음 메서드들도 제공됩니다.

$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 Server Error

URI 템플릿

HTTP 클라이언트는 URI 템플릿 명세를 이용해 요청 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 메서드의 두 번째 인수로 key/value 배열을 전달할 수 있습니다.

$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');

Form URL Encoded 요청 보내기

application/x-www-form-urlencoded 콘텐츠 타입으로 데이터를 전송하고 싶다면, 요청을 보내기 전에 asForm 메서드를 호출하세요.

$response = Http::asForm()->post('http://example.com/users', [ 'name' => 'Sara', 'role' => 'Privacy Consultant', ]);

Raw 요청 바디 보내기

요청 바디에 원시(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 메서드를 사용하면 요청에 헤더를 추가할 수 있습니다. 이 메서드는 key/value 쌍으로 이루어진 배열을 인수로 받습니다.

$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 Authentication)과 다이제스트 인증(Digest Authentication) 정보는 각각 withBasicAuth, withDigestAuth 메서드로 지정할 수 있습니다.

// 기본 인증... $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // 다이제스트 인증... $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */);

Bearer 토큰

요청의 Authorization 헤더에 Bearer 토큰을 빠르게 추가하고 싶다면 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 메서드는 최대 시도 횟수와, 시도 사이에 대기할 시간(밀리초)을 인수로 받습니다.

$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 메서드에 전달한 콜러블의 $request 인수를 직접 수정하면 됩니다. 예를 들어, 첫 번째 시도가 인증 오류를 반환했다면 새로운 인증 토큰으로 재시도하도록 만들 수 있습니다.

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

연결 문제(connection issue)로 인해 모든 요청이 실패한 경우에는, throw 인수를 false로 설정했더라도 Illuminate\Http\Client\ConnectionException은 여전히 발생합니다.

오류 처리

Guzzle의 기본 동작과 달리, Laravel의 HTTP 클라이언트 래퍼는 클라이언트 오류나 서버 오류(서버가 반환하는 400대, 500대 응답)에 대해 예외를 던지지 않습니다. 이런 오류가 발생했는지는 successful, clientError, serverError 메서드로 확인할 수 있습니다.

// 상태 코드가 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(); // 오류가 발생했고 주어진 조건이 참이라면 예외 발생... $response->throwIf($condition); // 오류가 발생했고 주어진 클로저가 true를 반환한다면 예외 발생... $response->throwIf(fn (Response $response) => true); // 오류가 발생했고 주어진 조건이 거짓이라면 예외 발생... $response->throwUnless($condition); // 오류가 발생했고 주어진 클로저가 false를 반환한다면 예외 발생... $response->throwUnless(fn (Response $response) => false); // 응답이 특정 상태 코드일 때 예외 발생... $response->throwIfStatus(403); // 응답이 특정 상태 코드가 아닐 때 예외 발생... $response->throwUnlessStatus(200); // 서버 오류(상태 코드 500 이상)가 발생했다면 예외 발생... $response->throwIfServerError(); // 클라이언트 오류(상태 코드 400 이상, 500 미만)가 발생했다면 예외 발생... $response->throwIfClientError(); return $response['user']['id'];

Illuminate\Http\Client\RequestException 인스턴스는 $response라는 public 속성을 가지고 있어서, 반환된 응답 내용을 확인할 수 있습니다.

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 { // 요청 예외 메시지를 240자로 자르도록 설정... RequestException::truncateAt(240); // 요청 예외 메시지 자르기를 비활성화... RequestException::dontTruncate(); })

또는 truncateExceptionsAt 메서드를 사용해 요청 단위로 예외 메시지 자르기 동작을 설정할 수도 있습니다.

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

Guzzle 미들웨어

Laravel의 HTTP 클라이언트는 내부적으로 Guzzle을 기반으로 동작하기 때문에, Guzzle 미들웨어를 활용해 보내는 요청을 조작하거나 받은 응답을 살펴볼 수 있습니다. 보내는 요청을 조작하려면 withRequestMiddleware 메서드로 Guzzle 미들웨어를 등록하세요.

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 메서드를 통해 미들웨어를 등록하면 들어오는 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');

전역 미들웨어

경우에 따라서는 애플리케이션에서 보내는 모든 요청과 받는 모든 응답에 적용되는 미들웨어를 등록하고 싶을 수 있습니다. 이럴 때는 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 요청 옵션을 지정할 수 있습니다. withOptions 메서드는 key/value 쌍으로 이루어진 배열을 인수로 받습니다.

$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, ]); }

동시 요청

경우에 따라 여러 개의 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);

풀에 담긴 요청 중 하나가 연결 단계에서 실패하는 경우(예: 타임아웃이나 DNS 조회 실패), $responses 배열의 해당 위치에는 Response 인스턴스 대신 Illuminate\Http\Client\ConnectionException 인스턴스가 담깁니다:

foreach ($responses as $response) { if ($response instanceof Throwable) { // 연결 자체에 실패한 요청... } elseif ($response->failed()) { // 연결은 되었지만 에러 응답을 받은 요청... } }

동시 요청 커스터마이징

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 Batching)

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) { // 배치는 생성되었지만 아직 요청이 시작되지 않은 상태... })->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();

send 메서드를 호출해 배치를 실행하고 나면 더 이상 해당 배치에 새로운 요청을 추가할 수 없습니다. 이 상태에서 요청을 추가하려고 하면 Illuminate\Http\Client\BatchInProgressException 예외가 발생합니다.

요청 배치의 최대 동시 처리 개수는 concurrency 메서드로 제어할 수 있습니다. 이 값은 배치를 처리하는 동안 동시에 진행 중일 수 있는 HTTP 요청의 최대 개수를 의미합니다:

$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();

배치 지연 실행하기

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) { // 모든 요청이 성공적으로 완료된 경우... })->defer();

NOTE

defer를 사용하면 사용자에게는 응답을 빠르게 돌려주면서, 뒤에서는 느린 외부 API 호출이나 알림 전송 같은 부가 작업을 처리할 수 있습니다. 다만 지연된 배치의 실행 결과를 현재 요청 내에서 곧바로 사용할 수는 없다는 점에 유의하세요.

HTTP 클라이언트

매크로

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'); }); }

매크로를 설정하고 나면, 애플리케이션 어디에서든 해당 매크로를 호출해서 미리 구성된 설정이 적용된 pending request(아직 전송되지 않은 요청 인스턴스)를 생성할 수 있습니다:

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

NOTE

예를 들어 GitHub, Slack, 사내 결제 API처럼 자주 호출하는 외부 서비스가 있다면, 매번 baseUrl이나 인증 헤더를 반복 작성하는 대신 매크로로 한 번만 정의해두는 것이 유지보수 측면에서 훨씬 편리합니다.

테스트

라라벨의 여러 서비스가 그렇듯, HTTP 클라이언트 역시 테스트를 쉽고 명료하게 작성할 수 있도록 다양한 기능을 제공합니다. Http 파사드의 fake 메서드를 사용하면 실제로 외부 서버에 요청을 보내는 대신, 미리 정해둔 더미 응답을 반환하도록 HTTP 클라이언트에 지시할 수 있습니다.

응답 가짜로 만들기 (Faking Responses)

예를 들어 모든 요청에 대해 빈 본문과 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에 대해 기본(fallback) 응답을 지정하고 싶다면, 패턴으로 * 하나만 사용하면 됩니다.

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 메서드로 특정 요청이 전송되지 않았음을 단언(assert)할 수도 있습니다.

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 메서드에 클로저를 전달하면, 클로저가 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'), ]); // "ok" 응답이 반환됩니다... Http::get('https://github.com/laravel/framework'); // 예외가 발생합니다... Http::get('https://laravel.com');

NOTE

preventStrayRequests는 "혹시 실수로 테스트 환경에서 실제 외부 API를 호출하는 것은 아닐까?"를 방지하기 위한 안전장치입니다. 특히 결제 API나 외부 서비스 연동 테스트에서 실수로 실제 요청이 나가는 것을 막는 데 유용합니다.

대부분의 의도치 않은 요청은 막되, 특정 요청만큼은 실제로 실행되도록 허용하고 싶을 수도 있습니다. 이럴 때는 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 이벤트가 발생합니다.

RequestSending과 ConnectionFailed 이벤트는 모두 Illuminate\Http\Client\Request 인스턴스를 확인할 수 있는 공개(public) $request 속성을 가지고 있습니다. 마찬가지로 ResponseReceived 이벤트는 $request 속성뿐만 아니라 Illuminate\Http\Client\Response 인스턴스를 확인할 수 있는 $response 속성도 함께 가지고 있습니다. 애플리케이션 내에서 이 이벤트들을 위한 이벤트 리스너를 작성할 수 있습니다.

use Illuminate\Http\Client\Events\RequestSending; class LogRequest { /** * 이벤트를 처리합니다. */ public function handle(RequestSending $event): void { // $event->request ... } }

예를 들어, 외부 결제 API로 전송되는 모든 요청을 로그로 남기거나, 연결에 실패했을 때 Slack으로 알림을 보내는 등의 용도로 이 이벤트들을 활용할 수 있습니다. 이러한 이벤트는 HTTP 클라이언트의 동작을 직접 수정하지 않고도 요청·응답 흐름을 관찰(observe)하고 싶을 때 특히 유용합니다.

NOTE

요청이나 응답 내용을 가로채서 변경해야 한다면 이벤트보다는 이 문서 앞부분에서 다룬 미들웨어를 사용하는 것이 더 적합합니다. 이벤트는 어디까지나 관찰 목적으로만 사용하시기 바랍니다.

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

번역일: 2026년 10월 3일