본문 바로가기

Actuator 엔드포인트

Actuator 엔드포인트의 활성화와 노출을 구분하고 관리 포트·사용자 엔드포인트를 안전하게 설정합니다.

개발 환경에서는 브라우저에 페이지가 보이면 성공처럼 느껴집니다.

운영 환경에서는 애플리케이션이 살아 있는지, 데이터베이스와 연결되는지, 요청이 얼마나 느린지까지 확인해야 합니다.

이렇게 실행 중인 시스템의 상태를 바깥에서 이해할 수 있게 만드는 것을 관측 가능성(observability)이라고 합니다.

Spring Boot Actuator는 상태·정보·메트릭 같은 운영용 엔드포인트를 제공합니다.

엔드포인트는 운영 정보를 읽거나 일부 설정을 바꾸는 접근 지점이고, Micrometer는 카운터·타이머 같은 메트릭을 여러 관측 시스템에 전달하는 공통 계층입니다.

Actuator 스타터를 추가했다고 모든 정보가 자동으로 공개되지는 않습니다.

엔드포인트가 생성되는 것과 HTTP나 JMX에 노출되는 것은 별도 결정입니다.

ACTUATOR · FOUR INDEPENDENT CONDITIONS

HTTP 운영 점검은 네 조건을 모두 만족해야 한다

Boot 4의 작업 access, 웹 노출, 실제 관리 경로, 요청 권한은 별도 조건입니다. 네 조건이 충족돼도 작업 자체가 실패하거나 health가 503을 반환할 수 있습니다.

웹 운영 호출을 위한 네 조건의 논리 관계작업 접근, 웹 노출, 실제 관리 경로, 요청 권한은 별도 조건이며 모두 충족해야 HTTP 작업 호출 후보가 된다. 화살표는 논리 입력 관계이고 요청의 처리 순서가 아니다. 작업 자체의 실패와 Health 503은 별도로 판단한다. 같은 6노드5관계를 모바일에서도 유지한다.작업 access웹 노출관리 경로요청 권한네 조건 모두 충족작업 호출결과는 별도
웹 운영 호출을 위한 네 조건의 논리 관계작업 접근, 웹 노출, 실제 관리 경로, 요청 권한은 별도 조건이며 모두 충족해야 HTTP 작업 호출 후보가 된다. 화살표는 논리 입력 관계이고 요청의 처리 순서가 아니다. 작업 자체의 실패와 Health 503은 별도로 판단한다. 같은 6노드5관계를 모바일에서도 유지한다.작업접근웹노출관리경로요청권한네 조건모두 충족작업 호출결과는 별도

1 · 작업 access

none은 접근 불가, read-only는 읽기 작업, unrestricted는 전체 작업을 허용합니다. 엔드포인트별 설정과 전역 max-permitted 상한을 함께 확인합니다.

2 · 웹 노출

HTTP의 include/exclude 허용 목록을 확인합니다. JMX 노출은 별도 설정입니다. 웹에 이름을 적어도 access가 허용하지 않으면 나타나지 않습니다.

3 · 실제 관리 경로

관리 주소·포트·base path·TLS·프록시 라우팅을 실제 배포에서 확인합니다. 별도 포트도 프로세스와 JVM을 공유합니다.

4 · 요청 주체와 작업

익명 점검·수집기·운영자의 읽기/변경 권한을 구분합니다. 탐색 링크가 보호되거나 숨겨졌다면 알려진 엔드포인트를 직접 점검합니다.

이 번호는 진단 항목이며 요청 처리 순서를 뜻하지 않습니다. 404는 미생성·미노출·경로/포트 등을 다시 좁힙니다. 사용자 엔드포인트는 Clock을 주입하고 집계 DTO만 반환합니다.


Actuator 인프라

spring-boot-starter-actuator를 애플리케이션 의존성에 추가하면 상태, 정보, 메트릭 같은 엔드포인트 인프라와 Micrometer 통합이 자동 구성됩니다.

엔드포인트마다 필요한 클래스와 빈 조건이 다르므로 의존성 그래프, 조건 보고서, 엔드포인트 목록을 함께 확인합니다.

src/main/java/board/BoardApplication.java
package board;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BoardApplication {
    public static void main(String[] args) {
        SpringApplication.run(BoardApplication.class, args);
    }
}
build.gradle 의존성
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

애플리케이션이 정상 시작하면 /actuator 탐색 문서에서 현재 웹에 노출된 링크를 볼 수 있습니다.

엔드포인트 빈이 없거나 보안이 거부한 결과와 경로가 틀린 404를 구별합니다.


활성화·노출 구분

