새로운 프로젝트를 진행하면서 다양한 개발 환경에 맞게 애플리케이션 설정을 관리할 수 있는 프로파일을 적용하면서 알게 된 내용을 정리해 본다.
환경변수
yml 파일의 프로퍼티 값을 전달받아 스프링 애플리케이션 내부에서 사용할 수 있다.
예를 들어 yml에 다음과 같이 security.jwt.token.secret-key 라는 환경변수 값을 추가하고 스프링 빈에 주입해 주면 애플리케이션이 실행될 때 잘 동작한다.
security:
jwt:
token:
secret-key: testkey
@Component
publci class SecretKey {
@Value("${security.jwt.token.secret-key"})
private String secretKey;
}
그런데 개발 과정에서는 상황에 따라 다른 값이 주입되어야 하는 경우가 있다.
예를 들어 테스트 환경에서 배포 환경에서 사용되는 DB를 사용하면 안 된다.
Test용 DB를 별도로 두어야 하는 것이다. 그리고 이러한 값들이 외부로 노출되지 않도록 관리해야 한다.
이러한 문제에 대한 해결방안 중 가장 효율적인 방안은 스프링 프로파일을 활용하는 것이다.
스프링 프로파일
스프링 프로파일을 활용해서 하나의 yml 파일 내에 다양한 프로퍼티 값들을 관리하는 방법에 대해 알아보자
아래는 환경변수를 받는 클래스이다.
@Component
public class SecretKey {
@Value("${common.data}")
private String commonData;
@Value("${security.jwt.token.secret-key"})
private String secretKey;
}
다음은 yml 파일이다.
application.yml
# 디폴트로 사용하는 프로파일은 local
spring:
profiles:
active: local
security:
jwt:
access-token-expiration-millis: 7200000 # 120 * 60 * 1000 == 2시간
refresh-token-expiration-millis: 1209600000 # 20160 * 60 * 1000 == 2주
application-dev.yml
# 프로파일이 dev인 경우, 해당 프로퍼티 사용
spring:
config:
activate:
on-profile: dev
jpa:
database: mysql
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate:
ddl-auto: create
show-sql: true
properties:
hibernate:
format_sql: true # SQL pretty print
defer-datasource-initialization: true
open-in-view: false
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://mysql.com:3306/challenge66?
username: username
password: password
sql:
init:
data-locations: classpath*:db/h2/data.sql
mode: always
application-local.yml
# 프로파일이 local인 경우, 해당 프로퍼티 사용
spring:
config:
active:
on-profile: local
jpa:
show-sql: true
hibernate:
ddl-auto: create
defer-datasource-initialization: true
database-platform: org.hibernate.dialect.H2Dialect
datasource:
drvier-class-name: org.h2.Driver
url: jdbc:h2:mem:db
username: sa
password:
h2:
console:
path: /h2
enabled: true
redis:
host: localhost
port: 6380
maxmemory: 128M
- 프로파일 명은 주로 dev, prod, test, local 등이 사용되지만 자유롭게 만들어서 사용해도 된다.
- spring.config.active.on-profile : 해당 프로파일이 선택되었을 때만 해당 영역의 프로퍼티들이 사용된다.
- 반대로 spring.config.active.on-profile 프로퍼티가 없는 영역의 프로퍼티들은 활성화된 프로파일과 무관하게 모든 프로파일들에서 공통적으로 사용된다.
- spring.profiles.active : 디폴트로 사용될 프로파일을 명시한다. 해당 값을 명시하지 않으면 default 프로파일이 사용되며, JVM 시스템 프로퍼티를 통해 덮어쓸 수 있다.
- 위 코드에서는 application.yml이 default 프로파일이다.
- 위 코드를 실행하면 security.jwt.access-token-expiration-millis 값은 “공통으로 사용되는 데이터”가 그대로 주입되고, 자동으로 local 프로파일이 선택된다.
- 별도 프로파일을 설정을 하지 않는 이상 local 프로파일이 선택되기 때문에 h2 데이터베이스를 사용한다.
- 사용될 프로파일을 수정하고 싶다면 Edit Configuration 을 선택하고, Active profiles 에 실행할 때 활성화시키리 프로파일을 기재한다.
- Active profiles 에 dev 를 입력하면 dev 프로파일이 선택되므로 MySQL 데이터베이스를 사용한다.
테스트용 환경설정
테스트를 진행하기 위해서는 로컬 환경에서 사용하는 DB와는 다른 DB를 사용해야 했다. 그리고 JWT의 Access Token에 설정한 현재 설정되어 있는 만료시간은 2시간으로 되어있기 때문에 만료가 잘되는지 테스트할 수가 없었다. 그래서 Test 전용 yml 파일을 만들었다.
application-test.yml
# 프로파일이 test인 경우, 해당 프로퍼티 사용
spring:
config:
active:
on-profile: test
jpa:
show-sql: true
hibernate:
ddl-auto: create
defer-datasource-initialization: true
database-platform: org.hibernate.dialect.H2Dialect
datasource:
drvier-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb
username: sa
password:
h2:
console:
path: /h2
enabled: true
redis:
host: localhost
port: 6381
maxmemory: 128M
security:
jwt:
secret-key: "test 프로파일에서만 사용되는 secret key"
access-token-expiration-millis: 5000 # 5초
refresh-token-expiration-millis: 5000 # 5초
@ActiveProfiles
현재 local 프로파일이 기본으로 설정되어 있어서 Test할 때마다 Edit Configuration 의Active profiles 에서 test 프로파일을 사용하도록 명시해야 한다.
굉장히 번거로운 작업이기 때문에 test 코드를 사용할 때는 test 프로파일을 사용할 수 있는 방법을 찾아보다가 @ActiveProfile 애너테이션을 알게 됐다.
@ActiveProfiles 애너테이션은 테스트 동작 시 사용할 프로파일을 지정할 수 있다. 테스트 코드 위에 @ActiveProfiles(”test) 애너테이션을 추가하면 해당 테스트는 test 프로파일의 데이터를 사용하게 된다.
@Slf4j
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@ActiveProfiles("test")
@SpringBootTest
class JwtTokenProviderTest {
...
}
마무리
프로파일 설정은 여러 가지 환경에 맞게 프로젝트를 설정할 수 있도록 하며, @ActiveProfiles는 특정 환경 프로파일을 선택할 때 사용한다.
프로파일 설정과 @ActiveProfiles를 사용하여 애플리케이션을 환경에 따라 구성하면, 같은 애플리케이션을 다양한 환경 따른 애플리케이션 설정을 쉽게 관리할 수 있다.
하지만 이 기능들은 너무 남발하면 코드를 복잡하게 만들 수 있으므로, 신중하게 사용해야 한다.
참조
'Spring' 카테고리의 다른 글
Spring - Spring Security + JWT 적용기 1편: 로그인 (5) | 2023.04.14 |
---|---|
Spring - 나의 첫 통합 테스트(Integration Test) (0) | 2023.04.12 |
Spring Security - Spring Security란? (0) | 2023.03.27 |
Spring - Spring initializr로 프로젝트 생성하기 (0) | 2023.03.22 |
Spring - Thmbnail 이미지로 웹 성능 향상시키기 (0) | 2023.03.15 |