
application.yml 환경별로 관리할 필요가 있는 이유
- 개발 환경과 운영 환경에서 사용하는 DB는 독립적이다.
- 개발 환경 DB는 팀원 모두가 바라보고 있기 때문에 특정 기능에 의존적인 테스트 데이터 주입 등 자유롭게 설정하는 데 부담이 존재한다. 필요하다면 로컬 환경에서도 작업할 수 있어야 한다.
- 개발 환경을 기준으로 작성한 application.yml을 두고 작업한다면 매번 본인의 작업 환경으로 수정해야 한다. 자칫 이를 수정하지 않고 원격 저장소에 반영하게 되면 팀원들은 불필요한 충돌 작업을 겪게 된다. 또한, 운영 환경과 개발 환경 두 가지를 충족시키기 어렵다.
관리 방법
1. 개발환경, 운영환경, 테스트환경, 로컬 환경별로 관리
- 팀원이 실수하면 설정이 충돌이 나는 시스템은 좋지 않다. 약간의 노력만 투입하면 필요한 환경을 미리 설정하여 충돌이 나지 않게 할 수 있다.
- application.yml을 profile마다 선언한다. application.yml에는 공통 환경을 설정하고 application-dev.yml, application-prod.yml을 만든다.
- application-local.yml은 로컬 개발 환경을 사용하는 사람이 자유롭게 작성해서 사용할 수 있도록 미리 .gitignore에서 등록해둔다.
spring:
application:
name: ggorangjirang
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DB_DEV_HOST}:${DB_DEV_PORT}/${DB_DEV_NAME}
username: ${DB_DEV_USER}
password: ${DB_DEV_PW}
jpa:
properties:
hibernate:
show_sql: true
format_sql: true
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DB_PROD_HOST}:${DB_PROD_PORT}/${DB_PROD_NAME}
username: ${DB_PROD_USER}
password: ${DB_PROD_PW}
- 환경별로 구성한 yml을 어떻게 선택할 수 있을까? Intelij를 사용한다면 아래와 같이 간단히 active할 profile을 선택할 수 있다.

- 위의 사진은 intellij ultimate 버전만 가능하고, community 버전이라면 environment variable에
spring.profiles.active=dev
를 추가한다.
2. DB가 필요한 테스트 환경
- 서비스, 컨트롤러 단위테스트는 상관이 없지만 DB에 종속적인 통합 테스트를 진행할 수도 있다. 테스트 환경에서는 어떤 DB를 바라봐야 할까? 로컬 DB를 바라보게 되면 다른 팀원들이 gradle build를 하게 되면 TEST 빌드가 실패할 수 있다. 누구는 테스트가 성공하고, 누구는 실패하는 건 좋은 테스트 환경이 아니다.
- DB에 의존적인 테스트는 개발 DB를 기준으로 작성되어야 한다. 따라서 src/test/resources에 application.yml을 작성하고 개발 DB를 기준으로 환경을 설정한다.
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${DB_DEV_HOST}:${DB_DEV_PORT}/${DB_DEV_NAME}
username: ${DB_DEV_USER}
password: ${DB_DEV_PW}
3. 기존 환경변수 주입방식의 문제점(intellij export Environment variables)
- 애플리케이션에 민감한 정보를 application.yml에 직접 선언하게 되면 보안상 굉장히 취약해진다. 프로젝트 저장소를 외부에서 접근할 수 있다면 환경변수를 통해 민감한 정보를 숨겨야 한다.

- Intelij에서 환경 변수를 위에 처럼 추가하거나 파일 자체로도 추가할 수 있다. 하지만 이러한 설정은 Intelij에 의존적이다. 예를 들어 빌드서버에서 gradle로 build할 때는 intelij에서 설정한 환경변수를 읽을 수 없었다. 그래서 별도로 gradle에 환경변수를 설정하는 스크립트를 작성하여 빌드할 때 환경변수를 추가하였다.
- 위의 방식도 괜찮다. 다만, 환경변수가 추가되거나 변경되었을 때 이를 intelij에 반영하고, 빌드 서버에서도 변경된 환경변수를 반영해야 한다. 비슷한 작업을 두 번해야 한다는 피로감이 존재했다.
4. 개선된 환경변수 주입방식(env.yml)
- 스프링 애플리케이션이 실행될 때 필요한 환경변수 파일을 읽어서 가지고 있다면 intelij에서나 빌드 서버에서나 동일한 환경변수를 사용하는 게 보장된다. 물론, 빌드 서버에 env.yml을 잘 전달해야 하는 건 다른 문제다.
- env.yml을 .gitignore에 등록해 프로젝트 저장소에 올라가지 않도록 주의하자!
- SpringBootAplication이 실행될 때 필요한 환경변수를 파일로 읽을 수 있도록 설정하는 방법이다.
1) @PropertySouce로 환경변수를 읽을 파일명(env.yml)과 방법 정의(Envconfig.class)
@SpringBootApplication
@PropertySource(value = {
"classpath:env.yml",
}, factory = EnvConfig.class)
public class SpacestoryApplication {
public static void main(String[] args) {
SpringApplication.run(SpacestoryApplication.class, args);
}
}
2) EnvConfig.class에서 YML 구현체 선택
@Configuration
public class EnvConfig implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(resource.getResource());
Properties properties = factoryBean.getObject();
assert properties != null;
return new PropertiesPropertySource(Objects.requireNonNull(resource.getResource().getFilename()), properties);
}
}
3. 테스트 환경에서도 env.yml에서 환경변수를 읽도록 추가 (1와 동일)
- 이렇게 설정하면 환경변수를 파일로 관리하기 때문에 intelij에서 직접 실행하든, 빌드 서버에서 빌드를 하든 환경변수 동기화 문제가 해결된다.
여전히 해결되지 않는 문제
1. env.yml 동기화 문제
- 각기 작업을 하면서 필요한 환경변수를 각자 로컬에 있는 env.yml에 추가한다. env.yml을 git으로 추적하지 않기 때문에 형상관리 문제는 존재하지 않지만, 누구는 필요한 환경변수가 없어서 애플리케이션이 제대로 동작하지 않을 수 있다.
2. 전달 과정에서 보안 문제
- 팀원들 간 추가된 환경변수를 공유하는 과정에서 보안 취약점이 발생할 가능성이 굉장히 높아진다.
hashicorp vault 필요성
- 여러 방법이 있겠지만 vault를 이용해서 키 관리를 하면 위의 문제를 해결할 수 있다.
- 하지만 vault라는 기술의 학습 곡선도 존재하고, 초기 unseal key를 공유하는 과정에서 추가 기술(예를 들어 aws의 Key Management System)을 학습해야 하니 팀원들간 상의 후 적용해보는 것도 좋겠다.
참고자료