이 교재의 Boot 4 계열에서는 management.endpoint.<id>.access의 none, read-only, unrestricted로 작업 접근을 제어합니다. 접근 불가 엔드포인트는 컨텍스트에서 제거되고, 전역 max-permitted 상한도 적용됩니다.

노출 설정은 만들어진 엔드포인트를 웹 또는 JMX에 연결할지 결정합니다.

metrics가 활성화되어 있어도 웹 노출 허용 목록에 없으면 HTTP로 읽을 수 없고, 웹 노출 허용 목록에 이름을 적어도 엔드포인트가 비활성화되어 있으면 나타나지 않습니다.

기본 노출은 보수적입니다.

*로 전부 열고 네트워크 방화벽에만 기대지 말고 실제 운영자가 필요한 ID 허용 목록을 작성합니다.

YAML에서 별표는 문법 의미가 있으므로 따옴표로 감싸고, 포함·제외 설정이 어떤 결과를 만드는지 시작 테스트에서 확인합니다.

Actuator 공식 노출 규칙에 따라 exclude는 include보다 우선합니다. 예를 들어 include에 health·info·metrics가 있고 exclude에 metrics가 있으면 metrics는 웹 노출 대상에서 빠집니다. endpoint access가 읽기를 허용하는지, 해당 빈이 만들어지는지는 이 노출 목록과 별도로 확인합니다.

src/main/resources/application.yml
management:
  endpoints:
    web:
      exposure:
        include: "health,info,metrics"
  endpoint:
    health:
      show-details: never

엔드포인트 ID·경로

health, info, metrics, loggers, configprops, env, beans, mappings, threaddump, heapdump, httpexchanges, prometheus, shutdown은 민감도와 비용이 다릅니다.

엔드포인트 이름만 보고 공개하지 말고 응답 내용, 변경 가능성, 페이로드 크기, 호출 빈도를 목록에 기록합니다.

기반 경로를 바꾸면 보안이 생기는 것이 아닙니다.

/internal/manage처럼 추측하기 어려운 경로도 인증과 네트워크 정책 없이는 공개 엔드포인트입니다.

리버스 프록시가 접두사를 덧붙이면 준비 상태 점검과 대시보드 대상도 함께 바꿉니다.

src/main/java/board/operations/EndpointPolicy.java
package board.operations;

import java.util.Set;

public record EndpointPolicy(
        Set<String> enabled,
        Set<String> webExposed,
        Set<String> mutating
) {
    public EndpointPolicy {
        enabled = Set.copyOf(enabled);
        webExposed = Set.copyOf(webExposed);
        mutating = Set.copyOf(mutating);
        if (!enabled.containsAll(webExposed)) {
            throw new IllegalArgumentException("exposed endpoint must be enabled");
        }
        if (!webExposed.containsAll(mutating)) {
            throw new IllegalArgumentException("mutating endpoint must be inventoried");
        }
    }

    public Set<String> internalOnly() {
        var result = new java.util.HashSet<>(enabled);
        result.removeAll(webExposed);
        return Set.copyOf(result);
    }
}

정책 파일과 실제 탐색 결과를 배포 후 기본 동작 검증에서 비교하면 속성 오타나 버전 업그레이드로 엔드포인트가 조용히 늘어난 상황을 잡을 수 있습니다.

예시 enabled가 health·info·metrics·postqueue이고 webExposed가 health·info·metrics이면 internalOnly에는 postqueue만 남습니다. mutating은 빈 집합이라고 정합니다. webExposed에 enabled에 없는 env를 넣으면 생성자에서 먼저 거부합니다. 이 집합 검사는 애플리케이션이 정의한 목록의 정합성 검사이며 실제 Boot 속성과 보안 필터를 적용하는 코드가 아닙니다.


관리 서버 분리

management.server.port를 애플리케이션 포트와 다르게 두면 별도 커넥터에서 Actuator를 제공합니다.

비공개 인터페이스에 바인딩하고 인그레스·서비스·방화벽을 분리하면 공개 요청 경로에 운영 엔드포인트가 섞이지 않습니다.

그러나 프로세스와 JVM은 공유하므로 애플리케이션이 stop-the-world 일시 중지에 빠지면 관리 포트도 응답하지 못할 수 있습니다.

별도 포트의 TLS, 접근 로그, 단계적 종료, 포트 충돌도 운영 대상입니다.

management.server.address=127.0.0.1은 사이드카 수집에는 맞지만 원격 점검에는 닿지 않을 수 있습니다.

배포 구성에 따라 주소를 결정하고 로컬 개발 기본값을 운영 환경에 그대로 쓰지 않습니다.


사용자 정의 엔드포인트

