Looking up service instances with Spring Discovery Client

2026. 1. 4. 21:41Spring Microservice/Service Discovery

🔍 Spring Discovery Client를 사용한 서비스 인스턴스 조회하기

마이크로서비스 아키텍처(MSA)에서 서비스 간 통신은 항상 핵심적인 고민 거리입니다.
특히 서비스의 물리적 위치(IP, 포트) 가 동적으로 변하는 클라우드 환경에서는
서비스 디스커버리(Service Discovery) 없이는 안정적인 통신이 불가능합니다.

이번 챕터에서는 Spring Cloud Load Balancer 와 연동되는
👉 Spring Discovery Client 를 사용해
Eureka에 등록된 서비스 인스턴스를 직접 조회하고 호출하는 방식을 살펴봅니다.

 

🧠 Spring Discovery Client란?

Spring Discovery Client
Spring Cloud Load Balancer 및 서비스 레지스트리(Eureka 등)에
접근할 수 있는 가장 낮은 수준(low-level)의 API를 제공합니다.

이를 통해 다음이 가능합니다.

  • 📌 현재 등록된 모든 서비스 목록 조회
  • 📌 특정 서비스의 모든 인스턴스(ServiceInstance) 정보 조회
  • 📌 각 인스턴스의 URI, 호스트, 포트 정보 확인

즉, Load Balancer 내부 상태를 직접 들여다볼 수 있는 도구입니다.

 

🚀 예제 시나리오 개요

이번 예제의 목표는 다음과 같습니다.

  1. Licensing Service 에서
  2. Spring Discovery Client 를 사용해
  3. Eureka에 등록된 Organization Service 인스턴스를 조회하고
  4. 그중 하나의 인스턴스를 선택해
  5. RestTemplate 으로 직접 호출

 

🧩 1단계: Discovery Client 활성화

먼저, Licensing Service 애플리케이션에 Discovery Client 기능을 활성화해야 합니다.

📂 src/main/java/com/optimagrowth/license/LicenseServiceApplication.java

package com.optimagrowth.license;

@SpringBootApplication
@RefreshScope
@EnableDiscoveryClient
public class LicenseServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(LicenseServiceApplication.class, args);
    }
}

🔑 핵심 포인트

  • @EnableDiscoveryClient
    • Spring Cloud가 이 애플리케이션에서
    • Discovery Client + Spring Cloud Load Balancer 를 사용할 수 있도록 트리거
  • ✅ 이 설정이 있어야 Eureka와 연동 가능

 

🧩 2단계: Discovery Client를 이용한 서비스 호출 구현

이제 실제로 Discovery Client를 사용해 Organization Service를 호출하는 코드를 살펴봅니다.

📂 src/main/java/com/optimagrowth/license/service/client/OrganizationDiscoveryClient.java

@Component
public class OrganizationDiscoveryClient {

    @Autowired
    private DiscoveryClient discoveryClient;

    public Organization getOrganization(String organizationId) {

        RestTemplate restTemplate = new RestTemplate();

        List<ServiceInstance> instances =
            discoveryClient.getInstances("organization-service");

        if (instances.size() == 0) return null;

        String serviceUri = String.format(
            "%s/v1/organization/%s",
            instances.get(0).getUri().toString(),
            organizationId
        );

        ResponseEntity<Organization> restExchange =
            restTemplate.exchange(
                serviceUri,
                HttpMethod.GET,
                null,
                Organization.class,
                organizationId
            );

        return restExchange.getBody();
    }
}

 

🔎 코드 상세 분석

1️⃣ DiscoveryClient 사용

@Autowired
private DiscoveryClient discoveryClient;
  • DiscoveryClient
    • Spring Cloud Load Balancer와 상호작용하는 핵심 클래스
    • 서비스 레지스트리(Eureka)에 등록된 정보에 접근 가능

2️⃣ 서비스 인스턴스 조회

List<ServiceInstance> instances =
    discoveryClient.getInstances("organization-service");
  • "organization-service"서비스 ID
  • 반환값:
    • List<ServiceInstance>
  • ServiceInstance 는 다음 정보를 포함합니다.
    • 🌐 Hostname
    • 🔌 Port
    • 🔗 URI

 

3️⃣ 첫 번째 인스턴스 선택

if (instances.size() == 0) return null;
  • 인스턴스가 없으면 null 반환
instances.get(0)
  • ⚠️ 로드 밸런싱 없이
  • 무조건 첫 번째 인스턴스만 사용

 

4️⃣ 직접 URL 생성

String serviceUri = String.format(
    "%s/v1/organization/%s",
    instances.get(0).getUri().toString(),
    organizationId
);
  • Discovery Client로 얻은 URI를 기반으로
  • 직접 호출 URL을 조합

 

5️⃣ RestTemplate으로 서비스 호출

ResponseEntity<Organization> restExchange =
    restTemplate.exchange(
        serviceUri,
        HttpMethod.GET,
        null,
        Organization.class,
        organizationId
    );
  • 일반적인 RestTemplate 호출 방식
  • 응답 Body를 Organization 객체로 매핑

 

⚠️ Discovery Client의 현실적인 문제점

❌ 1. 클라이언트 사이드 로드 밸런싱 미사용

  • Discovery Client를 직접 호출하면
    • 단순히 서비스 목록만 제공
  • 어떤 인스턴스를 호출할지는 개발자 책임
  • Spring Cloud Load Balancer의
    • 라운드 로빈
    • 가중치
    • 상태 기반 분산
      👉 전혀 활용하지 못함

❌ 2. 불필요한 코드 증가

  • 직접 해야 하는 작업들:
    • 서비스 인스턴스 선택
    • URL 직접 조합
    • 호출 로직 관리
  • ✋ “적을수록 좋은 코드” 원칙에 위배

 

🧠 RestTemplate 직접 생성에 대한 숨은 의도

눈치 빠른 Spring 개발자라면 다음 코드에서 의문을 가질 수 있습니다.

RestTemplate restTemplate = new RestTemplate();

 

🤔 왜 @Autowired를 사용하지 않았을까?

일반적으로는 이렇게 씁니다.

@Autowired
private RestTemplate restTemplate;

하지만 여기서는 의도적으로 직접 생성했습니다.

🔍 이유는 바로 이것입니다

  • @EnableDiscoveryClient 가 활성화되면
  • Spring이 관리하는 모든 RestTemplate Bean 에는
    • Load Balancer 인터셉터가 자동으로 주입됩니다
  • 이 인터셉터는
    • http://organization-service/...
    • 같은 논리적 서비스 이름 기반 URL
    • 실제 인스턴스 주소로 변환합니다

👉 하지만 이번 예제는
Discovery Client의 “순수한 동작”을 보여주기 위한 것이므로

  • ❌ Load Balancer 개입 제거
  • ❌ 자동 URL 변환 제거
  • ✅ Discovery Client 결과만 사용

그래서 RestTemplate을 직접 new로 생성한 것입니다.

 

🧾 정리 (Takeaway)

Spring Discovery Client는 강력하지만 저수준 도구입니다.
✔ 서비스 메타데이터를 직접 다뤄야 할 때만 사용해야 합니다.
✔ 일반적인 서비스 호출에는:

  • 🔥 LoadBalancer 기반 RestTemplate
  • 🔥 OpenFeign
    같은 고수준 추상화 도구가 훨씬 적합합니다.

👉 Discovery Client는
“서비스를 이해하기 위한 도구”이지
“일반적인 서비스 호출 도구”는 아닙니다.