spring

[Spring] @Value 어노테이션 (설정 값 분리의 필요성)

허몽구 2023. 4. 15. 05:43

개발을 하다보면 DB에 연결해야하는 정보나 외부 API 주소 등 메타 정보를 관리해야할 때가 있다.

이러한 정보들을 클래스 파일에 직접 넣을 수도 있지만, properties나 yml로 분리해서 관리할 것을 권장하고 있다.

이유를 알아보자.

 

1. 환경에 따라 유연한 값 설정 가능

환경에 따라 다른 값을 자바 코드로만 설정하려면 번거롭고 중복되는 코드가 많아진다.

그래서 설정값은 별도의 properties나 yml 파일을 사용하여 분리하고, 자바 코드에서는 환경에 맞는 설정값을 불러오도록 설정할 수 있다.

 

2. 초기값 설정 가능

불러오는 설정 값이 초기값을 지정할 수 있는데, 이를 통해 설정값이 불러지지 않은 경우에 대비할 수 있다.

즉, 초기값을 설정함으로써 안전한 개발을 할 수 있다.

 

3. 불필요한 컴파일 줄이기

값이 클래스 안에 직접 설정되어 있다면 값이 수정될 때마다 소스코드가 변하게 되므로, 다시 컴파일을 해야 한다.

하지만 설정값을 분리하고 @Value로 참조하게 한다면 불필요한 컴파일 없이 주입되는 값을 변경할 수 있다.

 

값을 클래스에 설정하지 말고 @Value로 참조하여 분리시키는 이유를 알아봤다.

그럼 코드에 어떻게 적용할 수 있을까?

 

우선 @Value로 정보를 가져오는 방법은 PropertyPlaceHolderConfigurer을 통해 ${}을 사용하는 방식과, SpEL을 통해 #{}을 사용하는 방식이 있다.

 

1. PropertyPlaceHolderConfigurer

PropertyPlaceHolderConfigurer는 빈 팩토리 후처리기로 빈 설정 메타정보가 모두 준비됐을 때 빈 메타정보 자체를 조작하기 위해 사용한다.

다음과 같이 사용할 수 있다.

jwt:
  secret: ${token.secret}
  access-token-expiration-time: ${token.access-token-expiration-time}
  refresh-token-expiration-time: ${token.refresh-token-expiration-time}

: 옆의 ${}은 환경변수로 설정한 것이다.

 

    @Value("${token.access-token-expiration-time}")
    private String accessTokenExpirationTime;

    @Value("${token.refresh-token-expiration-time}")
    private String refreshTokenExpirationTime;

    @Value("${token.secret}")
    private String tokenSecret;

yml에 작성된 키 값을 @Value ${} 안에 넣어주면 Spring이 PropertyPlaceHolderConfigurer를 통해 초기화 작업 중에 해당 값을 실제 값으로 치환한다.

 

하지만 이 방법은 대체할 위치를 치환자로 지정하고 별도의 후처리기가 값을 변경해주기를 기다리기 때문에 수동적인 방법이다.

초기 빈 메타정보에는 ${token.secret} 등과 같은 문자열이 저장되어 있다.

만약 스프링 컨테이너가 설정 파일에서 대체할 적절한 키 값을 찾지 못 한다면, 문자열이 그대로 남아있게 되는 것이다.

그래서 치환자의 값이 변경되지 않더라도 예외가 발생하지 않는다.

 

위의 문제때문에, Spring 3부터 SpEL을 지원하며 능동 변환을 권장하고 있다. 

 

2. SpEL

SpEL을 통해 다른 빈 오브젝트에 접근할 수 있는 표현식을 이용하여 원하는 프로퍼티에 쉽게 접근이 가능해 값을 능동적으로 가져온다.

SpEL은 #{} 안에 표현식을 넣는데, token.secret이라는 표현식은 이름이 token인 secret 프로퍼티를 의미한다.

SpEL은 다른 빈 프로퍼티에 접근할 수 있고, 메소드 호출이 가능하며 다양한 연산을 지원해 클래스 정보에도 접근이 가능하다.

심지어, 생성자를 호출해서 객체를 생성할 수도 있다.

 

yml형식은 같고, 클래스에 다음과 같이 사용한다.

    @Value("#{environment['token.access-token-expiration-time']}")
    private String accessTokenExpirationTime;

    @Value("#{environment['token.refresh-token-expiration-time']}")
    private String refreshTokenExpirationTime;

    @Value("#{environment['jwt.secrett']}")
    private String tokenSecret;

environment라는 이름의 빈으로부터 해당 키에 해당하는 값을 읽어온다.

여기서 가져오는 빈은 Properties나 yml타입 등의 빈이 될 수 있다.

 

SpEL은 오타와 같은 실수가 있을 때 에러 검증이 가능하다.

그래서 이름을 잘못 적는 실수가 있어도 예외가 발생하지 않는 SpEL 사용을 권장하는 것이다. 

 

@Value("#{environment['token.secret'] == null " + 
			"? '기본 토큰 시크릿 정보'" +
			": environment['token.secret']}")
private String tokenSecret;

이런 식으로 사용할 수도 있다.

 


매번 PropertyPlaceHolderConfigurer 방법을 사용해왔었는데, 간혹 이름을 잘못 적어서 적용이 안 될 때가 있었다.

앞으로는 PropertyPlaceHolderConfigurer 보단 SpEL 방식을 사용해야겠다.