0Pricing
PHP Academy · 강의

API 게이트웨이와 서비스 검색

서비스를 동적으로 라우팅하고 집계하며 찾습니다.

API 게이트웨이와 서비스 검색은(는) CoddyKit의 무료 PHP Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PHP Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

게이트웨이와 서비스 검색

서비스가 열두 개쯤 되면 두 가지 문제가 나타납니다. 클라이언트가 각 서비스의 주소를 알 필요가 없어야 하고, 화면 하나를 렌더링하기 위해 다섯 서비스에 직접 호출할 필요도 없어야 합니다. 이것이 API 게이트웨이의 역할입니다. 또한 규모가 늘고 줄면서 IP가 바뀌는 서비스는 서로를 찾을 방법이 필요합니다. 이것이 서비스 검색입니다.

이 레슨에서는 두 가지를 모두 다루며, 게이트웨이 경계에서는 PHP를 사용합니다.

API 게이트웨이의 역할

API 게이트웨이는 서비스 앞에 위치하는 단일 진입점입니다. 일반적인 역할은 다음과 같습니다.

  • 올바른 백엔드로 요청을 라우팅합니다.
  • 공통 관심사: 인증, 속도 제한, CORS, TLS 종료입니다.
  • 집계: 여러 백엔드 호출을 하나의 클라이언트 응답으로 구성합니다.
  • 프로토콜 변환: 외부에서는 REST 요청을 받고 내부에서는 gRPC를 사용합니다.

클라이언트를 단순하게 유지하고, 그렇지 않으면 모든 서비스에 중복해서 넣어야 할 정책을 중앙화합니다.

엣지에서의 라우팅

게이트웨이의 핵심은 수신 경로를 업스트림 서비스에 매핑하는 것입니다. 프로덕션 게이트웨이(Kong, Traefik, Nginx, AWS API Gateway)는 이를 선언적으로 처리하지만, 그 논리는 PHP로 설명할 수 있을 만큼 단순합니다.

<?php
$routes = [
    '#^/api/orders#'    => 'http://orders-svc',
    '#^/api/customers#' => 'http://customers-svc',
    '#^/api/catalog#'   => 'http://catalog-svc',
];

function resolveUpstream(string $path, array $routes): ?string {
    foreach ($routes as $pattern => $upstream) {
        if (preg_match($pattern, $path)) {
            return $upstream . $path;
        }
    }
    return null; // 404 at the gateway
}

echo resolveUpstream('/api/orders/42', $routes), "\n";

인증 중앙화

게이트웨이에서 호출자를 한 번 검증한 다음 신뢰할 수 있는 신원 정보를 다운스트림으로 전달하면, 각 서비스가 원본 토큰을 다시 검증할 필요가 없습니다. 게이트웨이는 JWT의 서명과 만료 여부를 확인하고, 신뢰할 수 있는 네트워크를 통해 내부 요청에 X-User-Id 같은 헤더를 주입합니다.

<?php
function authenticate(string $authHeader): ?array {
    if (!str_starts_with($authHeader, 'Bearer ')) return null;
    $jwt = substr($authHeader, 7);
    $claims = verifyJwt($jwt);            // signature + exp check
    if ($claims === null) return null;
    // Forward minimal trusted identity to internal services
    return ['X-User-Id' => $claims['sub'], 'X-Scopes' => implode(',', $claims['scopes'])];
}
function verifyJwt(string $j): ?array { return ['sub' => 'u-7', 'scopes' => ['orders:read']]; }
print_r(authenticate('Bearer abc.def.ghi'));

응답 집계

모바일 화면에는 주문, 고객, 카탈로그 데이터가 모두 필요할 수 있습니다. 클라이언트가 세 번 호출하도록 하는 대신 게이트웨이가 여러 곳으로 요청을 보내고, 기다린 뒤 결과를 병합합니다. 이 작업을 빠르게 처리하려면 업스트림 호출을 순차적으로 보내지 말고 동시에 실행해야 합니다(Guzzle promises / curl_multi).

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;

$http = new Client(['timeout' => 2.0]);
$promises = [
    'order'    => $http->getAsync('http://orders-svc/orders/42'),
    'customer' => $http->getAsync('http://customers-svc/customers/7'),
    'catalog'  => $http->getAsync('http://catalog-svc/items?order=42'),
];
$results = Utils::settle($promises)->wait(); // run in parallel
// merge the fulfilled bodies into one response for the client

프런트엔드용 백엔드

하나의 범용 게이트웨이로 웹 앱, 모바일 앱, 파트너를 모두 똑같이 잘 지원하기는 어렵습니다. 각 클라이언트 유형마다 필요한 집계 방식과 데이터 형식이 다르기 때문입니다. 프런트엔드용 백엔드(BFF) 패턴은 각 클라이언트 유형에 요구 사항에 맞춘 얇은 게이트웨이를 제공하고, 공유 서비스는 범용적으로 유지합니다.

이렇게 하면 비대해진 만능 게이트웨이를 피하고 각 클라이언트 팀이 독립적으로 작업할 수 있습니다.

서비스 검색 문제

동적인 환경에서는 인스턴스가 생겼다 사라지고 IP도 바뀝니다. http://10.0.3.14:8080처럼 주소를 하드코딩하면 취약합니다. 서비스 검색은 "현재 서비스 X의 정상 인스턴스가 어떤 것인지"를 실시간으로 기록하는 레지스트리를 유지하므로, 호출자는 호출 시점에 논리적 이름을 실제 주소로 확인할 수 있습니다.

클라이언트 측 검색과 서버 측 검색

