🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Eureka Client 설정 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 21. · 4 Views

Eureka Client 설정 완벽 가이드

Spring Cloud Netflix Eureka Client를 활용한 마이크로서비스 등록과 발견을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리로 풀어낸 가이드입니다. 서비스 등록부터 헬스 체크, 등록 해제까지 전 과정을 다룹니다.


목차

  1. Eureka Client 의존성
  2. 서비스 등록 설정
  3. 인스턴스 정보 설정
  4. 헬스 체크 구성
  5. 서비스 발견 사용
  6. 등록 해제 처리

1. Eureka Client 의존성

김개발 씨는 이번 프로젝트에서 처음으로 마이크로서비스 아키텍처를 접하게 되었습니다. 팀장님이 "이제 우리 서비스들을 Eureka에 등록해야 해요"라고 말씀하셨는데, 무슨 말인지 도통 감이 오지 않았습니다.

일단 첫 번째 단계는 의존성을 추가하는 것이라고 합니다.

Eureka Client 의존성은 Spring Boot 애플리케이션이 Eureka 서버와 통신할 수 있게 해주는 라이브러리입니다. 마치 국제 전화를 걸려면 국제 전화 서비스에 가입해야 하는 것처럼, 서비스 등록소에 접속하려면 먼저 필요한 도구를 설치해야 합니다.

이 의존성을 추가하면 자동으로 많은 설정이 활성화되어 별도 코드 작성 없이도 서비스 등록이 가능해집니다.

다음 코드를 살펴봅시다.

// Maven pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

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

// Spring Boot 메인 클래스
@SpringBootApplication
@EnableDiscoveryClient  // Eureka Client 활성화
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

[도입 - 실무 상황 스토리] 김개발 씨는 입사 6개월 차 주니어 개발자입니다. 지금까지는 단일 서버에서 모든 기능을 처리하는 모놀리식 애플리케이션만 개발해왔습니다.

그런데 이번 프로젝트는 다릅니다. 주문 서비스, 결제 서비스, 배송 서비스가 각각 분리되어 있고, 서로 통신해야 한다고 합니다.

팀장 박시니어 씨가 설명합니다. "이제 마이크로서비스 시대예요.

각 서비스가 어디에 있는지 자동으로 찾아주는 게 필요하죠. 그게 바로 Eureka입니다." [개념 설명 - 비유로 쉽게] 그렇다면 Eureka Client란 정확히 무엇일까요?

쉽게 비유하자면, Eureka는 마치 대형 쇼핑몰의 안내 데스크와 같습니다. 각 매장(서비스)이 안내 데스크에 "저희는 3층 302호에서 신발을 팔아요"라고 등록하면, 손님(다른 서비스)이 "신발 파는 곳 어디예요?"라고 물었을 때 즉시 안내해줄 수 있습니다.

Eureka Client는 바로 이 안내 데스크에 매장 정보를 등록하고 다른 매장 위치를 조회하는 역할을 담당합니다. [왜 필요한가 - 문제 상황] Eureka Client가 없던 시절에는 어땠을까요?

개발자들은 각 서비스의 IP 주소와 포트 번호를 설정 파일에 직접 적어야 했습니다. 예를 들어 결제 서비스가 "192.168.1.50:8080"에 있다고 하드코딩하는 식이었죠.

문제는 서버가 추가되거나 이동할 때마다 모든 설정 파일을 수정해야 한다는 것이었습니다. 더 큰 문제는 서버가 다운되었을 때 자동으로 감지할 방법이 없다는 점이었습니다.

