Eureka를 활용한 서비스 탐색(Service Discovery)과 마이크로서비스 호출

2025. 3. 14. 17:31Spring Microservice

🌐 Eureka를 활용한 서비스 탐색(Service Discovery)과 마이크로서비스 호출 🚀

마이크로서비스 아키텍처(MSA)에서는 서비스 간의 동적인 탐색과 통신이 필수적입니다.
이번 글에서는 Licensing Service가 Organization Service를 직접적인 위치 정보 없이 호출하는 방법을 알아보겠습니다. 💡

🔹 목표

  • Licensing Service가 Eureka를 활용하여 Organization Service의 물리적 위치를 찾는 방법
  • Spring Cloud Load Balancer를 활용하는 3가지 방식
  • 실제 코드 예제와 서비스 호출 방법

🎯 1. 서비스 탐색(Service Discovery)란?

Eureka를 사용하면 서비스의 위치(IP & 포트)를 직접 설정하지 않아도 됩니다.
이렇게 하면 서비스가 자동 확장(Auto Scaling)되거나 장애 조치(Failover)될 때도 동적으로 위치를 탐색할 수 있습니다.

Licensing Service가 Organization Service를 호출하는 과정
1️⃣ Licensing Service는 Eureka에 등록된 Organization Service의 위치를 탐색
2️⃣ Eureka는 해당 서비스의 IP & 포트 정보를 반환
3️⃣ Licensing Service는 반환된 정보를 기반으로 Organization Service에 요청 전송

🔹 직접적인 서비스 위치를 알 필요 없이 Eureka를 통해 탐색 가능유연한 서비스 호출 가능!

🔗 2. Spring Cloud Load Balancer를 활용한 3가지 방식

Spring Boot에서는 Eureka를 활용하여 다른 서비스의 위치를 탐색하는 여러 가지 방법을 제공합니다.
이번 글에서는 3가지 클라이언트 라이브러리를 활용한 서비스 호출 방법을 살펴보겠습니다.

방식 사용 라이브러리 특징
Discovery Client DiscoveryClient 가장 낮은 수준의 API
RestTemplate (Load Balanced) RestTemplate + LoadBalancer 중간 수준의 추상화
Feign Client OpenFeign 가장 높은 수준의 추상화

💡 클라이언트가 Eureka를 활용하여 Organization Service를 찾는 방법은 다르지만,
결과적으로 모두 Eureka Server에서 등록된 서비스의 위치를 가져와서 호출하는 것이 핵심입니다.

🏗 3. LicenseController에서 동적 클라이언트 호출

먼저, LicenseController에서 3가지 클라이언트 유형 중 하나를 선택하여 요청을 처리할 수 있도록 설정합니다.

📌 LicenseController.java

@RequestMapping(value="/{licenseId}/{clientType}", method = RequestMethod.GET)
public License getLicensesWithClient(
    @PathVariable("organizationId") String organizationId,
    @PathVariable("licenseId") String licenseId,
    @PathVariable("clientType") String clientType) {
    return licenseService.getLicense(organizationId, licenseId, clientType);
}

요청 경로 예시:

GET http://localhost:8082/v1/organization/123/license/456/discovery
GET http://localhost:8082/v1/organization/123/license/456/rest
GET http://localhost:8082/v1/organization/123/license/456/feign

clientType을 통해 호출 방식 선택:

  • discoverySpring Discovery Client 활용
  • restLoad Balancer가 적용된 RestTemplate 활용
  • feignNetflix Feign Client 활용

⚙️ 4. LicenseService에서 클라이언트 유형에 따라 서비스 호출

LicenseService에서 클라이언트 유형에 따라 Organization Service를 호출하도록 구현합니다.

📌 LicenseService.java

public License getLicense(String licenseId, String organizationId, String clientType) {
    License license = licenseRepository.findByOrganizationIdAndLicenseId(organizationId, licenseId);
    if (license == null) {
        throw new IllegalArgumentException(
            String.format(messages.getMessage("license.search.error.message", null, null),
            licenseId, organizationId)
        );
    }

    Organization organization = retrieveOrganizationInfo(organizationId, clientType);
    if (organization != null) {
        license.setOrganizationName(organization.getName());
        license.setContactName(organization.getContactName());
        license.setContactEmail(organization.getContactEmail());
        license.setContactPhone(organization.getContactPhone());
    }

    return license.withComment(config.getExampleProperty());
}

retrieveOrganizationInfo(organizationId, clientType) 를 호출하여 Organization Service 정보를 가져옴
clientType에 따라 적절한 방식으로 Eureka에서 Organization Service 정보를 조회

🔍 5. 3가지 방법을 활용한 서비스 호출 방식

1️⃣ Discovery Client 활용 (DiscoveryClient)

📌 DiscoveryClient 기반 Eureka 탐색

@Autowired
private DiscoveryClient discoveryClient;

public String getOrganizationServiceUrl() {
    List<ServiceInstance> instances = discoveryClient.getInstances("organization-service");
    if (instances.isEmpty()) return null;
    return instances.get(0).getUri().toString();
}

DiscoveryClient를 사용하여 Eureka에서 "organization-service"의 인스턴스를 조회
✔ 서비스가 여러 개 실행 중이라면 첫 번째 인스턴스를 반환

2️⃣ Load Balanced RestTemplate 활용

📌 RestTemplate을 사용하여 Eureka 기반 서비스 호출

@Autowired
private RestTemplate restTemplate;

public Organization getOrganization(String organizationId) {
    return restTemplate.getForObject(
        "http://organization-service/v1/organizations/{organizationId}",
        Organization.class, organizationId
    );
}

"organization-service"라는 논리적 이름을 사용하여 Eureka에서 서비스 위치를 동적으로 조회
✔ 실제 HTTP 요청이 발생할 때 Eureka에서 적절한 서비스 인스턴스를 선택하여 요청을 보냄

📌 Spring Boot 설정 (@LoadBalanced)

@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

@LoadBalanced 를 추가하면 RestTemplate이 Eureka와 연동되어 자동으로 서비스 위치를 탐색

3️⃣ Feign Client 활용

📌 Feign Client 인터페이스 정의

@FeignClient("organization-service")
public interface OrganizationFeignClient {
    @GetMapping("/v1/organizations/{organizationId}")
    Organization getOrganization(@PathVariable("organizationId") String organizationId);
}

Feign을 사용하면 인터페이스만 정의하면 Eureka 기반으로 서비스를 호출할 수 있음
✔ 실제 HTTP 요청 없이 자동으로 Eureka에서 "organization-service"의 위치를 탐색하고 호출

📌 Feign Client 활성화 (@EnableFeignClients)

@EnableFeignClients
@SpringBootApplication
public class LicenseServiceApplication {
}

Feign을 활성화하면 Eureka와 자동으로 연동되어 REST 요청을 간결하게 작성 가능

🎯 6. 서비스 호출 예제

Discovery Client 방식

GET http://localhost:8082/v1/organization/123/license/456/discovery

Load Balanced RestTemplate 방식

GET http://localhost:8082/v1/organization/123/license/456/rest

Feign Client 방식

GET http://localhost:8082/v1/organization/123/license/456/feign

📌 모든 방식은 Eureka에서 Organization Service의 위치를 탐색하여 자동으로 적절한 인스턴스를 호출

🏆 결론

이번 글에서는 Eureka를 활용하여 마이크로서비스를 동적으로 탐색하는 방법을 살펴봤습니다. 🚀

Spring Discovery Client → 가장 직접적인 방법
RestTemplate + LoadBalancer → 중간 수준의 추상화
Feign Client → 가장 간결하고 직관적인 방식