두 가지 모델이 있습니다.

  • 클라이언트 측 — 호출자가 레지스트리(Consul, etcd)에 질의하고 직접 인스턴스를 선택하며, 자체적으로 부하를 분산합니다.
  • 서버 측 — 호출자가 안정적인 가상 주소(로드 밸런서 / Kubernetes Service)에 요청하면 해당 주소가 대신 주소를 확인하고 부하를 분산합니다.

Kubernetes에서는 보통 서버 측 검색을 별도 설정 없이 사용할 수 있습니다. http://customers-svc를 호출하면 클러스터의 DNS와 Service가 나머지 작업을 처리합니다. k8s 외부에서는 Consul 방식의 레지스트리가 흔히 사용됩니다.

레지스트리 조회

클라이언트 측 검색을 사용할 때 PHP 호출자는 레지스트리에 정상 인스턴스를 요청하고 그중 하나를 선택합니다. 레지스트리는 상태 점검을 통과한 인스턴스만 반환하므로, 작동하지 않는 노드는 자동으로 제외됩니다.

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;

function discover(Client $http, string $service): string {
    // Consul: only passing-health instances
    $res = $http->get("http://consul:8500/v1/health/service/$service?passing=true");
    $nodes = json_decode((string) $res->getBody(), true);
    if (!$nodes) throw new \RuntimeException("No healthy $service");
    $pick = $nodes[array_rand($nodes)]['Service']; // simple LB
    return "http://{$pick['Address']}:{$pick['Port']}";
}

상태 점검과 등록

검색의 품질은 상태 데이터의 품질을 넘을 수 없습니다. 각 서비스는 실제 의존성(데이터베이스, 캐시)을 확인하는 /health 엔드포인트를 제공하고, 시작할 때 스스로 등록하거나 플랫폼이 등록하도록 합니다. 레지스트리는 해당 엔드포인트를 점검하고 실패하는 인스턴스를 제거합니다.

상태 점검은 의미 있게 작성해야 합니다. 데이터베이스가 중단되었는데 200을 반환하는 것은 쓸모없는 정도를 넘어 더 나쁩니다. 문제가 있는 노드로 트래픽을 보내기 때문입니다.

<?php
// GET /health
function health(PDO $db, Redis $cache): array {
    $checks = [
        'db'    => safe(fn() => $db->query('SELECT 1') !== false),
        'cache' => safe(fn() => $cache->ping() === '+PONG'),
    ];
    $ok = !in_array(false, $checks, true);
    http_response_code($ok ? 200 : 503);
    return ['status' => $ok ? 'pass' : 'fail', 'checks' => $checks];
}
function safe(callable $c): bool { try { return (bool) $c(); } catch (\Throwable) { return false; } }

활성 상태와 준비 상태

상태 엔드포인트 하나만으로는 충분하지 않습니다. 다음 두 질문을 구분해야 합니다.

  • 활성 상태: "프로세스가 실행 중인가요?" 실패하면 오케스트레이터가 컨테이너를 재시작합니다. 비용이 적고 의존성이 없도록 유지해야 합니다. 그렇지 않으면 불안정한 데이터베이스 때문에 불필요한 재시작이 발생합니다.
  • 준비 상태: "지금 트래픽을 처리할 수 있나요?" 실패하면 트래픽은 보류되지만 프로세스는 계속 실행됩니다(예: 캐시를 준비 중이거나 데이터베이스에 일시적으로 연결할 수 없는 경우).

두 상태를 혼동하면 재시작이 반복되거나 아직 준비되지 않은 노드로 라우팅되는 문제가 발생합니다.

<?php
// GET /livez  - is the process itself healthy? (no external deps)
function livez(): void { http_response_code(200); echo 'alive'; }

// GET /readyz - should we receive traffic? (checks dependencies)
function readyz(PDO $db): void {
    try { $db->query('SELECT 1'); http_response_code(200); echo 'ready'; }
    catch (\Throwable) { http_response_code(503); echo 'not ready'; }
}

빠른 확인

클라이언트의 왕복 요청 줄이기

요약

서비스 라우팅과 검색:

  • API 게이트웨이는 라우팅, 인증, 속도 제한, TLS, 집계를 중앙화합니다.
  • 동시에 집계하고, 클라이언트의 요구가 달라질 때는 BFF를 사용합니다.
  • 서비스 검색은 논리적 이름을 실행 중인 정상 인스턴스로 확인합니다.
  • 클라이언트 측(레지스트리에 질의) 검색과 서버 측(안정적인 LB / k8s Service) 검색이 있습니다.
  • 의미 있는 상태 점검은 문제가 있는 노드로 트래픽이 가지 않도록 합니다.

다음에는 일부가 필연적으로 실패하더라도 이 모든 호출을 복원력 있게 유지하는 방법을 살펴봅니다.

자주 묻는 질문

“API 게이트웨이와 서비스 검색” 강의는 무료인가요?

네 — “API 게이트웨이와 서비스 검색” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PHP Academy 강의 전체를 잠금 해제할 수 있습니다. PHP Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“API 게이트웨이와 서비스 검색”에서 뭘 배우나요?

서비스를 동적으로 라우팅하고 집계하며 찾습니다. 브라우저에서 직접 실행하는 실습 코드로 PHP Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

PHP Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 PHP Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“API 게이트웨이와 서비스 검색” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 PHP Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 PHP Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 모놀리스에서 마이크로서비스로
  2. 서비스 통신: REST와 gRPC
  3. API 게이트웨이와 서비스 검색
  4. 복원력: 회로 차단기와 재시도
← PHP Academy(으)로 돌아가기