일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 1차원 DP
- 2차원 dp
- 99클럽
- @BeforeAll
- @BeforeEach
- @Builder
- @Entity
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Query
- @Table
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- api gateway 설계
- api gateway 필터
- ApplicationEvent
- argocd
- assertThat
- async/await
- AVG
- AWS
- aws autoscaling
- aws eks
- AWS KMS
- aws vpc peering
- Today
- Total
기록
Spring 프로필, Gradle, JVM 옵션: -Dspring.profiles.active 본문
1. 이 글을 쓰게 된 이유
Spring Boot에서는 흔히. -Dspring.profiles.active=xxx 옵션으로 프로필을 지정한다. 그런데 어느 날 ./gradlew test -Dspring.profiles.active=git 명령어를 실행했는데, git 프로필이 적용되지 않는 현상을 마주하게 되었다. 분명히 JVM 옵션을 줬는데 왜 안 될까? 이 의문을 해결하기 위해 콘솔 → Gradle → JVM → Spring Boot까지의 프로필 전달 구조를 깊게 파헤쳐 보았다. 결과적으로 테스트 시에는 JVM 옵션이 자동으로 적용되지 않는 구조라는 걸 알게 되었고, Gradle의 테스트 환경과 JVM 간의 동작 방식에 대한 이해가 중요함을 느꼈다.
2. 기본 개념 정리
2.1 JVM 옵션 -D란?
JVM을 실행할 때 -Dkey=value 형식으로 전달하는 시스템 속성이다. Java 애플리케이션 내에서는 System.getProperty("key")로 이 값을 읽을 수 있다. 이 방식은 JVM 내부적으로 key-value 기반의 전역 설정 값을 전달할 수 있도록 해준다.
예:
java -Dspring.profiles.active=prod -jar app.jar
Spring Boot는 내부적으로 이 값을 읽어 어떤 프로필을 활성화할지 결정한다. 이 방식은 가장 높은 우선순위를 가지므로 설정을 명시적으로 지정하고자 할 때 사용된다.
2.2 Spring 프로필 우선순위
Spring Boot는 다음과 같은 순서로 프로필을 적용한다:
- -Dspring.profiles.active=... (JVM system property)
- SPRING_PROFILES_ACTIVE=... (환경변수)
- application.yml의 fallback 값 (e.g. ${SPRING_PROFILES_ACTIVE:native})
각 단계는 앞 단계의 값이 없을 경우에만 다음 단계로 넘어간다. 따라서 가장 우선 적용하고 싶은 방식이 있다면 해당 우선순위의 방법으로 지정해야 한다.
3. Gradle 테스트는 왜 다를까?
3.1 Gradle도 JVM이다
Gradle 자체도 Java로 만들어졌기 때문에 JVM 위에서 동작한다. 따라서 ./gradlew 명령어를 실행하면 Gradle JVM이 하나 생성된다. 이 JVM이 build.gradle 스크립트를 읽고, 태스크를 설정하고 실행한다.
3.2 테스트는 별도 JVM에서 실행된다
Gradle은 테스트를 실행할 때 현재 JVM(Gradle 실행 JVM)에서 하지 않고, 새로운 JVM 프로세스를 fork(분기)해서 테스트를 실행한다. 이것을 forked test JVM이라고 한다. 이 프로세스는 독립된 메모리와 클래스 로딩 구조를 가지기 때문에, 기존 Gradle JVM의 시스템 속성이나 환경변수가 자동으로 공유되지 않는다.
이로 인해 문제가 생긴다:
./gradlew test -Dspring.profiles.active=test
이렇게 실행하면 -D 옵션은 Gradle JVM에만 전달되고, 테스트를 실행하는 forked JVM에는 전달되지 않는다. 결과적으로 테스트 코드 내에서 System.getProperty("spring.profiles.active")는 null을 반환하게 되고, Spring Boot는 fallback 값이나 기본값을 사용하게 된다.
4. 해결 방법
4.1 Gradle build.gradle에서 jvmArgs로 명시적으로 전달
테스트 JVM에 시스템 프로퍼티를 전달하려면 jvmArgs로 명시해야 한다:
tasks.withType(Test).configureEach {
def profile = System.getProperty("spring.profiles.active")
if (profile) {
jvmArgs "-Dspring.profiles.active=${profile}"
}
}
이렇게 하면 Gradle이 fork하는 테스트 JVM에도 시스템 속성이 제대로 전달되어 Spring이 프로필을 인식한다.
4.2 환경변수를 사용하는 경우도 직접 전달 필요
SPRING_PROFILES_ACTIVE=dev ./gradlew test
이 경우도 테스트 JVM에는 자동으로 전달되지 않기 때문에 build.gradle에서 이렇게 지정해줘야 한다:
environment "SPRING_PROFILES_ACTIVE", "test"
System.getenv() 방식으로 읽는 값도 마찬가지로 fork된 JVM에서 따로 설정하지 않으면 접근할 수 없다.
5. application.yml의 fallback 전략
Spring Boot는 아무 설정도 전달되지 않았을 때를 대비해 application.yml에서 fallback 값을 지정할 수 있도록 지원한다:
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:local}
위와 같이 작성하면, SPRING_PROFILES_ACTIVE가 없을 경우 자동으로 local 프로필이 적용된다. 로컬 개발 환경에서 기본값을 지정하고 싶을 때 유용한 방식이다.
6. 전체 흐름 요약
실행 방식별 적용 여부
실행 방식 적용 여부 설명
java -jar | ✅ 적용됨 | JVM 직접 실행 → -D 바로 적용 |
./gradlew bootRun | ✅ 적용됨 | Spring Boot Plugin이 JVM에 전달함 |
./gradlew test -Dspring.profiles.active=xxx | ❌ 적용 안됨 | Gradle JVM에만 적용되고 테스트 JVM에는 전달되지 않음 |
./gradlew test + jvmArgs 설정 | ✅ 적용됨 | forked JVM에도 명시적으로 전달됨 |
7. 정리
- Gradle은 JVM 위에서 동작하지만, 테스트는 별도 JVM으로 실행된다.
- -Dspring.profiles.active=xxx는 테스트 JVM에는 자동 전달되지 않는다.
- 테스트 시에 프로필을 정확히 적용하려면 반드시 jvmArgs 또는 environment로 명시해야 한다.
- 반면, java -jar는 우리가 지정한 -D 옵션이 곧바로 실행 JVM에 적용되기 때문에 Spring이 정확히 인식할 수 있다.
'Web > Spring' 카테고리의 다른 글
Spring Cloud Gateway에서 특정 경로만 라우팅할 때의 고민과 설계 방향 (0) | 2025.04.28 |
---|---|
멀티 모듈 기반 영화 예매 서비스 설계기 : 테이블 및 엔티티 설계(1) - ERD 모델링 (0) | 2025.04.07 |
Spring Cloud Gateway에서 Config Server 설정 없이 로컬 개발하는 법 (0) | 2025.04.07 |
"JWT signature does not match" 오류의 원인과 해결 과정 (0) | 2025.04.03 |
멀티 모듈 기반 영화 예매 서비스 설계기 : 멀티 모듈 프로젝트 설계(3) - Layer 기반의 모듈 분리 (0) | 2025.04.01 |