(25.05.08) Spring Boot 설정값 주입 : @ConfigurationProperties 활용
(25.04.23) Spring Security 인증 인가를 통한 Login 기능 구현 & Token 재발급 구현
2025.03.20 - [[Gena Co.] Internship Project/GENA Labeling Tool] - Gena Labeling Tool 개발기 : 기획부터 PoC 까지 Gena Labeling Tool 개발기 : 기획부터 PoC 까지안녕하세요, 저는 Gena Co. 인턴 김현진(Andrew) 입니다. Gena에서 t
andrew75313.tistory.com
기존의 Gena Labelling Tool의 JWT 를 통한 인증 인가 방식을 도입하면서
실제로 툴을 활용할 개발자들이 변수를 통해 해당 JWT를 커스텀할 수 있도록 하는 방법을 고안하고자 했다.
이전의 Credential 부분에 있어서 Spring Security를 활용중 Spring Boot 의 @Value 를 활용했지만,
JWT 설정 값들을 application 에서 한번에 설정할 수 있도록
여러 값들을 안전하게 한번에 설정할 수 있도록
하기 위해 Spring 문서를 찾아보면서
ConfigurationProperties 을 적용을 했다.
적용 상황 : JwtUtil
- JWT 를 생성하는데 활용할 JwtUtil 클래스에서, JWT 시크릿키, 토큰 별 유효시간 등 값을 환경마다 지정할 수 있게 설정에서 다룰 필요가 있음
@Value
- 빠르고 간편하게, 속성 파일의 값 즉, application 에서도 값을 생성자, 메서드 등에 직접적으로 주입할 수 있는 Annotation
- 직접 직관적으로 환경변수로 지정을 할 수도 있음
기존 프로젝트에서 구성한 JwtUtil
@Component
public class JwtUtil {
...
@Value("${JWT_SECRET_KEY}")
private String secretKey;
...
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
...
- 기존 프로젝트에서의 JWT SECRET KEY를 외부 환경변수에서(env)에서 직접적으로 secretKey 라는 필드에 직접적으로 지정을 한 상태
→ Github Actions 를 통한 CI/CD 과정에서, application.yml 에서 설정한 변수가 아니었기 때문에, application.yml만 보고 JWT_SECRET_KEY 설정을 놓칠 경우 Application 자체가 돌아가지 않는 이슈가 있었음
- 지정한 대상에만 적용하기 때문에, 반복해서 사용할 경우 클래스마다 설정을 해줘야
- 사용자가 잘못된 타입으로 지정(int 값인데, 너무 큰 숫자를 변수로 설정 등), 오타 등 컴파일에서야 해당 오류를 알 수 있는 단점
→ 단점을 보완할 수 있을까?
@ConfigurationProperties
- Spring Boot에서 application에서의 설정값들을 그룹화하여 구조화된 방식으로 주입
- Java 객체 Pojo로 바인딩=매핑 하는 방식
application.yml
jwt:
secret-key: ${JWT_SECRET_KEY}
access-token-time: ${JWT_ACCESS_TOKEN_TIME}
refresh-token-time: ${JWT_REFRESH_TOKEN_TIME}
- Spring Boot 기준으로 application.yml에서 JWT 설정값을 따로 관리하기 위해 위와 같이 환경변수로 설정할 수 있도록 세팅
→ SpringBoot 실행에 있어서의 설정값이 아닌, 실제 로직 코드에서 활용할 수 있는 수단인 @ConfigurationProperties를 @Value 대신 활용해야함
JwtProperties
@Component
@ConfigurationProperties(prefix = "jwt")
@Getter
@Setter
public class JwtProperties {
private String secretKey;
private long accessTokenTime;
private long refreshTokenTime;
}
- List, Map, Nested Object 등 복잡한 타입도 applcation의 설정 이름을 맞춰 매핑이 가능
- Getter Setter 를 꼭 설정해서, 적용하고 활용할 수 있도록 해야함
새롭게 바꾼 JwtUtil
@Component
@RequiredArgsConstruor
public class JwtUtil {
private final JwtProperties jwtProperties;
...
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(jwtProperties.getSecretKey());
key = Keys.hmacShaKeyFor(bytes);
}
public String createAccessToken(User user) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(user.getId().toString())
.claim(AUTHORIZATION_KEY, user.getRole())
.setExpiration(new Date(date.getTime() + jwtProperties.getAccessTokenTime()))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
public String createRefreshToken(User user) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(user.getId().toString())
.claim(AUTHORIZATION_KEY, user.getRole())
.setExpiration(new Date(date.getTime() + jwtProperties.getRefreshTokenTime()))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
...
- 따라서 그냥 JwtProperties 클래스를 생성자 주입을 하게 된다면, get 메서드를 통해서 바로 사용할 수 있음
- ConfigurationProperties 클래스가 일종의 중간 역할
중첩 클래스를 바인딩해서 사용하기 예시)
mail:
host: smtp.example.com
port: 587
auth:
username: myemail@example.com
password: secret
@Component
@ConfigurationProperties(prefix = "mail")
@Getter
@Setter
public class MailProperties {
private String host;
private int port;
private Auth auth;
@Getter
@Setter
public static class Auth {
private String username;
private String password;
}
...
}
- 위처럼 여러 중첩 클래스를 설정값 처럼 똑같이 매핑해서 만들 수 이씅ㅁ
- MailProperties.getAuth.getUsername 처럼 활용할 수 있음
@Value와 비교
@Value | @ConfigurationProperties | |
설정 개수 | 소수, 여러개 | 다수, 그룹 |
타입 지원 | 제한적 | List, Map, 객체 등 맞춰서 설정 가능 |
코드 구조 | 흩어짐, 한번에 파악 어려움 | 정리된 클래스 |
타입 안전 | 낮음 (문자열 기반) | 높음 (객체 바인딩) |
복잡한 구조 | 불가(매번 반복) | 가능(중첩 클래스 등) |
참고
https://docs.spring.io/spring-boot/appendix/application-properties/index.html
Common Application Properties :: Spring Boot
docs.spring.io