2025-11-27 19:20:07 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services\FlowTester;
|
|
|
|
|
|
|
|
|
|
use Exception;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* HTTP 클라이언트 래퍼
|
|
|
|
|
*
|
|
|
|
|
* Laravel HTTP Client를 래핑하여 플로우 테스터에 맞게 제공합니다.
|
|
|
|
|
*/
|
|
|
|
|
class HttpClient
|
|
|
|
|
{
|
|
|
|
|
private string $baseUrl = '';
|
|
|
|
|
|
|
|
|
|
private int $timeout = 30;
|
|
|
|
|
|
|
|
|
|
private array $defaultHeaders = [];
|
|
|
|
|
|
|
|
|
|
private ?string $apiKey = null;
|
|
|
|
|
|
|
|
|
|
private ?string $bearerToken = null;
|
|
|
|
|
|
2025-11-27 22:20:36 +09:00
|
|
|
private bool $verifySsl = true;
|
|
|
|
|
|
|
|
|
|
private ?string $hostHeader = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* SSL 검증 비활성화 (Docker 내부 통신용)
|
|
|
|
|
*/
|
|
|
|
|
public function withoutVerifying(): self
|
|
|
|
|
{
|
|
|
|
|
$this->verifySsl = false;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Host 헤더 설정 (Docker 내부 통신용)
|
|
|
|
|
*/
|
|
|
|
|
public function setHostHeader(string $host): self
|
|
|
|
|
{
|
|
|
|
|
$this->hostHeader = $host;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 19:20:07 +09:00
|
|
|
/**
|
|
|
|
|
* 기본 URL 설정
|
|
|
|
|
*/
|
|
|
|
|
public function setBaseUrl(string $url): self
|
|
|
|
|
{
|
|
|
|
|
$this->baseUrl = rtrim($url, '/');
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 타임아웃 설정 (초)
|
|
|
|
|
*/
|
|
|
|
|
public function setTimeout(int $seconds): self
|
|
|
|
|
{
|
|
|
|
|
$this->timeout = $seconds;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 기본 헤더 설정
|
|
|
|
|
*/
|
|
|
|
|
public function setDefaultHeaders(array $headers): self
|
|
|
|
|
{
|
|
|
|
|
$this->defaultHeaders = $headers;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* API 키 설정 (X-API-KEY 헤더)
|
|
|
|
|
*/
|
|
|
|
|
public function setApiKey(string $apiKey): self
|
|
|
|
|
{
|
|
|
|
|
$this->apiKey = $apiKey;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bearer 토큰 설정
|
|
|
|
|
*/
|
|
|
|
|
public function setBearerToken(string $token): self
|
|
|
|
|
{
|
|
|
|
|
$this->bearerToken = $token;
|
|
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* HTTP 요청 실행
|
|
|
|
|
*
|
|
|
|
|
* @param string $method HTTP 메서드
|
|
|
|
|
* @param string $endpoint 엔드포인트
|
|
|
|
|
* @param array $options 옵션 ['headers' => [], 'body' => [], 'query' => []]
|
|
|
|
|
* @return array ['status' => int, 'headers' => array, 'body' => array, 'duration' => int]
|
|
|
|
|
*/
|
|
|
|
|
public function request(string $method, string $endpoint, array $options = []): array
|
|
|
|
|
{
|
|
|
|
|
$startTime = microtime(true);
|
|
|
|
|
|
|
|
|
|
$url = $this->buildUrl($endpoint);
|
|
|
|
|
$headers = $this->buildHeaders($options['headers'] ?? []);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$request = Http::timeout($this->timeout)
|
|
|
|
|
->withHeaders($headers);
|
|
|
|
|
|
2025-11-27 22:20:36 +09:00
|
|
|
// SSL 검증 비활성화 (Docker 내부 통신용)
|
|
|
|
|
if (! $this->verifySsl) {
|
|
|
|
|
$request = $request->withoutVerifying();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Host 헤더 추가 (Docker 내부 통신용)
|
|
|
|
|
if ($this->hostHeader) {
|
|
|
|
|
$request = $request->withHeaders(['Host' => $this->hostHeader]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 19:20:07 +09:00
|
|
|
// API 키 추가
|
|
|
|
|
if ($this->apiKey) {
|
|
|
|
|
$request = $request->withHeaders(['X-API-KEY' => $this->apiKey]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-03 20:14:03 +09:00
|
|
|
// Bearer 토큰 추가 (스텝 헤더에 Authorization이 없는 경우에만)
|
|
|
|
|
if ($this->bearerToken && ! isset($headers['Authorization'])) {
|
2025-11-27 19:20:07 +09:00
|
|
|
$request = $request->withToken($this->bearerToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 쿼리 파라미터
|
|
|
|
|
if (! empty($options['query'])) {
|
|
|
|
|
$url .= '?'.http_build_query($options['query']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 요청 실행
|
|
|
|
|
$response = match (strtoupper($method)) {
|
|
|
|
|
'GET' => $request->get($url),
|
|
|
|
|
'POST' => $request->post($url, $options['body'] ?? []),
|
|
|
|
|
'PUT' => $request->put($url, $options['body'] ?? []),
|
|
|
|
|
'PATCH' => $request->patch($url, $options['body'] ?? []),
|
|
|
|
|
'DELETE' => $request->delete($url, $options['body'] ?? []),
|
|
|
|
|
default => throw new Exception("Unsupported HTTP method: {$method}"),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$duration = (int) ((microtime(true) - $startTime) * 1000);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'status' => $response->status(),
|
|
|
|
|
'headers' => $response->headers(),
|
|
|
|
|
'body' => $response->json() ?? [],
|
|
|
|
|
'duration' => $duration,
|
|
|
|
|
'success' => $response->successful(),
|
|
|
|
|
'error' => null,
|
|
|
|
|
];
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
$duration = (int) ((microtime(true) - $startTime) * 1000);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'status' => 0,
|
|
|
|
|
'headers' => [],
|
|
|
|
|
'body' => [],
|
|
|
|
|
'duration' => $duration,
|
|
|
|
|
'success' => false,
|
|
|
|
|
'error' => $e->getMessage(),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GET 요청
|
|
|
|
|
*/
|
|
|
|
|
public function get(string $endpoint, array $query = [], array $headers = []): array
|
|
|
|
|
{
|
|
|
|
|
return $this->request('GET', $endpoint, [
|
|
|
|
|
'query' => $query,
|
|
|
|
|
'headers' => $headers,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* POST 요청
|
|
|
|
|
*/
|
|
|
|
|
public function post(string $endpoint, array $body = [], array $headers = []): array
|
|
|
|
|
{
|
|
|
|
|
return $this->request('POST', $endpoint, [
|
|
|
|
|
'body' => $body,
|
|
|
|
|
'headers' => $headers,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* PUT 요청
|
|
|
|
|
*/
|
|
|
|
|
public function put(string $endpoint, array $body = [], array $headers = []): array
|
|
|
|
|
{
|
|
|
|
|
return $this->request('PUT', $endpoint, [
|
|
|
|
|
'body' => $body,
|
|
|
|
|
'headers' => $headers,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* PATCH 요청
|
|
|
|
|
*/
|
|
|
|
|
public function patch(string $endpoint, array $body = [], array $headers = []): array
|
|
|
|
|
{
|
|
|
|
|
return $this->request('PATCH', $endpoint, [
|
|
|
|
|
'body' => $body,
|
|
|
|
|
'headers' => $headers,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* DELETE 요청
|
|
|
|
|
*/
|
|
|
|
|
public function delete(string $endpoint, array $body = [], array $headers = []): array
|
|
|
|
|
{
|
|
|
|
|
return $this->request('DELETE', $endpoint, [
|
|
|
|
|
'body' => $body,
|
|
|
|
|
'headers' => $headers,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 전체 URL 구성
|
|
|
|
|
*/
|
|
|
|
|
private function buildUrl(string $endpoint): string
|
|
|
|
|
{
|
|
|
|
|
$endpoint = ltrim($endpoint, '/');
|
|
|
|
|
|
|
|
|
|
if (empty($this->baseUrl)) {
|
|
|
|
|
return $endpoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->baseUrl.'/'.$endpoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 헤더 병합
|
|
|
|
|
*/
|
|
|
|
|
private function buildHeaders(array $additionalHeaders): array
|
|
|
|
|
{
|
|
|
|
|
return array_merge(
|
|
|
|
|
[
|
|
|
|
|
'Accept' => 'application/json',
|
|
|
|
|
'Content-Type' => 'application/json',
|
|
|
|
|
],
|
|
|
|
|
$this->defaultHeaders,
|
|
|
|
|
$additionalHeaders
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|