개인적으로 서비스를 개발하는 것도 중요하지만, 운영하는 것도 정말 중요하다고 생각한다. 왜냐하면 운영을 하게 됐을 때 어떤 상황들이 발생할지 모르기 때문에, 예기치 못한 오류나 성능 저하에 빠르게 대응할 수 있는 준비가 반드시 필요하다.
그렇기에 서비스 운영이 있어 로깅과 모니터링을 통해 항상 문제 없는지 체크하고, 장애를 감지할 수 있는 체계를 갖추어야 한다.
서비스에 문제가 없는지 확인하는 데에는 로그와 지표를 심어 감시하여 장애에 대응할 수 있다.
프로덕션 준비 기능이란?
운영 환경에서는 단순히 코드가 돌아간다는 사실만으로는 충분하지 않다. 서비스가 얼마나 안정적으로 동작하는지, 사용자가 체감하는 경험이 문제가 없는지까지 끊임없이 확인해야 한다. 이를 위해서는 눈에 보이지 않는 부분까지 세밀하게 점검할 수 있는 기준과 질문이 필요하다.
- 애플리케이션이나 DB가 잘 작동하고 있는지 확인할 수 있는가?
- 각 서비스들은 얼마만큼의 서버 자원을 소모하고 있는가?
- 만약 장애가 발생한다면 언제, 어디서 발생했는지 빠르게 확인 할 수 있는가?
- 요청 단위 추적(trace)으로 장애 원인을 끝까지 따라갈 수 있는가?
…
이런 질문들에 명확히 답할 수 없다면, 서비스는 언제든 예기치 못한 장애에 취약할 수밖에 없다.
결국 애플리케이션이 잘 살아있는지, 로그가 잘 찍히고 있는지, DB와의 연결이 잘 되어 있는지, 자원을 얼마나 사용하고 있는지 등 운영 환경에서 서비스할 때 필요한 기능들을 ‘프로덕션 준비 기능(Production-Ready Features)’이라 한다.
위와 같은 Spring Boot 애플리케이션을 운영함에 있어 프로덕션 준비 기능을 편하게 사용할 수 있는 것이 Spring Boot Actuator 라이브러리이다.
Spring Boot Actuator 알아보기
액추에이터란 애플리케이션 내부의 다양한 상태와 동작을 노출하여 운영자가 손쉽게 모니터링하고 관리할 수 있도록 도와주는 도구이다. 단순한 헬스체크 이상의 기능을 제공하며, 서비스가 프로덕션 환경에서 운영될 수 있도록 여러 정보들을 제공해주는 역할을 한다.
액추에이터가 기본적으로 상당히 많은 모니터링 요소를 제공해주고 다음과 같은 기능들을 통해 프로덕션 준비를 손쉽고 편리하게 진행할 수 있다.
- 애플리케이션 상태(Health) 및 정보(info) 조회
- JVM 및 시스템 자원(CPU, 메모리, GC) 등 모니터링 지표 확인 가능
- 비즈니스 지표(HTTP 요청, DB 커넥션)까지 모니터링 가능
- 내부 Bean, 매핑, 스레드 등 런타임 구조를 직접 노출해 심층적인 분석과 원인 진단을 지원
- 마이크로미터(Micrometer), 프로메테우스(Prometheus), 그라파나(Grafana) 등 모니터링 시스템과의 손쉬운 연동
참조 - https://eco-dev.tistory.com/entry/Spring-스프링-액추에이터actuator로-살펴보는-프로덕션-준비-기능
엔드포인트
액추에이터가 제공하는 다양한 기능들은 실제로는 각각 엔드포인트(Endpoint)라는 단위로 구성되어 있다.
엔드포인트는 애플리케이션 내부의 특정 정보를 외부에 노출해주는 창구 역할을 하며, 어떤 엔드포인트를 활성화하고 웹으로 열어둘지는 개발자가 직접 선택할 수 있다.
따라서 운영 환경에서는 단순히 헬스 체크만 노출하거나, 특정 상황에서만 필요한 엔드포인트를 제한적으로 허용하는 방식으로 활용한다.
다음은 엔드포인트들의 기본 활성화 상태와 웹 노출 Default 값에 대해 정리된 표를 살펴보고자 한다.
Enabled by default (기본 활성화): 엔드포인트 기능의 Bean이 기본적으로 Spring 컨테이너에 등록
Web Exposed (웹 노출): 실제로
/actuator/{id}URL을 통해 접근할 수 있는지 가능 여부ex)*http://test.com/actuator/health*- 어플리케이션 상태 체크
| ID | 설명 (Description) | 기본 활성화 | Web 기본 노출 |
|---|---|---|---|
| auditevents | 애플리케이션의 감사 이벤트 정보를 노출 | Yes | No |
| beans | 애플리케이션의 모든 Spring Bean 목록을 표시 | Yes | No |
| caches | 사용 가능한 캐시를 노출 | Yes | No |
| conditions | 자동 구성에 대한 조건 평가 보고서를 보여줌 | Yes | No |
| configprops | @ConfigurationProperties의 목록을 표시 |
Yes | No |
| env | 환경설정 정보 조회(application.properties, application.yml) |
Yes | No |
| flyway | 적용된 Flyway 데이터베이스 마이그레이션을 보여줌 | Yes | No |
| health | 애플리케이션 상태 정보를 보여줌 | Yes | Yes |
| heapdump | 힙 덤프 파일을 반환 | N/A | No |
| httptrace | 최근 HTTP request/response 표시 (기본값: 마지막 100개) | Yes | No |
| info | 애플리케이션 기본 정보를 표시 | Yes | Yes |
| integrationgraph | Spring Integration 그래프를 보여줌 | Yes | No |
| jolokia | JMX bean을 HTTP를 통해 노출 (Jolokia가 클래스패스에 있을 때). | N/A | No |
| logfile | logging.file.name 또는 logging.file.path가 설정된 경우 로그 파일 내용을 반환 |
N/A | No |
| loggers | 애플리케이션의 로거 구성을 보고 수정 | Yes | No |
| liquibase | 적용된 Liquibase 데이터베이스 마이그레이션을 보여줌 | Yes | No |
| metrics | 현재 애플리케이션의 '메트릭' 정보를 보여줌 | Yes | No |
| mappings | 모든 @RequestMapping 경로 목록을 표시 |
Yes | No |
| prometheus | Prometheus 서버가 스크랩할 수 있는 형식으로 메트릭을 노출 | N/A | No |
| scheduledtasks | 애플리케이션의 스케줄된 작업을 표시 | Yes | No |
| sessions | Spring Session에서 사용자 세션을 검색하고 삭제 가능 | Yes | No |
| shutdown | 애플리케이션을 정상적으로 종료 가능 | No | No |
| threaddump | 스레드 덤프 조회 | Yes | No |
보시는 바와 같이 URL을 통해 접근하는 경우 health와 info만 기본적으로 허용되어 있다.
그래서 단순 health check만 필요한 상황이면 의존성만 추가하더라도 바로 사용 가능하다.
만약 다른 엔드포인트(예: beans, env 등)를 웹으로 노출하고 싶다면 application.properties 또는 application.yml 파일에 다음과 같이 management.endpoints.web.exposure.include 설정을 추가해야 한다.
Actuator 사용해보기
액추에이터가 제공하는 기능들을 사용하기 위해선 의존성을 추가해줘야 한다.
implementation 'org.springframework.boot:spring-boot-starter-actuator'
액추에이터가 제대로 동작하는지 보기 위해서는 {서버 주소}/actuator 로 접속하면 확인 가능하다.

