HTTP 클라이언트

번역일: 2026년 6월 25일

HTTP 클라이언트

소개

Laravel은 Guzzle HTTP 클라이언트를 기반으로 한 간결하고 표현력 있는 HTTP 클라이언트를 제공합니다. 외부 API나 다른 웹 서비스와 통신할 때 흔히 쓰이는 패턴에 집중해 설계되어, 복잡한 Guzzle 설정 없이도 깔끔하게 HTTP 요청을 보낼 수 있습니다.

요청 보내기

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 메서드로 템플릿에 삽입할 파라미터를 지정합니다.

Http::withUrlParameters([ 'endpoint' => 'https://laravel.com', 'page' => 'docs', 'version' => '11.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에 직접 붙이거나, 두 번째 인수로 배열을 전달하거나, withQueryParameters 메서드를 사용할 수 있습니다.

// URL에 배열로 전달 $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 인코딩 요청 보내기

application/x-www-form-urlencoded 형식으로 데이터를 보내려면 요청 전에 asForm 메서드를 호출합니다.

$response = Http::asForm()->post('http://example.com/users', [ 'name' => '이순신', 'role' => '보안 담당자', ]);

Raw 요청 바디 전송

직접 바디 내용을 지정하려면 withBody 메서드를 사용합니다. 두 번째 인수로 Content-Type을 지정합니다.

$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' => 'Taylor', ]);

accept 메서드로 응답에서 기대하는 Content-Type을 지정할 수 있습니다.

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

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 메서드로 별도로 지정할 수 있습니다.

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

특정 조건에서만 재시도하려면 세 번째 인수로 클로저를 전달합니다.

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

재시도 전에 요청 자체를 수정해야 할 때(예: 401 응답 시 토큰 갱신)는 클로저 안에서 $request 객체를 직접 변경합니다.

use Exception; 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->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과 달리 4xx, 5xx 응답이 와도 자동으로 예외를 던지지 않습니다. 대신 아래 메서드로 응답 결과를 직접 확인하는 방식입니다.

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

예외 던지기

필요한 경우 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에는 $response 속성이 있어 응답 내용을 확인할 수 있습니다.

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

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

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

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에서 이 동작을 변경할 수 있습니다.

->withExceptions(function (Exceptions $exceptions) { // 240자로 제한 $exceptions->truncateRequestExceptionsAt(240); // 자르기 비활성화 $exceptions->dontTruncateRequestExceptions(); })

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', 'My Application/1.0' )); Http::globalResponseMiddleware(fn ($response) => $response->withHeader( 'X-Finished-At', now()->toDateTimeString() ));

Guzzle 옵션

Guzzle 요청 옵션을 직접 지정하려면 withOptions 메서드에 키/값 배열을 전달합니다.

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

동시 요청

느린 외부 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 메서드는 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'), ]);

매크로

HTTP 클라이언트 매크로를 활용하면 자주 사용하는 API 엔드포인트 설정(베이스 URL, 헤더 등)을 이름으로 정의해두고 재사용할 수 있습니다. AppServiceProviderboot 메서드에서 정의합니다.

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('/');

테스트

Laravel HTTP 클라이언트는 테스트를 쉽게 작성할 수 있도록 응답을 가짜로 만드는(fake) 기능을 제공합니다. Http 파사드의 fake 메서드를 사용합니다.

응답 페이킹

인수 없이 fake를 호출하면 모든 요청에 대해 빈 200 응답을 반환합니다.

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

특정 URL 페이킹

배열을 전달하면 URL 패턴별로 다른 응답을 지정할 수 있습니다. *를 와일드카드로 사용할 수 있으며, 패턴에 매칭되지 않는 URL은 실제로 요청이 전송됩니다.

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

매칭되지 않는 모든 URL에 대한 기본 응답을 지정하려면 *를 키로 사용합니다.

Http::fake([ '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, ]);

연결 예외 페이킹

ConnectionException이 발생하는 상황을 테스트하려면 failedConnection 메서드를 사용합니다.

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

순차 응답 페이킹

동일한 URL에 대해 요청할 때마다 다른 응답을 순서대로 반환해야 한다면 Http::sequence를 사용합니다.

Http::fake([ 'github.com/*' => Http::sequence() ->push('Hello World', 200) ->push(['foo' => 'bar'], 200) ->pushStatus(404), ]);

시퀀스의 응답이 모두 소진되면 이후 요청은 예외를 발생시킵니다. 소진 시 기본 응답을 지정하려면 whenEmpty를 사용합니다.

Http::fake([ '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); });

예상치 못한 요청 방지

테스트 중 페이크로 지정하지 않은 URL로

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

번역일: 2026년 6월 25일