사용자 정의 엔드포인트는 HTTP와 JMX에서 같은 작업 모델을 제공할 때 유용합니다.

주문 생성 같은 업무 명령을 Actuator에 넣지 않고 대기열 상태나 캐시 재생성처럼 호출 빈도가 낮은 운영 제어 기능을 둡니다.

선택기와 파라미터를 자유 텍스트로 받지 말고 값 범위를 제한합니다.

src/main/java/board/operations/PostQueueEndpoint.java
package board.operations;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;

@Component
@Endpoint(id = "postqueue")
public class PostQueueEndpoint {
    private final AtomicInteger pending = new AtomicInteger();

    @ReadOperation
    public Map<String, Object> state() {
        return Map.of(
                "pending", pending.get(),
                "observedAt", Instant.now().toString());
    }

    public void accepted() {
        pending.incrementAndGet();
    }

    public void completed() {
        pending.updateAndGet(value -> Math.max(0, value - 1));
    }
}

동적 타임스탬프는 캐시와 테스트를 어렵게 할 수 있어 실제 코드에서는 시계를 주입합니다.

대기 값에 회원 ID나 대기열 항목 내용을 넣지 않고 집계만 제공합니다.

독립 확인: 새 PostQueueEndpoint에 accepted를 두 번, completed를 세 번 호출합니다. 마지막 pending과 중간 값은 무엇인가요? 이 값만으로 어떤 항목이 완료됐는지 또는 영속 대기열이 비었는지 알 수 있나요?

풀이 확인

시작 0에서 1→2→1→0→0입니다. completed는 0 아래로 내려가지 않게 하지만 항목 ID나 실제 처리 이력을 보관하지 않습니다. 프로세스 메모리의 이 집계가 0이라는 사실과 영속 대기열이 비었다는 사실은 별도입니다. 운영 집계의 원본과 중복 완료 정책을 정해야 합니다.


가용성·인가 관찰

404는 엔드포인트 비활성화, 미노출, 경로 불일치 중 하나일 수 있고, 401은 인증 부재, 403은 권한 부족, 503은 상태가 서비스 사용 불가로 매핑된 결과일 수 있습니다.

자동화 도구는 이 상태를 하나의 “Actuator 실패”로 처리하지 않습니다. 다음은 서로 다른 노출·보안·관리 포트 설정의 진단 예시이며, 위 YAML 하나로 모두 재현한 결과는 아닙니다.

배포 smoke 결과
GET /actuator -> 200, links=[health,info,metrics]
GET /actuator/env -> 404, not exposed
GET /actuator/health -> 200, status=UP
GET /actuator/loggers -> 403, operator role required
GET application-port/actuator -> 404, management port separated

탐색 문서 자체를 숨기거나 보안으로 보호하는 경우 알려진 엔드포인트를 직접 점검합니다.

응답 본문뿐 아니라 콘텐츠 타입, 지연 시간 예산, 인증 경로를 확인합니다.


연습 문제

회원 게시판에 별도 관리 포트를 열고 상태·정보·메트릭만 웹에 노출하세요.

postqueue 읽기 엔드포인트를 추가하되 항목 원문은 공개하지 마세요.

원 조건에서는 웹 노출 허용 목록이 health·info·metrics 세 ID입니다. postqueue 빈을 추가해도 이 목록에 넣지 않으면 HTTP 노출이 생기지 않습니다. 이 연습은 읽기 엔드포인트의 생성과 웹 공개를 구분하는 과제입니다. 웹에서도 읽는 별도 변형을 시험하려면 허용 목록에 postqueue를 추가한다는 조건을 먼저 적으세요.

비활성화, 미노출, 미인증, 정상이라는 네 분기의 상태와 탐색 링크를 표로 검증하세요.

해설 보기

설정 파일만 검토하지 말고 실행된 서버의 탐색 결과를 정책 허용 목록과 비교합니다.

별도 포트가 공개 인그레스에 라우팅되지 않았는지도 확인합니다.

package board.operations;

public record EndpointObservation(int status, boolean linked, String meaning) {
    public static EndpointObservation classify(int status, boolean linked) {
        return switch (status) {
            case 200 -> new EndpointObservation(200, linked, "AVAILABLE");
            case 401 -> new EndpointObservation(401, linked, "AUTHENTICATION_REQUIRED");
            case 403 -> new EndpointObservation(403, linked, "AUTHORIZATION_DENIED");
            case 404 -> new EndpointObservation(404, linked, "DISABLED_OR_NOT_EXPOSED");
            default -> new EndpointObservation(status, linked, "INVESTIGATE");
        };
    }
}

404 원인은 시작 조건 보고서와 엔드포인트 목록으로 다시 좁힙니다.