[해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 Eureka Client가 등장했습니다. Eureka Client를 사용하면 서비스가 시작될 때 자동으로 Eureka 서버에 등록됩니다.

또한 다른 서비스의 위치를 코드에 하드코딩하지 않고 동적으로 조회할 수 있습니다. 무엇보다 서비스 인스턴스가 여러 개 있을 때 자동으로 로드 밸런싱까지 처리해준다는 큰 이점이 있습니다.

[코드 분석 - 단계별 설명] 위의 코드를 한 줄씩 살펴보겠습니다. 먼저 Maven이나 Gradle에 spring-cloud-starter-netflix-eureka-client 의존성을 추가합니다.

이것이 가장 중요한 첫 단계입니다. 이 의존성 하나로 Eureka와 통신하는 데 필요한 모든 라이브러리가 자동으로 포함됩니다.

다음으로 Spring Boot 메인 클래스에 @EnableDiscoveryClient 어노테이션을 추가합니다. 이 어노테이션이 붙으면 애플리케이션 시작 시 자동으로 Eureka 서버에 연결을 시도합니다.

참고로 Spring Cloud의 최신 버전에서는 @EnableDiscoveryClient를 생략해도 자동 설정이 작동하지만, 명시적으로 적어주는 것이 코드의 의도를 분명히 한다는 점에서 권장됩니다. [실무 활용 사례] 실제 현업에서는 어떻게 활용할까요?

예를 들어 쇼핑몰 서비스를 개발한다고 가정해봅시다. 주문 서비스, 재고 서비스, 결제 서비스가 각각 독립적으로 배포되어 있습니다.

주문이 들어오면 주문 서비스는 재고 서비스와 결제 서비스를 호출해야 합니다. Eureka Client가 설정되어 있으면 "재고 서비스 어디 있어?"라고 Eureka에게 물어보기만 하면 되고, Eureka가 현재 살아있는 재고 서비스 인스턴스 목록을 알려줍니다.

네이버, 카카오 같은 대형 IT 기업에서도 이런 서비스 디스커버리 패턴을 적극적으로 사용하고 있습니다. [주의사항] 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 의존성만 추가하고 Eureka 서버 주소를 설정하지 않는 것입니다. 이렇게 하면 애플리케이션이 시작될 때 Eureka 서버를 찾지 못해 계속 재시도하며 로그가 에러로 가득 차게 됩니다.

따라서 의존성 추가 후 반드시 application.yml에 Eureka 서버 위치를 명시해야 합니다. [정리] 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 먼저 의존성을 추가하는 거군요!" Eureka Client 의존성은 마이크로서비스 아키텍처의 시작점입니다.

이것을 제대로 설정하면 서비스 간 통신이 훨씬 유연해지고 관리하기 쉬워집니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - Spring Cloud 버전과 Spring Boot 버전 호환성을 확인하세요. 공식 문서의 호환성 매트릭스를 참고하면 안전합니다.

  • 로컬 개발 시에는 eureka.client.enabled=false로 설정해서 Eureka 연결을 비활성화할 수 있습니다.
  • @EnableEurekaClient는 Eureka 전용이고, @EnableDiscoveryClient는 다른 서비스 디스커버리 도구와도 호환됩니다.

2. 서비스 등록 설정

의존성 추가는 완료했지만, 김개발 씨는 여전히 막막했습니다. "이제 뭘 해야 하죠?" 박시니어 씨가 웃으며 대답했습니다.

"이제 우리 서비스가 누구인지 알려줘야죠. application.yml 파일을 열어보세요."

서비스 등록 설정은 Eureka 서버에 자신의 서비스 정보를 등록하기 위한 기본 설정입니다. 마치 신입 사원이 회사에 입사하면 인사팀에 자기소개서를 제출하는 것처럼, 서비스도 자신의 이름과 주소를 Eureka에 알려야 합니다.

이 설정을 통해 서비스는 고유한 식별자를 갖게 되고, 다른 서비스들이 찾을 수 있게 됩니다.

다음 코드를 살펴봅시다.

# application.yml
spring:
  application:
    name: order-service  # 서비스 이름 (매우 중요!)

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/  # Eureka 서버 주소
    register-with-eureka: true  # 이 서비스를 등록할 것인지
    fetch-registry: true  # 다른 서비스 정보를 가져올 것인지
  instance:
    hostname: localhost  # 이 인스턴스의 호스트명
    prefer-ip-address: true  # IP 주소 사용 여부

server:
  port: 8081  # 이 서비스가 실행될 포트

[도입 - 실무 상황 스토리] 김개발 씨가 application.yml 파일을 열어보니 생소한 설정들이 가득했습니다. spring.application.name, eureka.client.service-url...

이게 다 뭘까요? 설정 하나하나가 중요해 보이는데, 어떤 것부터 작성해야 할지 막막했습니다.

박시니어 씨가 화면을 가리키며 설명을 시작했습니다. "가장 중요한 건 서비스 이름이에요.

이게 바로 이 서비스의 신분증이죠." [개념 설명 - 비유로 쉽게] 그렇다면 서비스 등록 설정이란 정확히 무엇일까요? 쉽게 비유하자면, 이것은 마치 아파트 입주 신청서와 같습니다.

새 아파트에 입주할 때 이름, 동호수, 연락처 등을 관리사무소에 제출하죠. 그래야 택배가 왔을 때 경비실에서 여러분을 찾을 수 있고, 다른 입주민들도 여러분이 몇 동 몇 호에 사는지 알 수 있습니다.

서비스 등록 설정도 마찬가지로 Eureka라는 관리사무소에 "저는 order-service이고, 8081번 포트에서 살고 있어요"라고 알려주는 것입니다. [왜 필요한가 - 문제 상황] 서비스 이름을 제대로 설정하지 않으면 어떻게 될까요?

만약 spring.application.name을 설정하지 않으면 Spring Boot는 기본값으로 "unknown"이라는 이름을 사용합니다. 문제는 여러 서비스가 모두 "unknown"이라는 같은 이름으로 등록되면 Eureka가 이들을 구분할 수 없다는 것입니다.

또한 Eureka 서버 주소를 잘못 입력하면 서비스는 계속 연결을 시도하다가 결국 타임아웃 에러를 발생시킵니다. [해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 명확한 서비스 등록 설정이 필요합니다.

서비스 이름을 명확히 지정하면 다른 서비스들이 "order-service"라는 이름으로 이 서비스를 찾을 수 있습니다. 또한 Eureka 서버 주소를 정확히 설정하면 서비스가 시작될 때 자동으로 등록되고, 종료될 때 자동으로 등록 해제됩니다.

무엇보다 prefer-ip-address 설정을 통해 호스트명 대신 IP 주소를 사용할 수 있어, 컨테이너 환경에서도 문제없이 작동한다는 큰 이점이 있습니다. [코드 분석 - 단계별 설명] 위의 설정을 한 줄씩 살펴보겠습니다.

먼저 spring.application.name은 이 서비스의 고유한 이름입니다. 다른 서비스들이 이 이름으로 호출하게 되므로 명확하고 의미 있는 이름을 지어야 합니다.

보통 "주문-서비스", "결제-서비스" 같은 식으로 기능을 명확히 드러내는 이름을 사용합니다. eureka.client.service-url.defaultZone은 Eureka 서버의 주소입니다.

로컬 개발 환경에서는 보통 localhost:8761을 사용하지만, 실제 운영 환경에서는 Eureka 서버의 실제 주소를 입력해야 합니다. 여러 Eureka 서버가 있다면 쉼표로 구분해서 나열할 수도 있습니다.

register-with-eureka를 true로 설정하면 이 서비스가 Eureka에 자동으로 등록됩니다. false로 하면 등록되지 않죠.

fetch-registry는 다른 서비스들의 정보를 주기적으로 가져올지 여부를 결정합니다. 일반적으로 둘 다 true로 설정합니다.

prefer-ip-address를 true로 하면 호스트명 대신 IP 주소를 사용합니다. Docker나 Kubernetes 같은 컨테이너 환경에서는 호스트명이 자주 바뀌므로 IP 주소를 사용하는 것이 안전합니다.

[실무 활용 사례] 실제 현업에서는 어떻게 활용할까요? 예를 들어 스타트업에서 배달 앱을 개발한다고 가정해봅시다.

사용자 서비스, 음식점 서비스, 배달 서비스, 결제 서비스가 각각 독립적으로 운영됩니다. 각 서비스는 자신의 application.yml에 고유한 이름을 설정하고 같은 Eureka 서버 주소를 바라봅니다.

그러면 Eureka 대시보드에 접속했을 때 모든 서비스가 한눈에 보이고, 각 서비스가 몇 개의 인스턴스로 실행 중인지도 확인할 수 있습니다. 쿠팡이나 배달의민족 같은 대규모 서비스에서도 이런 방식으로 수백 개의 마이크로서비스를 관리합니다.

[주의사항] 하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 개발 환경과 운영 환경에서 같은 설정 파일을 사용하는 것입니다.

개발에서는 localhost:8761을 사용하지만, 운영에서는 실제 Eureka 서버 주소를 사용해야 합니다. 따라서 application-dev.yml, application-prod.yml처럼 환경별로 설정 파일을 분리하는 것이 좋습니다.

또 다른 실수는 서비스 이름에 공백이나 특수문자를 포함하는 것입니다. 서비스 이름은 URL의 일부로 사용되므로 영문 소문자와 하이픈만 사용하는 것이 안전합니다.

[정리] 다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 듣고 김개발 씨는 직접 설정을 작성했습니다.

서비스를 실행하고 Eureka 대시보드(http://localhost:8761)에 접속하니, "ORDER-SERVICE"라는 이름으로 자신의 서비스가 등록된 것이 보였습니다. "와, 신기해요!" 서비스 등록 설정을 제대로 하면 마이크로서비스들이 서로를 찾고 통신할 수 있는 기반이 만들어집니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 서비스 이름은 반드시 kebab-case(소문자-하이픈)로 작성하세요. camelCase나 snake_case는 피하는 것이 좋습니다.

  • 환경별 설정 파일을 사용하려면 spring.profiles.active를 활용하세요.
  • Eureka 서버가 여러 개라면 쉼표로 구분해서 모두 나열하면 한 서버가 다운되어도 다른 서버로 자동 전환됩니다.

3. 인스턴스 정보 설정

김개발 씨의 서비스가 Eureka에 등록되었지만, 박시니어 씨가 대시보드를 보더니 고개를 갸우뚱했습니다. "음, 인스턴스 정보가 좀 부족한데요?

이러면 나중에 문제를 찾기 어려울 수 있어요." 인스턴스 정보라는 게 뭘까요?

인스턴스 정보 설정은 Eureka에 등록되는 서비스의 상세 정보를 커스터마이징하는 설정입니다. 마치 명함에 이름뿐 아니라 직급, 부서, 연락처, 이메일을 추가로 적는 것처럼, 서비스 인스턴스에도 추가 정보를 담을 수 있습니다.

이 정보는 모니터링, 디버깅, 로드 밸런싱에 활용됩니다.

다음 코드를 살펴봅시다.

# application.yml
eureka:
  instance:
    instance-id: ${spring.application.name}:${random.value}  # 고유 인스턴스 ID
    hostname: ${HOSTNAME:localhost}  # 호스트명 (환경변수 사용)
    prefer-ip-address: true  # IP 주소 우선 사용
    ip-address: ${SERVER_IP:127.0.0.1}  # 명시적 IP 주소 지정

    # 메타데이터 추가
    metadata-map:
      zone: asia-northeast-2a  # 가용 영역
      version: 1.0.0  # 서비스 버전
      team: order-team  # 담당 팀

    # 리스 갱신 설정
    lease-renewal-interval-in-seconds: 30  # 하트비트 간격
    lease-expiration-duration-in-seconds: 90  # 만료 시간

[도입 - 실무 상황 스토리] 김개발 씨는 자신의 서비스가 잘 등록된 것을 보고 뿌듯해했습니다. 하지만 운영팀의 최운영 씨가 찾아와서 요청했습니다.

"김개발 씨, 서비스가 여러 개 떠 있을 때 어떤 인스턴스가 어느 서버에서 실행 중인지 알 수가 없어요. 구분할 수 있게 설정해주실 수 있나요?" 김개발 씨는 당황했습니다.

Eureka 대시보드를 보니 같은 서비스의 인스턴스들이 모두 똑같은 이름으로 표시되고 있었습니다. 어떻게 구분해야 할까요?

[개념 설명 - 비유로 쉽게] 그렇다면 인스턴스 정보 설정이란 정확히 무엇일까요? 쉽게 비유하자면, 이것은 마치 학교에서 같은 이름을 가진 학생들을 구분하는 방법과 같습니다.

"김철수"라는 학생이 3명이 있다면, "3학년 1반 김철수", "2학년 5반 김철수", "1학년 3반 김철수"처럼 추가 정보를 붙여서 구분하죠. 마찬가지로 같은 서비스의 인스턴스가 여러 개 실행될 때, 각각을 구분할 수 있는 고유한 정보를 설정해야 합니다.

[왜 필요한가 - 문제 상황] 인스턴스 정보를 제대로 설정하지 않으면 어떻게 될까요? 같은 서비스가 10대의 서버에서 실행되고 있다고 가정해봅시다.

갑자기 한 인스턴스에서 에러가 발생했는데, Eureka 대시보드에는 모두 같은 이름으로 표시됩니다. 어느 서버의 인스턴스인지 알 수 없어 문제를 찾는 데 시간이 오래 걸립니다.

더 심각한 문제는 인스턴스가 정상적으로 종료되지 않았을 때 Eureka가 이를 감지하지 못해 죽은 인스턴스로 트래픽이 계속 전달될 수 있다는 것입니다. [해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 상세한 인스턴스 정보 설정이 필요합니다.

instance-id를 고유하게 설정하면 각 인스턴스를 명확히 구분할 수 있습니다. metadata-map을 활용하면 버전, 담당 팀, 배포 환경 같은 커스텀 정보를 추가할 수 있습니다.

무엇보다 lease-renewal-intervallease-expiration-duration을 적절히 설정하면 죽은 인스턴스를 빠르게 감지하고 목록에서 제거할 수 있다는 큰 이점이 있습니다. [코드 분석 - 단계별 설명] 위의 설정을 한 줄씩 살펴보겠습니다.

먼저 instance-id는 이 인스턴스의 고유 식별자입니다. ${spring.application.name}:${random.value} 패턴을 사용하면 "order-service:a3f2c8d1" 같은 형태로 매번 다른 ID가 생성됩니다.

또는 ${spring.application.name}:${server.port} 패턴을 사용하면 "order-service:8081"처럼 포트 번호로 구분할 수도 있습니다. metadata-map은 키-값 쌍으로 자유롭게 정보를 추가할 수 있는 공간입니다.

여기에 버전 정보를 넣으면 나중에 카나리 배포나 블루-그린 배포를 할 때 유용합니다. zone 정보를 넣으면 같은 가용 영역의 서비스끼리 우선적으로 통신하도록 설정할 수도 있습니다.

lease-renewal-interval-in-seconds는 이 인스턴스가 "저 살아있어요!" 신호(하트비트)를 Eureka 서버에 보내는 주기입니다. 기본값은 30초입니다.

lease-expiration-duration-in-seconds는 하트비트가 오지 않았을 때 얼마나 기다린 후 죽었다고 판단할지를 결정합니다. 기본값은 90초입니다.

[실무 활용 사례] 실제 현업에서는 어떻게 활용할까요? 예를 들어 글로벌 서비스를 운영하는 회사에서 한국, 미국, 유럽에 각각 서버를 두고 있다고 가정해봅시다.

metadata-map에 region: korea, region: usa, region: europe 같은 정보를 넣으면, 한국 사용자의 요청은 한국 리전의 서비스로 라우팅하도록 구성할 수 있습니다. 또한 버전 정보를 활용해서 신규 버전을 일부 인스턴스에만 배포하고 테스트하는 카나리 배포도 가능합니다.

Netflix에서는 실제로 이런 메타데이터를 활용해 수천 개의 마이크로서비스를 효율적으로 관리한다고 합니다. [주의사항] 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 lease-renewal-interval을 너무 짧게 설정하는 것입니다. 예를 들어 1초로 설정하면 네트워크 트래픽이 급증하고 Eureka 서버에 부하가 걸립니다.

일반적으로 30초가 적절하며, 특별한 이유가 없다면 기본값을 사용하는 것이 좋습니다. 또 다른 주의사항은 instance-id에 공백이나 특수문자를 넣으면 URL 인코딩 문제가 발생할 수 있다는 것입니다.

영문, 숫자, 하이픈, 콜론만 사용하세요. [정리] 다시 김개발 씨의 이야기로 돌아가 봅시다.

인스턴스 정보를 상세히 설정한 후 Eureka 대시보드를 확인하니, 각 인스턴스가 명확히 구분되어 표시되었습니다. 최운영 씨가 만족스러운 표정을 지었습니다.

"이제 문제가 생겨도 어느 서버인지 바로 알 수 있겠네요!" 인스턴스 정보를 제대로 설정하면 운영 단계에서 문제를 빠르게 찾고 해결할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - instance-id는 ${spring.application.name}:${spring.cloud.client.hostname}:${server.port} 패턴도 자주 사용됩니다.

  • Docker 환경에서는 HOSTNAME 환경변수를 컨테이너 ID로 설정하면 각 컨테이너를 쉽게 구분할 수 있습니다.
  • 개발 환경에서는 lease-renewal-interval을 10초 정도로 줄여서 빠른 테스트가 가능합니다. 단, 운영에서는 30초로 되돌려야 합니다.

4. 헬스 체크 구성

김개발 씨의 서비스가 Eureka에 잘 등록되어 운영 중이었습니다. 그런데 어느 날, 데이터베이스 연결이 끊어졌는데도 Eureka는 여전히 그 인스턴스를 "정상"이라고 표시하고 있었습니다.

"왜 이런 일이 생기는 걸까요?" 박시니어 씨가 설명했습니다. "헬스 체크 설정이 빠졌네요."

헬스 체크 구성은 서비스가 실제로 정상 작동하는지 Eureka가 판단할 수 있도록 하는 설정입니다. 마치 병원에서 환자의 맥박과 혈압을 체크하는 것처럼, 애플리케이션의 데이터베이스 연결, 외부 API 상태 등을 확인해서 진짜 건강한지 판단합니다.

단순히 프로세스가 살아있다고 해서 정상이 아니라, 실제로 요청을 처리할 수 있는 상태여야 합니다.

다음 코드를 살펴봅시다.

// build.gradle에 actuator 의존성 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

# application.yml
eureka:
  client:
    healthcheck:
      enabled: true  # Actuator 헬스 체크 사용

management:
  endpoints:
    web:
      exposure:
        include: health,info  # 노출할 엔드포인트
  endpoint:
    health:
      show-details: always  # 상세 정보 표시
  health:
    db:
      enabled: true  # DB 헬스 체크
    diskspace:
      enabled: true  # 디스크 공간 체크

[도입 - 실무 상황 스토리] 김개발 씨는 주말에 편하게 쉬고 있었습니다. 그런데 갑자기 최운영 씨로부터 긴급 전화가 왔습니다.

"김개발 씨, 주문 서비스가 에러를 뿜고 있는데 Eureka에는 정상으로 떠요! 고객들이 주문을 못 하고 있어요!" 황급히 노트북을 열어보니 정말로 데이터베이스 연결이 끊어져 있었지만, Eureka 대시보드에는 초록색 "UP" 상태로 표시되고 있었습니다.

박시니어 씨가 월요일에 설명해주었습니다. "Eureka는 기본적으로 하트비트만 확인해요.

프로세스가 살아있으면 정상이라고 판단하죠. 하지만 DB가 죽었다면 실제로는 정상이 아니잖아요?

그래서 헬스 체크가 필요합니다." [개념 설명 - 비유로 쉽게] 그렇다면 헬스 체크란 정확히 무엇일까요? 쉽게 비유하자면, 이것은 마치 자동차의 계기판과 같습니다.

시동이 걸려 있다고 해서 운전할 수 있는 상태는 아닙니다. 엔진 오일은 충분한지, 브레이크는 정상인지, 연료는 남아있는지 확인해야 하죠.

헬스 체크도 마찬가지로 애플리케이션 프로세스가 실행 중이라는 것 외에, 데이터베이스 연결, 디스크 공간, 외부 API 연결 등 실제 서비스 제공에 필요한 모든 요소를 점검합니다. [왜 필요한가 - 문제 상황] 헬스 체크가 없으면 어떤 문제가 생길까요?

서비스가 시작되면 Eureka에 등록되고 하트비트를 보내기 시작합니다. 하지만 데이터베이스가 다운되거나 디스크가 가득 차도, 프로세스는 여전히 살아있기 때문에 하트비트는 계속 전송됩니다.

Eureka는 이 서비스를 정상이라고 판단하고, 다른 서비스들이 이 인스턴스로 요청을 보냅니다. 결과는?

모든 요청이 실패하고 사용자는 에러 메시지를 보게 됩니다. [해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 Spring Boot Actuator 헬스 체크가 필요합니다.

Actuator는 애플리케이션의 다양한 상태 정보를 제공하는 Spring Boot의 기능입니다. 데이터베이스가 살아있는지, 디스크 공간이 충분한지, 외부 API가 응답하는지 자동으로 확인합니다.

이 정보를 Eureka와 연동하면, 진짜 문제가 있을 때만 인스턴스를 목록에서 제거할 수 있습니다. 무엇보다 문제를 조기에 발견해서 장애를 예방할 수 있다는 큰 이점이 있습니다.

[코드 분석 - 단계별 설명] 위의 설정을 한 줄씩 살펴보겠습니다. 먼저 spring-boot-starter-actuator 의존성을 추가합니다.

이것이 헬스 체크의 핵심입니다. Actuator는 /actuator/health라는 엔드포인트를 자동으로 생성하고, 여기에 접속하면 애플리케이션의 상태를 JSON 형식으로 확인할 수 있습니다.

eureka.client.healthcheck.enabled를 true로 설정하면 Eureka가 하트비트 대신 Actuator의 헬스 체크 결과를 신뢰하게 됩니다. 이제 DB가 다운되면 헬스 체크가 "DOWN"을 반환하고, Eureka는 이 인스턴스를 자동으로 목록에서 제거합니다.

management.endpoint.health.show-details를 "always"로 설정하면 어떤 부분이 문제인지 상세히 볼 수 있습니다. 예를 들어 DB는 정상인데 디스크가 가득 찼다면, 헬스 체크 응답에 "diskSpace: DOWN"이라고 명확히 표시됩니다.

management.health.db.enableddiskspace.enabled는 어떤 항목을 체크할지 결정합니다. 필요에 따라 Redis, Kafka, RabbitMQ 등 다양한 컴포넌트의 헬스 체크를 추가할 수 있습니다.

[실무 활용 사례] 실제 현업에서는 어떻게 활용할까요? 예를 들어 전자상거래 서비스에서 결제 서비스를 운영한다고 가정해봅시다.

이 서비스는 MySQL 데이터베이스와 외부 PG사 API에 의존합니다. 헬스 체크에 두 가지를 모두 포함시키면, MySQL이 다운되거나 PG사 API가 응답하지 않을 때 자동으로 트래픽이 정상 인스턴스로만 전달됩니다.

실제로 AWS에서는 Auto Scaling과 헬스 체크를 연동해서 비정상 인스턴스를 자동으로 종료하고 새로운 인스턴스를 시작하는 자동 복구 시스템을 구축합니다. [주의사항] 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 헬스 체크에 너무 많은 의존성을 포함하는 것입니다. 예를 들어 메인 기능과 무관한 캐시 서버까지 헬스 체크에 포함시키면, 캐시가 잠깐 다운되었을 때 서비스 전체가 DOWN으로 표시됩니다.

핵심 의존성만 포함시켜야 합니다. 또 다른 주의사항은 헬스 체크가 너무 오래 걸리면 타임아웃이 발생할 수 있다는 것입니다.

외부 API 호출은 타임아웃을 짧게(1-2초) 설정하세요. [정리] 다시 김개발 씨의 이야기로 돌아가 봅시다.

헬스 체크를 설정한 후, 테스트로 데이터베이스를 잠깐 중단해보았습니다. 즉시 Eureka 대시보드에서 해당 인스턴스가 빨간색 "DOWN"으로 바뀌었고, 새로운 트래픽이 전달되지 않았습니다.

DB를 다시 시작하니 자동으로 "UP"으로 복구되었습니다. "이제 안심이네요!" 헬스 체크를 제대로 설정하면 장애 상황에서 자동으로 문제를 감지하고 대응할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 커스텀 헬스 체크를 만들려면 HealthIndicator 인터페이스를 구현하세요. 외부 API 상태 등을 직접 체크할 수 있습니다.

  • show-details는 운영 환경에서는 "when-authorized"로 설정해서 보안을 강화하세요.
  • 헬스 체크 엔드포인트(/actuator/health)는 모니터링 도구(Prometheus, Grafana)와 연동하면 더욱 강력합니다.

5. 서비스 발견 사용

김개발 씨의 주문 서비스가 Eureka에 잘 등록되었습니다. 이제 다른 서비스의 위치를 찾아서 호출해야 하는데, 어떻게 해야 할까요?

"IP 주소를 직접 입력하면 되지 않나요?" 박시니어 씨가 고개를 저었습니다. "그러면 Eureka를 사용하는 의미가 없죠.

서비스 발견 기능을 활용해야 합니다."

서비스 발견은 Eureka에 등록된 다른 서비스의 위치를 동적으로 찾아서 호출하는 기능입니다. 마치 전화번호부에서 친구 이름으로 전화번호를 찾는 것처럼, 서비스 이름만 알면 현재 실행 중인 인스턴스의 주소를 자동으로 찾아줍니다.

RestTemplate이나 WebClient에 @LoadBalanced를 붙이면 자동으로 서비스 발견과 로드 밸런싱이 적용됩니다.

다음 코드를 살펴봅시다.

// Config 클래스에서 RestTemplate 빈 생성
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced  // 서비스 발견 + 로드 밸런싱 활성화
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

// 다른 서비스 호출하기
@Service
public class OrderService {

    private final RestTemplate restTemplate;

    public OrderService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public PaymentResponse processPayment(PaymentRequest request) {
        // IP 주소 대신 서비스 이름 사용!
        String url = "http://payment-service/api/payments";
        return restTemplate.postForObject(url, request, PaymentResponse.class);
    }
}

[도입 - 실무 상황 스토리] 김개발 씨는 주문 서비스에서 결제 서비스를 호출해야 했습니다. 처음에는 이렇게 코드를 작성했습니다.

String url = "http://192.168.1.100:8082/api/payments"; 하지만 박시니어 씨가 코드 리뷰에서 지적했습니다. "IP 주소를 하드코딩하면 나중에 서버가 바뀔 때마다 코드를 수정해야 해요.

서비스가 10개, 100개로 늘어나면 관리가 불가능하죠." 김개발 씨는 고민에 빠졌습니다. 그렇다면 어떻게 해야 할까요?

"Eureka에 등록된 서비스 이름으로 호출하면 됩니다." 박시니어 씨의 설명이 시작되었습니다. [개념 설명 - 비유로 쉽게] 그렇다면 서비스 발견이란 정확히 무엇일까요?

쉽게 비유하자면, 이것은 마치 스마트폰 연락처와 같습니다. 친구에게 전화할 때 "010-1234-5678"을 외워서 누르지 않습니다.

그냥 "김철수"를 검색하면 자동으로 전화번호를 찾아주죠. 심지어 김철수가 번호를 바꿨어도, 연락처만 업데이트하면 다음부터는 새 번호로 자동 연결됩니다.

서비스 발견도 마찬가지로 "payment-service"라는 이름만 알면, 현재 어느 IP와 포트에서 실행 중인지 Eureka가 자동으로 알려줍니다. [왜 필요한가 - 문제 상황] 서비스 발견 없이 IP를 직접 입력하면 어떻게 될까요?

개발 환경에서는 "localhost:8082"를 사용하다가, 테스트 환경에 배포하면 "test-server:8082"로 바꿔야 합니다. 운영 환경에서는 또 다른 주소로 변경해야 하죠.

환경이 3개, 서비스가 10개라면 30번의 설정 변경이 필요합니다. 더 큰 문제는 Auto Scaling으로 서버가 자동으로 추가되거나 제거될 때입니다.

새 서버의 IP를 일일이 설정 파일에 추가할 수도 없고, 다운된 서버로는 더 이상 요청을 보내면 안 되는데 이를 어떻게 알 수 있을까요? [해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 동적 서비스 발견이 필요합니다.

@LoadBalanced 어노테이션을 RestTemplate에 붙이면 마법 같은 일이 일어납니다. URL에 서비스 이름을 입력하면 Spring Cloud가 자동으로 Eureka에 질의해서 현재 실행 중인 인스턴스 목록을 가져옵니다.

여러 인스턴스가 있다면 자동으로 로드 밸런싱까지 해줍니다. 무엇보다 인스턴스가 추가되거나 제거되면 즉시 반영되어, 항상 최신 상태의 서비스 목록을 사용할 수 있다는 큰 이점이 있습니다.

[코드 분석 - 단계별 설명] 위의 코드를 한 줄씩 살펴보겠습니다. 먼저 Configuration 클래스에서 RestTemplate 빈을 생성합니다.

여기서 핵심은 @LoadBalanced 어노테이션입니다. 이 어노테이션이 붙으면 RestTemplate은 일반 HTTP 클라이언트가 아니라 Eureka 기반의 스마트 클라이언트로 변신합니다.

서비스 호출 코드를 보면 "http://payment-service/api/payments"처럼 IP 주소 대신 서비스 이름을 사용합니다. payment-service는 Eureka에 등록된 서비스의 spring.application.name입니다.

이 요청이 실행되면, 내부적으로 다음과 같은 일이 일어납니다. 첫째, Eureka 클라이언트가 "payment-service"라는 이름의 인스턴스 목록을 Eureka 서버에서 조회합니다.

둘째, 여러 인스턴스가 있다면 Ribbon(또는 Spring Cloud LoadBalancer)이 라운드 로빈 방식으로 하나를 선택합니다. 셋째, 선택된 인스턴스의 실제 IP와 포트로 URL을 변환합니다.

넷째, HTTP 요청을 전송합니다. [실무 활용 사례] 실제 현업에서는 어떻게 활용할까요?

예를 들어 이커머스 플랫폼에서 주문 서비스, 재고 서비스, 결제 서비스, 알림 서비스가 있다고 가정해봅시다. 사용자가 주문 버튼을 누르면 다음과 같은 흐름이 일어납니다.

주문 서비스가 재고 서비스에 "상품 재고 있어?"라고 물어봅니다. restTemplate.getForObject("http://inventory-service/api/stock/{productId}", Stock.class, productId) 재고가 있다면 결제 서비스를 호출합니다.

restTemplate.postForObject("http://payment-service/api/pay", paymentRequest, PaymentResponse.class) 결제가 완료되면 알림 서비스를 호출합니다. restTemplate.postForObject("http://notification-service/api/send", notification, Void.class) 모든 호출에서 IP 주소는 하나도 없습니다.

서비스 이름만으로 모든 통신이 이루어집니다. 덕분에 어느 서비스든 서버를 추가하거나 교체해도 코드 변경이 전혀 필요 없습니다.

[주의사항] 하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 @LoadBalanced를 붙이지 않은 일반 RestTemplate과 혼용하는 것입니다.

외부 API를 호출할 때는 일반 RestTemplate을 사용해야 하고, 내부 마이크로서비스를 호출할 때는 @LoadBalanced RestTemplate을 사용해야 합니다. 두 개를 별도 빈으로 만들어서 용도에 맞게 주입받는 것이 좋습니다.

또 다른 주의사항은 서비스 이름을 잘못 입력하면 런타임 에러가 발생한다는 것입니다. "paymentService"로 입력했는데 실제 등록된 이름이 "payment-service"라면 찾을 수 없다는 에러가 발생합니다.

서비스 이름은 정확히 일치해야 합니다. [정리] 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 설명을 듣고 코드를 수정했습니다. IP 주소를 모두 서비스 이름으로 바꾸고 테스트해보니 완벽하게 작동했습니다.

심지어 결제 서비스 인스턴스를 하나 더 띄워보니, 자동으로 두 인스턴스에 번갈아가며 요청이 분산되었습니다. "이게 바로 마이크로서비스의 힘이군요!" 서비스 발견을 제대로 활용하면 유연하고 확장 가능한 마이크로서비스 아키텍처를 구축할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - WebClient를 사용한다면 @LoadBalanced를 WebClient.Builder에 붙이세요. 비동기 논블로킹 방식으로 더 높은 성능을 얻을 수 있습니다.

  • Feign Client를 사용하면 RestTemplate보다 더 간결한 코드로 서비스를 호출할 수 있습니다. @FeignClient("payment-service") 한 줄로 끝납니다.
  • eureka.client.registry-fetch-interval-seconds로 서비스 목록을 얼마나 자주 갱신할지 설정할 수 있습니다. 기본값은 30초입니다.

6. 등록 해제 처리

김개발 씨는 주말에 서버 점검을 위해 서비스를 중단했습니다. 그런데 이상한 일이 벌어졌습니다.

서비스를 종료했는데도 한동안 Eureka 대시보드에 그대로 표시되었고, 다른 서비스에서 계속 호출을 시도해서 에러가 발생했습니다. "왜 바로 목록에서 제거되지 않는 걸까요?"

등록 해제 처리는 서비스가 종료될 때 Eureka 목록에서 안전하게 제거되도록 하는 설정입니다. 마치 퇴근할 때 사무실 출입증을 반납하는 것처럼, 서비스도 종료 전에 Eureka에게 "이제 그만 일합니다"라고 알려야 합니다.

정상 종료(Graceful Shutdown)와 비정상 종료 상황을 모두 고려해야 안정적인 서비스 운영이 가능합니다.

다음 코드를 살펴봅시다.

# application.yml
eureka:
  instance:
    lease-renewal-interval-in-seconds: 10  # 하트비트 간격 (빠른 감지)
    lease-expiration-duration-in-seconds: 30  # 만료 시간 (빠른 제거)

server:
  shutdown: graceful  # 정상 종료 활성화

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 종료 타임아웃

// 커스텀 종료 처리 (선택사항)
@Component
public class EurekaDeregisterListener {

    private final EurekaClient eurekaClient;

    public EurekaDeregisterListener(EurekaClient eurekaClient) {
        this.eurekaClient = eurekaClient;
    }

    @PreDestroy
    public void deregister() {
        eurekaClient.shutdown();  // 명시적 등록 해제
        System.out.println("Eureka 등록 해제 완료");
    }
}

[도입 - 실무 상황 스토리] 김개발 씨는 새벽 2시에 긴급 배포를 하게 되었습니다. 새 버전을 배포하려면 기존 서비스를 종료해야 하는데, 걱정이 되었습니다.

"종료하는 순간 진행 중인 요청들은 어떻게 되는 거지?" 더 큰 걱정은 다른 서비스들이 이미 종료된 인스턴스로 계속 요청을 보내는 것이었습니다. 박시니어 씨가 옆에서 조언했습니다.

"그래서 Graceful Shutdown이 필요한 거예요. 서비스를 우아하게 종료하는 방법을 배워야 합니다." [개념 설명 - 비유로 쉽게] 그렇다면 등록 해제 처리란 정확히 무엇일까요?

쉽게 비유하자면, 이것은 마치 식당의 라스트 오더와 같습니다. 마감 시간이 되었다고 해서 바로 문을 닫아버리면 식사 중인 손님들이 당황하겠죠.

올바른 방법은 먼저 "라스트 오더입니다"라고 안내하고, 새로운 손님은 받지 않으며, 기존 손님들이 식사를 마칠 때까지 기다린 후 문을 닫는 것입니다. 서비스 종료도 마찬가지로 새 요청은 받지 않고, 진행 중인 요청은 완료한 후, Eureka에 등록 해제를 알리는 순서로 진행되어야 합니다.

[왜 필요한가 - 문제 상황] 등록 해제를 제대로 처리하지 않으면 어떻게 될까요? 서비스를 강제 종료(kill -9)하면 Eureka에 등록 해제 신호를 보낼 시간이 없습니다.

Eureka는 여전히 이 인스턴스가 살아있다고 생각하고 목록에 유지합니다. 다른 서비스들이 이 죽은 인스턴스로 요청을 보내면 당연히 실패합니다.

Eureka가 "아, 이 인스턴스가 죽었구나"하고 깨닫는 데는 기본적으로 90초가 걸립니다. 90초 동안 사용자들은 계속 에러를 경험하게 되는 것입니다.

[해결책 - 개념의 등장] 바로 이런 문제를 해결하기 위해 Graceful Shutdown과 빠른 만료 설정이 필요합니다. server.shutdown=graceful을 설정하면 서비스 종료 시 새 요청은 거부하고 기존 요청은 처리가 완료될 때까지 기다립니다.

lease-expiration-duration을 30초로 줄이면 하트비트가 멈춘 후 30초만에 Eureka가 인스턴스를 제거합니다. 무엇보다 @PreDestroy를 활용해 명시적으로 등록 해제를 요청하면 종료 즉시 Eureka 목록에서 사라진다는 큰 이점이 있습니다.

[코드 분석 - 단계별 설명] 위의 코드를 한 줄씩 살펴보겠습니다. 먼저 server.shutdown=graceful은 Spring Boot 2.3부터 지원되는 기능입니다.

이 설정이 활성화되면 서비스 종료 명령(kill -15, Ctrl+C)을 받았을 때 즉시 종료되지 않고 진행 중인 요청들을 기다립니다. 새로운 HTTP 요청은 503 Service Unavailable로 거부됩니다.

spring.lifecycle.timeout-per-shutdown-phase는 종료를 기다리는 최대 시간입니다. 30초로 설정하면 진행 중인 요청들이 30초 안에 완료되기를 기다립니다.

30초가 지나도 완료되지 않으면 강제 종료됩니다. 너무 길면 배포 시간이 오래 걸리고, 너무 짧으면 요청이 중간에 끊길 수 있으므로 적절한 값을 찾아야 합니다.

@PreDestroy 어노테이션이 붙은 메서드는 애플리케이션 종료 시 자동으로 실행됩니다. 여기서 **eurekaClient.shutdown()**을 호출하면 Eureka 서버에 등록 해제 요청을 보냅니다.

이 방법을 사용하면 몇 초 안에 Eureka 목록에서 제거됩니다. lease-renewal-interval을 10초, lease-expiration-duration을 30초로 설정한 이유는 빠른 감지를 위해서입니다.

비정상 종료로 @PreDestroy가 실행되지 않더라도, 최대 30초 안에는 Eureka가 인스턴스 제거를 감지합니다. [실무 활용 사례] 실제 현업에서는 어떻게 활용할까요?

예를 들어 무중단 배포(Blue-Green Deployment)를 수행한다고 가정해봅시다. 새 버전(Green)을 먼저 배포하고 Eureka에 등록합니다.

헬스 체크가 통과되면 기존 버전(Blue)을 종료하는데, 이때 Graceful Shutdown이 작동합니다. 먼저 Blue 인스턴스가 Eureka에 등록 해제 신호를 보냅니다.

Eureka는 즉시 Blue를 목록에서 제거하고, 다른 서비스들은 다음 요청부터 Green으로만 호출합니다. 동시에 Blue는 진행 중인 요청들이 완료되기를 30초간 기다립니다.

모든 요청이 완료되면 프로세스가 종료됩니다. 이 과정에서 단 한 건의 요청도 실패하지 않습니다.

AWS의 ECS나 Kubernetes에서도 이런 Graceful Shutdown 메커니즘을 활용해 무중단 배포를 구현합니다. [주의사항] 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수 중 하나는 timeout-per-shutdown-phase를 너무 길게 설정하는 것입니다. 예를 들어 300초(5분)로 설정하면 배포할 때마다 5분씩 기다려야 합니다.

일반적으로 대부분의 HTTP 요청은 수 초 안에 완료되므로 30초면 충분합니다. 또 다른 주의사항은 데이터베이스 연결 풀도 정상적으로 종료되어야 한다는 것입니다.

HikariCP 같은 커넥션 풀은 자체적으로 종료 처리를 하지만, 커스텀 리소스가 있다면 @PreDestroy에서 명시적으로 정리해야 합니다. 마지막으로, Docker나 Kubernetes 환경에서는 SIGTERM 신호를 제대로 처리해야 합니다.

Java 애플리케이션이 PID 1로 실행되면 신호를 받지 못할 수 있으므로, 작은 init 프로세스를 사용하거나 exec 형식으로 실행해야 합니다. [정리] 다시 김개발 씨의 이야기로 돌아가 봅시다.

Graceful Shutdown을 설정한 후 새벽 배포를 진행했습니다. 기존 서비스에 종료 명령을 내리자 Eureka 대시보드에서 즉시 사라졌고, 진행 중이던 요청들도 모두 정상적으로 완료되었습니다.

새 버전으로의 전환이 아무런 에러 없이 매끄럽게 이루어졌습니다. "이제 배포가 두렵지 않네요!" 등록 해제 처리를 제대로 하면 무중단 배포와 안정적인 서비스 운영이 가능해집니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - Kubernetes에서는 preStop hook을 설정해서 종료 전에 sleep 10을 실행하면 더욱 안전합니다. Ingress 컨트롤러가 변경을 감지할 시간을 줍니다.

  • 로드 밸런서(ALB, NLB) 뒤에 있다면 Deregistration Delay 설정도 확인하세요. 기본값이 300초라 너무 길 수 있습니다.
  • actuator의 /shutdown 엔드포인트를 활성화하면 원격에서 Graceful Shutdown을 트리거할 수 있습니다. 단, 보안 설정은 필수입니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Spring Cloud#Eureka Client#Service Discovery#Microservices#Netflix OSS

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

Istio 설치와 구성 완벽 가이드

Kubernetes 환경에서 Istio 서비스 메시를 설치하고 구성하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. istioctl 설치부터 사이드카 주입까지 단계별로 학습합니다.

서비스 메시 완벽 가이드

마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.

Helm 마이크로서비스 패키징 완벽 가이드

Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.

관찰 가능한 마이크로서비스 완벽 가이드

마이크로서비스 환경에서 시스템의 상태를 실시간으로 관찰하고 모니터링하는 방법을 배웁니다. Resilience4j, Zipkin, Prometheus, Grafana, EFK 스택을 활용하여 안정적이고 관찰 가능한 시스템을 구축하는 실전 가이드입니다.

Prometheus 메트릭 수집 완벽 가이드

Spring Boot 애플리케이션의 메트릭을 Prometheus로 수집하고 모니터링하는 방법을 배웁니다. Actuator 설정부터 PromQL 쿼리까지 실무에 필요한 모든 내용을 다룹니다.