HTTP 클라이언트
번역일: 2026년 6월 25일
HTTP 클라이언트
소개
Laravel은 Guzzle HTTP 클라이언트를 기반으로 간결하고 표현력 있는 HTTP 클라이언트 API를 제공합니다. 외부 웹 서비스나 API와 통신할 때 Guzzle을 직접 다루는 복잡함 없이, 가장 일반적인 사용 패턴을 쾌적하게 처리할 수 있도록 설계되어 있습니다.
시작하기 전에 Guzzle 패키지가 설치되어 있는지 확인하세요. Laravel은 기본적으로 Guzzle을 포함하고 있습니다. 만약 이전에 제거했다면 Composer로 다시 설치할 수 있습니다.
composer require guzzlehttp/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) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$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 템플릿 명세를 사용해 요청 URL을 동적으로 구성할 수도 있습니다. withUrlParameters 메서드로 URI 템플릿에 들어갈 파라미터를 지정하세요.
Http::withUrlParameters([
'endpoint' => 'https://laravel.com',
'page' => 'docs',
'version' => '9.x',
'topic' => 'validation',
])->get('{+endpoint}/{page}/{version}/{topic}');요청 디버깅
요청이 실제로 전송되기 전에 내용을 확인하고 싶다면, dd 메서드를 체인에 추가하세요. 요청 정보를 덤프하고 스크립트 실행을 즉시 종료합니다.
return Http::dd()->get('http://example.com');요청 데이터
POST, PUT, PATCH 요청 시에는 두 번째 인자로 데이터 배열을 전달할 수 있습니다. 기본적으로 application/json 콘텐츠 타입으로 전송됩니다.
use Illuminate\Support\Facades\Http;
$response = Http::post('http://example.com/users', [
'name' => '홍길동',
'role' => '관리자',
]);GET 요청 쿼리 파라미터
GET 요청 시에는 URL에 직접 쿼리 문자열을 붙이거나, get 메서드의 두 번째 인자로 배열을 전달할 수 있습니다.
$response = Http::get('http://example.com/users', [
'name' => 'Taylor',
'page' => 1,
]);또는 withQueryParameters 메서드를 사용할 수도 있습니다.
Http::retry(3, 100)->withQueryParameters([
'name' => 'Taylor',
'page' => 1,
])->get('http://example.com/users');Form URL 인코딩 방식으로 전송
application/x-www-form-urlencoded 방식으로 데이터를 전송하려면 asForm 메서드를 먼저 호출하세요.
$response = Http::asForm()->post('http://example.com/users', [
'name' => '김철수',
'role' => '개인정보 담당자',
]);Raw 요청 바디 전송
withBody 메서드를 사용하면 원시 요청 바디를 직접 지정할 수 있습니다. 두 번째 인자로 콘텐츠 타입을 지정합니다.
$response = Http::withBody(
base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');멀티파트 요청 (파일 업로드)
파일을 멀티파트 방식으로 전송하려면 attach 메서드를 사용하세요. 파일 이름, 파일 내용을 필수로 전달하고, 세 번째 인자로 파일명, 네 번째 인자로 추가 헤더를 지정할 수 있습니다.
$response = Http::attach(
'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg']
)->post('http://example.com/attachments');파일 내용 대신 스트림 리소스를 전달하는 것도 가능합니다.
$photo = fopen('photo.jpg', 'r');
$response = Http::attach(
'attachment', $photo, 'photo.jpg'
)->post('http://example.com/attachments');헤더
withHeaders 메서드에 키/값 배열을 전달하여 요청 헤더를 추가할 수 있습니다.
$response = Http::withHeaders([
'X-First' => 'foo',
'X-Second' => 'bar'
])->post('http://example.com/users', [
'name' => 'Taylor',
]);accept 메서드로 응답에서 기대하는 콘텐츠 타입을 지정할 수 있습니다.
$response = Http::accept('application/json')->get('http://example.com/users');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(/* ... */);세 번째 인자로 재시도 여부를 판단하는 콜백을 전달할 수 있습니다. 예를 들어 ConnectionException이 발생한 경우에만 재시도하도록 제한할 수 있습니다.
use Exception;
use Illuminate\Http\Client\PendingRequest;
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->post(/* ... */);재시도 전에 요청 내용을 수정하고 싶다면 콜백 내에서 $request 인자를 직접 변경하면 됩니다. 예를 들어 401 응답 시 새 토큰으로 교체하는 경우입니다.
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();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', 'My Application/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');동시 요청
여러 HTTP 요청을 순차적으로 보내는 대신 동시에 보내면 성능을 크게 향상시킬 수 있습니다. 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;
$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 클라이언트의 매크로 기능을 사용하면 자주 사용하는 요청 설정(기본 URL, 헤더 등)을 한 번 정의해 두고 재사용할 수 있습니다. AppServiceProvider의 boot 메서드에서 Http::macro로 정의합니다.
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 클라이언트는 테스트를 쉽게 작성할 수 있도록 응답을 페이크(가짜)로 대체하는 기능을 제공합니다. Http 파사드의 fake 메서드를 사용하면 실제 HTTP 요청 대신 미리 정의한 더미 응답을 반환하게 할 수 있습니다.
응답 페이크
모든 요청에 대해 빈 200 OK 응답을 반환하게 하려면 인자 없이 fake를 호출하세요.
use Illuminate\Support\Facades\Http;
Http::fake();
$response = Http::post(/* ... */);특정 URL 페이크
fake 메서드에 배열을 전달하면 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 엔드포인트에 JSON 응답 반환
'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
// 그 외 모든 엔드포인트에 문자열 응답 반환
'*' => Http::response('Hello World', 200, ['Headers']),
]);응답 시퀀스 페이크
동일한 URL에 요청이 반복될 때 순서대로 다른 응답을 반환하게 하려면 Http::sequence를 사용하세요.
Http::fake([
// GitHub 엔드포인트에 순서대로 응답 반환
'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, array $options) {
return Http::response('Hello World', 200);
});예상치 못한 요청 방지
테스트 중 페이크로 등록되지 않은 URL로 실제 HTTP 요청이 나가는 것을 막으려면 preventStrayRequests를 호출하세요. 이 메서드를 호출한 후 페이크 응답이 없는 URL로 요청이 시도되면 예외가 발생합니다.
use Illuminate\Support\Facades\Http;
Http::preventStrayRequests();
Http::fake([
'github.com/*' => Http::response('ok'),
]);
// 페이크 응답이 반환됨
Http::get('https://github.com/laravel/framework');
// 예외 발생 — 페이크가 등록되지 않은 URL
Http::get('https://