본문 바로가기

더 나은 엔지니어가 되기 위해/지금은 안쓰는 자바

[스프링 부트 개념과 활용] 외부 설정

인프런에서 백기선님의 스프링부트 개념과 활용 강의를 듣고, 개인적으로 공부하며 핵심만 정리한 글입니다.

외부 설정 사용하기

Properties

resources 디렉토리 안에 있는 application.properties 파일에 키 = 값 형태로 일종의 설정 값들을 따로 구분하여 저장해놓을 수 있다.
이를 코드에서는 @Value("${key}") 로 가져올 수 있다.
예를 들면 다음과 같다.

// application.properties
heumsi.name = heumsi
// SampleRunner.java

@Value("${heumsi.name}")
private String name; // name 에 heumsi 가 바인딩 됨.
...

Environment

Environment 객체를 가져와 .getProperty("key") 메쏘드로 설정 값을 가져올 수 있다.

@Autowired
Environment environment;

private String name = environment.getProperty("heumsi.name"); 
// name 에 heumsi 가 바인딩 됨.
...

테스트에서의 외부 설정

테스트를 실행 하면, main -> test 순으로 프로젝트를 빌딩하고,
test 에 있는 파일들이 main 에 있는 파일들을 오버라이딩함.
즉, main 과 test 에 동일하게 application.properties 가 있다면, test 내의 application.properties 가 최종적으로 사용됨.
따라서, 테스트 환경에서 main 과 다르게 별도의 설정을 주어야 하는 경우 주의가 필요함.
이를 위해 테스트 파일에서 외부 설정을 다르게 주는 방법은 다음과 같이 있음.

1) @SpringBootTest 의 Properties 속성

// SpringinitApplicationTest.java

@RunWith(SpringRunner.class)
@SpringBootTest(properties = "heumsi.name = heumsiTest")
public class SpringinitApplicationTest {

  @Autowired
  Environment environment

  @Test
  public void contextLoads {
    String name = environment.getProperty("heumsi.name");
    // name 에 위에서 변경한 heumsiTest 가 바인딩 됨.
  }
}

2) @TestPropertySource 사용

// SpringinitApplicationTest.java

@RunWith(SpringRunner.class)
@TestPropertySource(properties = {"heumsi.name = heumsiTest", })
@SpringBootTest
public class SpringinitApplicationTest {
  ...
}

혹은 별도의 설정파일 test/resources/test.properties 을 만들어 놓고, 다음과 같이 사용 가능하다.

// SpringinitApplicationTest.java

@RunWith(SpringRunner.class)
@TestPropertySource(locations = "classpath:/test.properties")
@SpringBootTest
public class SpringinitApplicationTest {
  ...
}

우선 순위

불러오는 설정 값은 여러 곳에서 오버라이딩될 수 있기 때문에, 누가 누구를 오버라이딩하는지 우선순위를 알아야 한다.

1) Property 우선 순위

우선 순위를 간략히 정리하면 다음과 같다.

  1. @TestPropertySource
  2. @SpringBootTest(properties=...)
  3. 커맨드 라인 argument
  4. @PropertySource
  5. 기본 프로퍼티

2) Properties 파일 우선순위

application.propertiesresources/ 말고 다른 곳에서도 사용될 수 있다.
이 때 application.properties 의 위치에 따른 우선 순위는 다음과 같다.

  1. /config/
  2. /
  3. <classpath>/config/
  4. <classpath>/

플레이스 홀더와 랜덤 값

application.properties 에서 다음과 같이 사용 가능하다.

// application.properties

// place holder (재 사용 가능)
heumsi.lastName = Jeon
heumsi.firstName = heumsi
heumsi.name = ${heumsi.lastName} ${heumsi.firstname}

// random number (랜덤 값 입력하기)
random.number = ${random.int}

외부 설정 Bean 으로 만들기

외부 설정 파일을 하나의 클래스로 만들어, 이를 코드 내부에서 Bean 으로 등록한 뒤 사용할 수 있다.

먼저 applcation.properties 는 다음과 같이 입력되있다고 하자.

// application.properties

heumsi.firstName = heumsi
heumsi.lastName = jeon
heumsi.age = ${random.int(0,100)}
heumsi.fullName = ${heumsi.firstName} ${heumsi.lastName}

여기서 heumsi. 에 해당하는 설정 값들만 고려하여 클래스로 정의하자.

// HeumsiProperties.java

@Component // 빈 등록
@ConfigurationProperties("heumsi") // application.properties 와 바인딩
public class HeumsiProperties {

    String firstName;
    String lastName;
    int age;
    String fullName;

    ... (getter, setter)

Bean 으로 등록되었으니, 이제 @Autowired 로 주입받아 접근하는게 가능해진다.
예를 들어 다음과 같이 사용할 수 있다.

// SampleRunner.java

@Component
public class SampleRunner implements ApplicationRunner {

    @Autowired
    HeumsiProperties heumsiProperties;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        String fullName = heumsiProperties.getFullName();
        int age = heumsiProperties.getAge();
    }
}

application.property 더 알아보기

1) 융통성 있는(Relaxed) 바인딩

application.properties 는 키 이름에 _ (under score)- (kebab) 가 포함되어도 알아서 camleCase 로 변환하여 바인딩 한다.

heumsi.full-name
heumsi.full_name
위 모두 heumsi.fullName 에 바인딩 됨.

2) Duration Type 컨버전

application.properties 안에 있는 키, 값들은 사실 모두 문자열 형태로 존재하지만, Bean 으로 등록되는 클래스와 바인딩될 때, String, int, Duration 등으로 모두 타입 컨버전 된다. (이는 스프링이 제공하는 기능)

특히 Duration 의 예시를 좀 더 살펴보면

// HeumsiProperties.java

@Component
@ConfigurationProperties("heumsi")
public class HeumsiProperties {
  ...
  private Duration sessionTimeout = Duration.ofSeconds(30);
  // 기본 값 30초
}
// application.properties

heumsi.sessionTimeout = 20s

위와 같이 s, m, d 등과 같은 suffix 로 Duration 타입임을 명시하고 바인딩시켜줄 수 있다.

3) Validate 로 값 검증

다음과 같이 @Validated@NotEmpty 가 추가되었다고 생각해보자.

// HeumsiProperties.java

@Component
@ConfigurationProperties("heumsi")
@Validated
public class HeumsiProperties {
  @NotEmpty
  private String name
}

그리고 application.properties 에서 heumsi.name 으로 다음과 같이 빈 값을 주면
컴파일 과정 중 에러를 발생시킨다.

// application.properties

heumsi.name = 
...

@NotEmpty 외에도 다양한 어노테이션이 있는데, 이는 JSR-303 을 참고하면 된다.