spring boot profile 설정 — properties 분리와 active 지정
스프링부트의 프로파일 설정은 두 가지가 전부입니다. ① 설정 파일을 application-{환경}.properties로 나누고, ② 실행할 때 spring.profiles.active로 어느 것을 쓸지 고르는 것입니다. 고르는 방법은 JVM 옵션 -Dspring.profiles.active=dev, 실행 인자 --spring.profiles.active=dev, 환경변수 SPRING_PROFILES_ACTIVE=dev, application.properties에 직접 적기 네 가지입니다. 안 먹는다면 -D를 -jar 뒤에 둔 경우가 대부분입니다. 아래 출력은 Spring Boot 3.3.5 · JDK 21에서 직접 실행해 확인한 결과입니다.
1. properties 파일 나누기
먼저 개발 환경에 맞게 설정 파일을 만듭니다. 이름은 반드시 application-{프로파일이름}.properties 형식이어야 합니다. 이 이름 규칙이 곧 프로파일 이름이 됩니다.
지금 어떤 프로파일로 떠 있는지 눈으로 확인할 수 있게, 각 파일에 키를 하나씩 넣어 둡니다.
# application-dev.properties
profile.now=dev_profile
여기서 중요한 건 application.properties(이름에 프로파일이 없는 파일)는 항상 함께 로드된다는 점입니다. 공식 문서도 "프로파일 전용 프로퍼티는 표준 application.properties와 같은 위치에서 로드되며, 프로파일 전용 파일이 항상 비전용 파일을 덮어쓴다"고 설명합니다. 즉 공통 설정은 application.properties에 두고, 환경마다 달라지는 값만 프로파일 파일에 적으면 됩니다.
2. 활성 프로파일 지정하는 4가지 방법
| 방법 | 쓰는 곳 | 예 |
|---|---|---|
| JVM 시스템 속성 | 배포 서버 실행 스크립트 | java -Dspring.profiles.active=dev -jar app.jar |
| 실행 인자 | 임시 실행·테스트 | java -jar app.jar --spring.profiles.active=dev |
| 환경변수 | 도커·CI·컨테이너 | SPRING_PROFILES_ACTIVE=prod |
| 설정 파일에 직접 | 로컬 기본값 | application.properties에 spring.profiles.active=dev |
실제로 돌려 보면 이렇게 갈립니다. 아무것도 지정하지 않으면 활성 프로파일은 비어 있고, 기본 프로파일 default만 있습니다.
$ java -jar app.jar
getActiveProfiles() = []
getDefaultProfiles() = [default]
profile.now = default_profile
$ java -Dspring.profiles.active=dev -jar app.jar
getActiveProfiles() = [dev]
profile.now = dev_profile
여러 개를 한 번에 켤 수도 있습니다. 이때 같은 키가 겹치면 뒤에 적은 프로파일이 이깁니다. 공식 문서의 표현으로는 "여러 프로파일이 지정되면 last-wins 전략이 적용된다"입니다.
$ java -Dspring.profiles.active=dev,prod -jar app.jar
getActiveProfiles() = [dev, prod]
profile.now = prod_profile # dev 가 아니라 prod 값
3. 프로파일이 안 먹을 때 — 함정 두 가지
-D 를 -jar 뒤에 두면 조용히 무시된다
$ java -jar app.jar -Dspring.profiles.active=dev # ❌ 무시됨
getActiveProfiles() = []
profile.now = default_profile
-D는 JVM에게 주는 옵션이라 반드시 -jar(또는 메인 클래스) 앞에 와야 합니다. 뒤에 쓰면 JVM이 아니라 애플리케이션에 넘기는 문자열이 되고, 스프링부트는 --로 시작하는 인자만 프로퍼티로 읽기 때문에 오류 없이 그냥 무시됩니다. 뒤에 두고 싶다면 --spring.profiles.active=dev 형태로 바꿔야 합니다.
프로파일 전용 파일 안에는 spring.profiles.active 를 못 쓴다
application-dev.properties 안에 spring.profiles.active를 적으면 애플리케이션이 아예 뜨지 않습니다.
org.springframework.boot.context.config.InvalidConfigDataPropertyException:
Property 'spring.profiles.active' imported from location
'class path resource [application-dev.properties]'
is invalid in a profile specific resource
공식 문서에 "spring.profiles.active와 spring.profiles.default는 프로파일 전용이 아닌 문서에서만 쓸 수 있다"고 명시돼 있습니다. 프로파일을 켜는 스위치는 프로파일 바깥에 있어야 한다는 뜻입니다. 프로파일을 더 얹고 싶다면 대신 spring.profiles.include나 spring.profiles.group을 씁니다.
4. 현재 프로파일 확인하기
가장 간단한 방법 — Environment.getActiveProfiles()
별도 키를 만들지 않아도 스프링이 활성 프로파일을 그대로 알려줍니다.
import org.springframework.core.env.Environment;
private final Environment env;
// 활성 프로파일 배열 (지정 안 했으면 빈 배열)
String[] active = env.getActiveProfiles();
// 특정 프로파일로 떠 있는지
boolean isDev = env.matchesProfiles("dev");
키를 직접 두고 enum으로 다루기
프로파일 이름을 코드 여기저기에 문자열로 흩뿌리기 싫다면, 설정 파일에 키를 하나 두고 enum으로 감싸는 방법도 있습니다. 앞서 application-dev.properties에 넣어 둔 profile.now가 그 키입니다.
public enum ProfileKey {
NAME;
public String str() {
return "profile.now";
}
}
public enum ProfileCode {
LOCAL_PROFILE,
DEV_PROFILE,
MAIN_PROFILE;
public String lowerStr() {
return this.name().toLowerCase();
}
public String upperStr() {
return this.name().toUpperCase();
}
}
upperStr()은 toUpperCase()여야 합니다. 실수로 toLowerCase()를 쓰면 lowerStr()과 완전히 같은 값을 돌려주는데, 컴파일도 되고 예외도 안 나서 한참 뒤에야 발견됩니다. 실제로 돌려 보면 DEV_PROFILE.upperStr()이 dev_profile을 반환합니다.
이제 Environment에서 그 키를 읽어 오는 설정 클래스를 만듭니다.
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Slf4j
@Configuration
public class ProfileConfig {
private final Environment env;
public ProfileConfig(Environment env) {
this.env = env;
}
@PostConstruct
public void init() {
log.info(" ===== [INIT] nowProfile = {}", env.getProperty(ProfileKey.NAME.str()));
}
public ProfileCode now() {
String nowProfile = env.getProperty(ProfileKey.NAME.str());
if (nowProfile == null) return ProfileCode.DEV_PROFILE;
else if (nowProfile.equals(ProfileCode.LOCAL_PROFILE.lowerStr())) return ProfileCode.LOCAL_PROFILE;
else if (nowProfile.equals(ProfileCode.DEV_PROFILE.lowerStr())) return ProfileCode.DEV_PROFILE;
else if (nowProfile.equals(ProfileCode.MAIN_PROFILE.lowerStr())) return ProfileCode.MAIN_PROFILE;
else return ProfileCode.DEV_PROFILE;
}
}
lowerStr()이 돌려주는 값은 dev_profile이므로, 설정 파일의 profile.now 값도 enum 이름을 소문자로 바꾼 형태(local_profile·dev_profile·main_profile)로 맞춰 두어야 합니다. 이 규칙만 지키면 어느 클래스에서든 now() 한 번으로 현재 환경을 알 수 있습니다. 애플리케이션이 뜰 때는 @PostConstruct의 로그로 확인됩니다.
===== [INIT] nowProfile = dev_profile
5. @Profile 로 빈 자체를 갈아끼우기
값만 다른 게 아니라 구현체가 통째로 달라져야 할 때는 @Profile을 씁니다. 활성 프로파일에 해당하는 빈만 등록됩니다.
@Bean @Profile("dev")
MailSender devMailSender() { return new ConsoleMailSender(); } // 개발: 콘솔에 출력만
@Bean @Profile("prod")
MailSender prodMailSender() { return new SmtpMailSender(); } // 운영: 실제 발송
직접 확인해 보면 프로파일에 따라 등록되는 빈이 이렇게 갈립니다.
지정 없음 → 등록된 빈 [] # 둘 다 안 만들어짐
dev → 등록된 빈 [devBean]
prod → 등록된 빈 [prodBean]
dev,prod → 등록된 빈 [devBean, prodBean]
마지막 줄을 주의해서 보세요. 여러 프로파일을 켜면 해당하는 빈이 전부 등록됩니다. 같은 타입의 빈이 둘이 되면 주입 시점에 이렇게 죽습니다.
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'user': Unsatisfied dependency expressed through
method 'user' parameter 0: No qualifying bean of type 'Mail' available:
expected single matching bean but found 2: devMail,prodMail
배타적으로 쓸 프로파일은 동시에 켜지 않는 게 원칙입니다. 굳이 함께 켜야 한다면 한쪽에 @Primary를 붙이거나 주입 지점에 @Qualifier로 이름을 찍어 주어야 합니다.
6. 인텔리제이와 배포 시 설정
인텔리제이에서는 Run/Debug Configurations에서 프로파일을 지정합니다.
배포할 때는 빌드가 아니라 실행 명령에 옵션을 붙입니다. 같은 jar 하나로 환경만 바꿔 띄우는 게 프로파일을 쓰는 이유이기 때문입니다.
# JVM 옵션 (-jar 앞!)
java -Dspring.profiles.active=dev -jar myapp.jar
# 실행 인자
java -jar myapp.jar --spring.profiles.active=dev
# 도커·CI 라면 환경변수가 편하다
SPRING_PROFILES_ACTIVE=prod java -jar myapp.jar
환경마다 갈리는 설정은 DB 주소만이 아닙니다. 허용할 프런트엔드 도메인도 로컬·개발·운영이 다른 게 보통인데, 이 값을 프로파일별 properties로 빼 두면 편합니다. 설정 방법은 spring boot cors 설정 글에 정리해 두었습니다.
자주 묻는 질문 (FAQ)
Q. application.properties와 application-dev.properties를 둘 다 두면 어느 쪽이 이기나요?
프로파일 전용 파일이 이깁니다. 다만 비전용 파일이 무시되는 건 아닙니다. 둘 다 로드되고, 같은 키가 겹칠 때만 프로파일 전용 값이 덮어씁니다. 그래서 공통 설정은 application.properties에 두는 게 맞습니다.
Q. 프로파일을 지정하지 않으면 어떻게 되나요?
getActiveProfiles()가 빈 배열이 되고 기본 프로파일 default가 켜집니다. application-dev.properties 같은 파일은 로드되지 않고, @Profile("dev") 빈도 만들어지지 않습니다.
Q. 옵션을 줬는데 계속 기본값으로 뜹니다.
-D의 위치를 확인하세요. java -jar app.jar -Dspring.profiles.active=dev처럼 -jar 뒤에 두면 오류 없이 무시됩니다. -D는 -jar 앞, --는 뒤입니다.
Q. 프로파일 이름을 꼭 dev·prod로 해야 하나요?
아닙니다. 파일 이름 application-{이름}.properties의 {이름}이 그대로 프로파일 이름이 되므로 local·staging·qa 무엇이든 됩니다. spring.profiles.active에 같은 이름을 주기만 하면 됩니다.
Q. 운영 설정 파일을 jar 안에 넣기 싫습니다.
application-prod.properties를 jar 바깥, 실행 위치의 config/ 폴더에 두면 됩니다. 스프링부트는 클래스패스뿐 아니라 실행 디렉터리와 그 아래 config/도 찾아보고, 바깥 파일이 jar 안의 값을 덮어씁니다.
마무리
정리하면 이렇습니다. ① 파일은 application-{환경}.properties로 나누고, 공통 값은 application.properties에 남긴다. ② 활성화는 -D·--·환경변수·설정 파일 네 가지 중 환경에 맞는 것으로. ③ -D는 반드시 -jar 앞에 — 뒤에 두면 조용히 무시된다. ④ 프로파일 전용 파일 안에는 spring.profiles.active를 넣을 수 없다. ⑤ 현재 프로파일은 Environment.getActiveProfiles()로 바로 알 수 있고, 구현체 자체를 바꿔야 하면 @Profile을 쓴다.
📚 참고 출처 (2026년 7월 29일 확인 · Spring Boot 3.3.5 · JDK 21 실행)
· Spring Boot Reference — Profiles
· Spring Boot Reference — Externalized Configuration

COMMENTS