위와 같은 기능들이 달려 있는 것을 확인할 수 있다.
의존성만 달아두면 별다른 설정을 하지 않더라도, 기본적인 헬스체크는 바로 사용가능하다.

/actuator/health에 접근하면 단순히 "정상/비정상" 여부만 반환하는 것이 아니라,
애플리케이션의 현재 상태를 추상화한 status 값을 함께 응답한다.
이 값은 로드밸런서나 모니터링 시스템이 서버를 관리할 때 중요한 기준이 되며, 운영자가 서버 상태를 빠르게 이해하는 데에도 큰 도움을 주는데 이 상태는 총 4가지가 존재한다.
UP: 시스템이 작동중.접근 가능DOWN: 시스템이 작동중이지 않거나, 접근 불가능UNKNWON: 시스템의 상태가 분명하지 않음OUT_OF_SERVICE: 시스템에 접근은 가능하지만, 현재 사용은 불가능
이 4가지 상태를 기반으로 로드밸런서는 정상적인 서버에만 트래픽을 분산하고, 운영자는 현재 애플리케이션이 서비스 가능한 상태인지 빠르게 판별할 수 있다.
즉, 단순히 서버가 살아있다는 신호를 주는 것이 아니라, 의존 리소스와 환경까지 종합적으로 평가한 결과를 전달해주는 것이다.
이 덕분에 서비스는 고가용성을 보장할 수 있고, 장애 발생 시에도 원인을 신속히 좁혀나갈 수 있다.
다음은 액츄에이터 엔드포인트 추가에 대해서 살펴보고자 한다.
엔드포인트 노출 범위나 상세 정보 출력 여부는 application.yml 또는 application.properties에서 손쉽게 설정할 수 있다.
사용하고 싶은 엔드포인트들의 id 값을 management.endpoints.web.exposure.include에 담아주기만 하면 설정 완료이다. 그 밖에도 헬스 체크 결과에 세부 정보를 포함할지 여부도 management.endpoint.health.show-details 옵션으로 제어할 수 있다.
# application.properties 예시
management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=when_authorized
# application.yml 예시
management:
endpoints:
web:
exposure:
include: health,info,metrics # 노출할 엔드포인트 지정
endpoint:
health:
show-details: when_authorized # health 상세 정보 접근 제어
위와 같이 설정하면 운영 환경에서는 꼭 필요한 엔드포인트만 노출하고,
민감한 세부 정보는 인증된 사용자에게만 확인할 수 있도록 제어할 수 있다.
추가로, 특정 엔드포인트 자체를 아예 활성화하거나 비활성화할 수도 있다.
exposure.include는 “웹으로 노출할지 말지”를 결정하는 반면,
management.endpoint.{id}.enabled는 엔드포인트 기능 자체를 등록할지 여부를 제어한다.
# application.yml 예시
management:
endpoint:
shutdown:
enabled: true # shutdown 엔드포인트 활성화
beans:
enabled: false # beans 엔드포인트 비활성화
loggers:
enabled: true # loggers 엔드포인트 활성화
쉽게 말하자면
exposure/include- 보여줄지 말지enabled- 기능을 사용할지 말지
이렇게 구분할 수 있을 것 같다.
방식 비교
| 구분 | exposure.include |
endpoint.{id}.enabled |
|---|---|---|
| 목적 | 웹으로 노출할 엔드포인트 선택 | 엔드포인트 자체를 켜거나 끔 |
| 범위 | 노출 제어 | |
| (내부 Bean은 살아있음) | 등록 자체를 차단 | |
| (엔드포인트가 없음) | ||
| 예시 | beans는 동작하지만 URL로 접근 불가 |
beans 엔드포인트 자체가 아예 사라짐 |
| 보안 수준 | URL 차원에서 접근 막음 | 기능 자체 제거 |
커스텀 엔드포인트
액추에이터에는 위와 같이 이미 다양한 엔드포인트들을 제공해주고 있지만, 실제 서비스를 운영하거나 실무환경에서는 프로젝트의 특성에 따라 직접 정의한 엔드포인트가 필요한 경우도 있다.
예를 들어
그냥 @RestController로 만들면 되는 거 아닌가?”라는 의문이 들 수 있다.
하지만 직접 만든 컨트롤러는 액추에이터 환경과 연동이 안된다..
예를 들어 Prometheus, Grafana 같은 모니터링 시스템은 액추에이터의 엔드포인트 인터페이스를 통해 데이터를 수집합니다. 따라서 커스텀 모니터링 지표를 제공하려면 액추에이터의 엔드포인트 구조를 따르는 것이 훨씬 유리하다.
커스텀 엔드포인트 만들기
Spring Boot Actuator는 이미 다양한 범용 엔드포인트를 제공하고 있다. beans, env, caches, metrics 등 "이런 게 기본 제공되면 좋겠다" 싶은 기능들이 대부분 들어있다.
하지만 실무에서는 사내 정책이나 프로젝트 특성에 따라 직접 정의한 전용 엔드포인트가 필요한 경우도 있다. 예를 들어 서비스 전용(특정 로직) 상태 점검 API, 개별 라이브러리 버전 정보, 운영용 데이터 뽑아낼 때 등이 이에 해당한다.
이럴 때는 액추에이터가 제공하는 확장 기능을 활용해 Custom Endpoint를 만들 수 있다.
간단한 예제
아래 예시는 myLibraryInfo라는 이름의 엔드포인트를 만들어, 애플리케이션에서 사용하는 라이브러리 이름과 버전 정보를 반환하도록 구현한 것이다.
@Endpoint(id = "myLibraryInfo") // 엔드포인트 id 지정
public class MyLibraryInfoEndpoint {
@ReadOperation // GET 요청(Read)에 매핑
public List<LibraryInfo> getLibraryInfos() {
LibraryInfo lib1 = new LibraryInfo("logback", "1.0.0");
LibraryInfo lib2 = new LibraryInfo("jackson", "2.0.0");
return Arrays.asList(lib1, lib2);
}
}
@Data
@AllArgsConstructor
class LibraryInfo {
private String name;
private String version;
}
그리고 이 클래스를 빈으로 등록해야 한다.
@Configuration
public class MyLibraryInfoConfig {
@Bean
public MyLibraryInfoEndpoint myLibraryInfoEndpoint() {
return new MyLibraryInfoEndpoint();
}
}
Spring Boot를 재실행하면 /actuator/myLibraryInfo라는 URL이 추가되고, 우리가 정의한 데이터가 JSON 형태로 노출된다.
지원되는 Operation 종류
엔드포인트는 단순 조회뿐만 아니라, 쓰기나 삭제 같은 명령형 동작도 지원한다. 이를 위해 액추에이터는 다음음과 같은 어노테이션을 제공한다.
| 어노테이션 | 설명 | 매핑되는 HTTP Method |
|---|---|---|
@ReadOperation |
데이터 조회 | GET |
@WriteOperation |
데이터 생성/변경 | POST |
@DeleteOperation |
데이터 삭제 | DELETE |
예를 들어, 특정 로거 레벨을 변경하는 동작을 엔드포인트로 노출하고 싶다면 @WriteOperation을 붙여 구현할 수 있다.
파라미터 처리
- Query String: 단순 GET 파라미터는 메서드 인자에 그대로 선언하면 자동 매핑된다.
- Body:
@WriteOperation으로 매핑하면 POST Body도 받을 수 있다. (단, DTO 객체 바인딩은 지원되지 않고, 개별 필드 단위만 허용된다.) - Path Parameter:
@Selector어노테이션을 이용하면 path 기반 파라미터도 받을 수 있다.
@ReadOperation
public String getPathInfo(@Selector String path) {
return "입력받은 값: " + path;
}
/actuator/myLibraryInfo/test로 호출하면 입력받은 값: test라는 결과를 반환한다.
왜 직접 REST Controller가 아닌 Actuator를 써야 할까?
"그냥 @RestController로 만들면 되는 거 아닌가?"라는 의문이 들 수 있다.
하지만 직접 만든 컨트롤러는 액추에이터의 환경과 연동되지 않는다.
예를 들어 Prometheus, Grafana 같은 모니터링 시스템은 액추에이터의 엔드포인트 인터페이스를 통해 데이터를 수집한다. 따라서 커스텀 모니터링 지표를 제공하려면 액추에이터의 엔드포인트 구조를 따르는 것이 훨씬 유리하다.
Actuator 안전하게 사용하기
외부 노출 방지
액추에이터가 애플리케이션의 많은 정보들을 제공하는만큼 내부 정보가 외부에 과도하게 노출되며 보안이 취약해지는거 같다. 그래서 누구나 액추에이터를 접근할 수 있다면 문제가 발생할 수 있기에 반드시 접근 제어와 보안 설정이 필요하다고 생각한다.
- 내부망으로만 접근 제한
액추에이터 엔드포인트는 외부에 직접 노출하지 말고, 내부망에서만 접근 가능하도록 설정하는 것이 좋다.
AWS 환경에서는 내부 ALB나 VPC 내부에서만 접근하도록 보안 그룹을 설정하거나, 프라이빗 서브넷에 위치시키는 방법을 사용할 수 있다.
- 보안 정책 설정
민감한 정보에 대한 접근을 제한하기 위해 특정 헤더나 IP 기반의 접근 제어를 구현할 수 있다.
또한 상세 정보 노출은 인증된 사용자에게만 제한하는 것이 좋다.
| 옵션 | 설명 | 보안 수준 | 권장 사용 환경 |
|---|---|---|---|
always |
항상 상세 정보를 노출 | 낮음 | 개발 및 테스트 환경 |
| (디버깅 목적) | |||
never |
상세 정보 없이 전반적인 상태만 노출 | 보통 | 모든 환경 (기본값) |
when_authorized |
권한 있는 사용자에게만 상세 정보 노출 | 높음 | |
| (스프링 시큐리티 적용 시) | 프로덕션 환경 |
management:
endpoint:
health:
show-details: when_authorized # 인증된 사용자에게만 상세 정보 제공
여기서 인증이란 스프링 시큐리티가 인가 과정에서 검증하는, 사용자가 가지고 있는 특정 역할을 의미한다 JWT를 사용하든, 세션을 사용하든 관계없이 스프링 시큐리티가 인가 규칙에 따라 그 사용자가 상세 정보를 볼 수 있는 권한이 있는지 판단하기에 별도의 시큐리티 설정도 필요하다.
- 스프링 시큐리티를 이용한 접근제어
스프링 시큐리티를 통해 액추에이터 엔드포인트에 대한 인증 및 권한 제어가 가능하다.
@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest().hasRole("ACTUATOR_ADMIN")
.and()
.httpBasic();
}
}
이렇게 설정하면 'ACTUATOR_ADMIN' 역할을 가진 사용자만 액추에이터에 접근할 수 있다.
또는 security path에 대해 설정을 해두었다면, 특정 권한을 가진 유저들만 접근 할 수 있기에 훨씬 안전하다.
security:
path:
# 모든 요청에 대해 인증을 요구하지 않는 경로 설정
permit-all:
ALL:
- "/swagger-ui/**"
- "/v3/api-docs/**"
- "/api/v1/user/univ/campus"
# 인증된 사용자만 접근 가능한 경로 설정
authenticated:
ALL:
- "/api/v1/user/bookmark"
- "/api/v1/user/recentReview"
- "/api/v1/auth/tokenRefresh"
- "/api/v1/auth/logout"
- "/actuator/info" # 이렇게 인증된 사용자만 접근 가능하도록
- 필요한 엔드포인트만 활성화
모든 엔드포인트를 노출하지 말고, 꼭 필요한 것만 선택적으로 활성화하는 것이 안전하다.
management:
endpoints:
web:
exposure:
include: health,info,prometheus # 필요한 엔드포인트만 활성화
- 엔드포인트 변경와 포트 변경 (비추)
액추에이터는 yml 설정을 통해 다른 포트로 변경을 하거나 기본 '/actuator' 경로를 변경할 수도 있다.
management:
server:
port: 8081
management:
endpoints:
web:
base-path: /management # 기본 경로 변경
단, 이는 단순히 엔드포인트만 변경한 것이기에 nmap과 포트 스캔, 서비스 탐지로 충분히 다 뚫릴수도..
실질적인 보안 수단으로는 사용 불가능하기에 다른 보안 조치와 함께 사용해야 한다.
헬스체크 주의사항
액추에이터의 /health는 로드밸런서와 오토스케일러가 서버 상태를 판단하는 기준으로 자주 사용된다.
하지만 동작 원리를 충분히 이해하지 못한 채 그대로 노출하거나 의존하면, 의도치 않게 장애가 확산되거나 원인 분석이 지연될 수 있다.
헬스체크 오용에 따른 혼란
최근 액추에이터를 설정했었을때, 도메인 관련 이슈가 있어서 LB에서의 설정이 제대로 안되어 서버는 살아있지만 헬스체크로는 DOWN으로 나온 상황이 있었다.
이처럼 문제가 발생했었을 때 LB 설정 문제인지, 서버 자체인지, DB인지 파악하는 데 시간이 오래 걸려 복구가 지연될 수 있다.
의존 리소스 문제 전파
HealthIndicator가 여러 개 등록되면, 하나라도 DOWN일 경우 전체 상태를 DOWN으로 반환한다.예를 들어 서비스 DB는 정상인데 로그 DB만 일시적으로 끊겨도 서버가 503을 반환하며 로드밸런서에서 제외된다.
이는 실제 서비스는 가능한데, 헬스체크 때문에 오히려 장애가 발생하는 상황이다..
외부 서비스 장애 영향
Elasticsearch, Redis, 외부 API처럼 핵심 서비스가 아닌 의존 리소스가 죽었을 때도 액추에이터는 DOWN으로 판정할 수 있다.
API 서버 자체는 살아있음에도 LB가 트래픽을 끊어버려 불필요한 가용성 저하가 발생할 수 있다.
실제로 ES 장애 → API 서버 DOWN 판정 → 트래픽 차단 → 트러블슈팅 지연으로 이어진 경우가 있다.
(feat. 밑 첨부된 토스 블로그 글)
보안 이슈
management.endpoint.health.show-details: always옵션을 켜두면 DB 연결 정보나 내부 의존성 상태가 노출될 수 있다.로컬 테스트나 제한된 환경에서만 활용하고, 운영에서는
when_authorized수준으로 제한하는 것이 안전하다.
이를 해결 하기 위해 아래와 같은 대응들이 필요하다고 생각한다.
- 꼭 필요한
HealthIndicator만 활성화
(management.health.db.enabled=false등으로 불필요한 지표 비활성화) - 핵심 서비스 상태만 판단하는 커스텀 헬스체크 API 구현 고려하기
- 헬스체크의 DOWN 기준을 팀 내에서 명확히 합의하고 문서화하기
- 상세 상태 노출은 위에 언급했듯 접근 제어와 보안 설정을 통해 내부망이나 인증된 사용자에게만 제한
헬스체크는 단순히 서버가 살아있는지 확인하는 수단이 아니라,
운영 장애의 전파 범위와 대응 속도를 결정하는 중요한 요소니까
무엇을 UP/DOWN으로 볼지를 반드시 팀 차원에서 합의하고,불필요한 의존성이 전체 서비스 가용성을 해치지 않도록 설계하는 것이 중요할 것 같다.
Spring Boot Actuator의 헬스체크 살펴보기
정리
Actuator는 단순한 개발 편의 기능을 넘어서, 운영 안정성과 서비스 관리의 편리함을 가져다주는 라이브러리다. 사실 단순한 헬스 체크만 간단히 사용할꺼라면 이만한 게 없는 것 같기도..
덕분에 애플리케이션의 상태를 빠르게 점검하고, 장애 원인을 좁혀나가며, 모니터링 시스템과 연동해 더 체계적인 운영을 할 수 있다.
다만, 민감한 정보까지 노출될 수 있다는 점에서 사용에는 반드시 신중해야 한다.
노출할 엔드포인트와 차단할 엔드포인트를 명확히 구분하고, 접근 권한을 제한하며, 헬스체크 기준을 팀 차원에서 합의하는 것이 필요하다.
좀 더 자세히 액추에이터에 대해 알고싶다면 밑 블로그의 액추에이터 시리즈 또는 참조 사이트들을 참조하면 더욱 좋다.
spring boot 3.x + actuator 파헤치기. 1. 프로젝트 생성
다음 글은 매트릭 수집을 위한 Micrometer에 대해 알아가보고자 한다.
스프링 2.x.x 버전부터는 액추에이터에 Micrometer가 내장되어 metrics을 표시할 때 도움을 주긴 하지만, 기본적인 기능들만 제공하기에 특정 비즈니스 로직을 추적하기 위한 커스텀 메트릭 등을 사용하고 싶을때는 따로 의존성을 추가해주는 편이 좋다고 한다.
그래서 Micrometer를 이용하여 어떻게 매트릭을 수집하고 이를 액츄에이터로 어떻게 표현해줄 지에 대해 다뤄볼 생각이다.
참조
[Spring] 스프링부트 엑츄에이터(Actuator)란?
'Framework & Library > Spring Boot' 카테고리의 다른 글
| [Spring Boot] Haversine 공식을 이용한 원하는 범위의 마커 출력하기 (0) | 2025.10.12 |
|---|