Experience/LG CNS AM Inspire Camp 1기

[LG CNS AM Inspire Camp] 23. Eureka로 해결하는 마이크로서비스 주소 관리의 복잡성

chillmyh 2025. 5. 14. 22:44

1. 들어가며

마이크로서비스 아키텍처를 도입하면, 애플리케이션은 하나의 덩어리가 아니라 여러 개의 독립된 서비스로 쪼개지게 된다. 이 각각의 서비스는 독립적으로 배포되고, 개별적으로 확장 가능하다는 장점이 있지만, 반대로 각 서비스가 서로를 찾고 통신해야하는 문제가 생긴다. 이 때, 이 문제를 해결해주는 Spring Cloud Eureka와 Service Discovery 개념에 대해서 정리해보려고 한다.

 

1.1 마이크로서비스에서 주소 관리는 왜 복잡해질까?

근데, 왜 마이크로서비스에서 주소 관리는 복잡해진다고 할까? 서비스를 여러 개로 쪼개면 다음과 같은 문제가 생긴다.

어떤 서비스가 다른 서비스를 호출할 때 정확한 주소(IP:PORT)를 알아야 한다

 

그런데 이 주소는 환경마다 다르고, 특히 클라우드 환경에선 컨테이너가 재시작되거나 Auto Scaling이 동작하면서 IP나 포트가 수시로 바뀐다. 수동으로 이걸 관리한다는 건 거의 불가능에 가깝다. 이런 문제를 해결하기 위해 서비스 디스커버리(Service Discovery)를 도입하게 된다.

 

2. 서비스 디스커버리란?

서비스 디스커버리는 말 그대로 서비스를 동적으로 찾을 수 있도록 도와주는 기능이다.

이를 위해 보통 다음 세 가지 역할이 존재한다.

 

  • Service Registry: 서비스의 주소 정보를 저장하고 관리하는 중앙 저장소. (Eureka Server)
  • Service Provider: 자신의 주소를 Registry에 등록하는 서비스. (예: user-service)
  • Service Consumer: Registry에서 주소를 조회해 다른 서비스를 호출하는 주체. (예: order-service)

 

2.1 Eureka란 무엇인가?

Spring Cloud에서 지원하는 대표적인 서비스 디스커버리 도구가 바로 Eureka다. Netflix에서 만든 오픈소스 프로젝트로, Spring Cloud와 매우 잘 통합되어 있어 쉽게 사용할 수 있다.

Eureka는 Service Registry 역할을 하고, 클라이언트들은 Eureka에 자신을 등록하거나 다른 서비스를 조회하는 방식으로 동작한다.

또한 자체적으로 Health Check, Self-preservation 모드, 복제(Replication) 같은 고급 기능도 제공한다.

 

3. Eureka Server 실습 예제

Eureka를 쓰기 위해선 먼저 서비스 주소를 저장하고 관리할 Eureka Server를 만들어야 한다.

 

3.1 의존성 추가

dependencies {
  implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
}

 

 

3.2 설정과 어노테이션

Eureka 서버는 자기 자신을 등록할 필요가 없기 때문에 아래처럼 설정한다.

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

 

그리고 어플리케이션에 @EnableEurekaServer를 붙이면 끝이다.

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class, args);
  }
}

 

서버를 실행하면 http://localhost:8761 에서 Eureka 대시보드를 확인할 수 있다.

 

4. Eureka Client 만들기

4.1 의존성 추가

이제 실제로 서비스들(user-service, order-service 등)이 Eureka에 자신을 등록하고, 다른 서비스를 찾을 수 있게 설정해보자.

dependencies {
  implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}

 

4.2 설정 예시

spring:
  application:
    name: user-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

여기서 중요한 건 spring.application.name이다. 이 이름이 곧 Eureka에 등록되는 서비스 식별자가 된다.

 

5. 서비스 간 통신은 어떻게?

Eureka에 등록했으니 이제 서비스 간에 직접 IP나 포트를 쓰지 않고도 서로를 호출할 수 있다.

 

5.1 RestTemplate + Eureka

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
  return new RestTemplate();
}
restTemplate.getForObject("http://user-service/api/users", String.class);

여기서 user-service는 Eureka에 등록된 서비스 이름이다.
@LoadBalanced를 붙여줌으로써, RestTemplate이 Eureka와 통합되어 주소를 동적으로 가져올 수 있다.

 

5.2 FeignClient 사용

더 간결하고 선언적인 방식으로는 FeignClient를 사용할 수 있다.

@FeignClient(name = "user-service")
public interface UserClient {
  @GetMapping("/api/users")
  List<User> getUsers();
}

Feign은 내부적으로 Eureka를 조회해서 동적으로 통신을 처리해주기 때문에 매우 깔끔한 방식이다.

 

6. Eureka의 추가 기능

6.1 인스턴스 상태 관리

Eureka는 기본적으로 Self-preservation 모드가 켜져 있어서, 서비스가 잠깐 네트워크 문제로 다운되어도 바로 제거하지 않는다.

statusPageUrl, healthCheckUrl 같은 메타데이터도 함께 등록할 수 있어 상태 모니터링에 도움이 된다.

 

6.2 Eureka Replication

Eureka는 여러 대로 구성하여 서로 레지스트리를 복제할 수 있다.

고가용성(HA)를 위해서 실무에선 2대 이상 Eureka 서버를 두기도 한다고 한다.

 

7. 마무리

Eureka는 작은 규모의 Spring 기반 마이크로서비스 아키텍처에서 간단한 설정으로 동적 서비스 탐색을 구현할 수 있는 장점이 있다.
하지만 Kubernetes 같은 클라우드 네이티브 환경에서는 Service Mesh나 DNS 기반 디스커버리 방식이 더 일반적이긴 하다.

그래도 Spring 환경에서는 여전히 매우 실용적인 기술이다. 특히 Gateway, Config, Feign, Bus 등과 연계해서 사용하면 진짜 마이크로서비스처럼 유기적으로 움직이는 시스템을 만들 수 있다. 다음은 spring cloud gateway에 대해서 정리해보겠다.