본문 바로가기

Programming/Spring Boot

Spring Boot @ConfigurationProperties 검증: 잘못된 운영 설정을 시작 단계에서 차단하기

추천캐릭터 2026. 7. 31. 13:00
728x90

Spring Boot @ConfigurationProperties 검증: 잘못된 운영 설정을 시작 단계에서 차단하기

결제 서버 주소가 비어 있거나 재시도 횟수에 음수가 들어갔는데도 애플리케이션이 정상적으로 시작된다면, 오류는 실제 요청이 들어온 뒤에야 드러납니다. 배포는 성공한 것처럼 보이지만 첫 결제에서 장애가 발생하는 구조입니다.

Spring Boot의 @ConfigurationProperties와 Jakarta Validation을 함께 사용하면 잘못된 설정을 애플리케이션 시작 단계에서 차단할 수 있습니다. 운영자가 수정해야 할 값과 실패 원인도 시작 로그에서 바로 확인할 수 있습니다.

@ConfigurationProperties가 필요한 이유

Spring Boot 설정값은 YAML, properties 파일, 환경 변수, 명령행 인수 등 여러 위치에서 들어옵니다. @Value로 하나씩 주입할 수도 있지만, 관련 설정이 늘어나면 흩어진 문자열 키를 관리하기 어렵습니다.

@Value("${payment.base-url}")
private String baseUrl;

@Value("${payment.timeout}")
private Duration timeout;

@ConfigurationProperties는 같은 접두사의 값을 하나의 타입 안전한 객체로 묶습니다.

기준 @Value @ConfigurationProperties
적합한 용도 단일 값, 간단한 표현식 서비스별 구조화된 설정
타입 변환 지원 지원
여러 값 탐색 필드마다 키 확인 접두사 기준으로 한곳에 모임
유효성 검증 별도 구성 필요 @Validated와 자연스럽게 결합
불변 객체 작성이 번거로움 record와 잘 맞음

검증 의존성 추가

Bean Validation 구현을 사용할 수 있도록 다음 의존성을 추가합니다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

설정 파일 작성

결제 연동에 필요한 값을 payment 아래에 모아 보겠습니다.

payment:
  base-url: https://sandbox-payment.example.com
  timeout: 3s
  max-retries: 2

Spring Boot의 완화된 바인딩 덕분에 base-url은 Java의 baseUrl, max-retriesmaxRetries에 연결됩니다. 3sDuration으로 변환됩니다.

record로 바인딩하고 검증하기

package com.example.payment;

import java.time.Duration;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@Validated
@ConfigurationProperties(
        prefix = "payment",
        ignoreUnknownFields = false
)
public record PaymentProperties(
        @NotBlank String baseUrl,
        @NotNull Duration timeout,
        @NotNull @Min(0) @Max(5) Integer maxRetries
) {
}

@Validated가 있어야 바인딩된 객체에 검증이 실행됩니다. @NotBlank는 URL 문자열의 null, 빈 문자열, 공백만 있는 값을 막습니다. @Min@Max는 재시도 횟수의 운영 범위를 제한합니다.

필수 숫자에는 원시 타입 int보다 Integer가 안전합니다. 값이 누락되면 int는 기본값 0이 되어 @Min(0) 검증을 통과할 수 있지만, Integer@NotNull을 적용하면 누락 자체를 구분할 수 있습니다.

ignoreUnknownFields = false를 사용하면 max-retires처럼 철자가 틀린 키도 시작 실패로 드러납니다.

설정 클래스 등록

애플리케이션 시작 클래스에 스캔을 활성화합니다.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

특정 클래스만 등록하려면 @EnableConfigurationProperties(PaymentProperties.class)를 사용할 수도 있습니다.

이제 다음처럼 잘못된 설정으로 실행해 봅니다.

payment:
  base-url: " "
  timeout: 3s
  max-retries: 10

애플리케이션 컨텍스트를 만드는 과정에서 검증이 실패하므로 서버가 트래픽을 받기 전에 문제를 발견할 수 있습니다. 서비스에는 PaymentProperties를 생성자로 주입합니다. 문자열 키가 서비스 코드에 퍼지지 않아 IDE 자동 완성과 타입 검사를 활용할 수 있습니다.

운영에서는 PAYMENT_BASE_URL, PAYMENT_TIMEOUT, PAYMENT_MAX_RETRIES 같은 환경 변수로 덮어쓸 수 있습니다. API 키와 비밀번호는 YAML에 커밋하지 말고 Secret Manager나 배포 시스템의 비밀 변수로 주입합니다.

중첩 설정 검증

인증 정보를 중첩 객체로 분리한다면 @Valid를 붙여 내부 제약까지 연쇄 검증해야 합니다.

public record PaymentProperties(
        @NotBlank String baseUrl,
        @NotNull @Valid Auth auth
) {
    public record Auth(
            @NotBlank String clientId,
            @NotBlank String clientSecret
    ) {
    }
}

중첩 필드의 @Valid를 빠뜨리면 내부 제약이 실행되지 않을 수 있습니다.

자주 하는 실수

  • 스캔 또는 클래스 등록을 하지 않습니다.
  • @Validated나 Validation 의존성을 빠뜨립니다.
  • 필수 숫자를 원시 타입으로 선언해 누락이 기본값으로 숨습니다.
  • 중첩 객체에 @Valid를 붙이지 않습니다.
  • 운영 비밀값을 application-prod.yml에 직접 저장합니다.
  • 타임아웃에 단위 없는 숫자를 사용해 사람이 기대한 단위와 실제 단위가 달라집니다.

핵심 요약

  • 관련 설정은 @ConfigurationProperties로 한 객체에 묶습니다.
  • @Validated와 Jakarta Validation 제약으로 시작 시점에 값을 검사합니다.
  • 필수 숫자는 래퍼 타입과 @NotNull을 함께 사용합니다.
  • ignoreUnknownFields = false로 설정 키 오타를 빠르게 발견할 수 있습니다.
  • 중첩 속성에는 @Valid, 운영 비밀에는 별도 비밀 저장소를 사용합니다.

잘못된 설정을 요청 처리 코드에서 발견하는 것은 너무 늦습니다. 설정도 애플리케이션 입력값이라는 관점에서 타입과 범위를 정의하면, “배포는 됐지만 첫 요청부터 실패하는” 장애를 시작 단계에서 차단할 수 있습니다.

함께 읽으면 좋은 글:

최종 확인: 2026-07-30

출처: Spring Boot 4.1 Externalized Configuration

728x90