Environment
- ApplicationContext는
EnvirionmentCapable
이란 클래스를 extends 함으로써, Environment 라는 기능을 제공한다.
Environment란?
Spring Application이 처한 다양한 환경, 즉 개발 환경, 테스트 환경, 운영 환경 등 의미- DataBase 또는 mockServer 등의 변경이 빈번하게 일어나는 환경 변화 중 하나이다.
- Spring의 환경은 프로파일과 프로퍼티로 제공된다.
- 프로파일에 따라 환경이 바뀌고, 그에 따라 프로퍼티가 바뀐다.
- 즉, 환경에 따라 프로파일과 프로퍼티 소스가 다르게 설정된 Environment 오브젝트가 사용된다.
Property
- Application을 개발하다 보면 DB/SFTP 접속정보, 서버포트 정보 등 다양한 정보를 Application 속성 즉 Property로 정의하고 설정하게 된다.
- 이러한 Property 정보는 변동이 잦거나, 보안상의 이슈가 발생할 수 있는 경우가 많기 때문에 외부에 저장해두고 이를 Application이 읽어올 수 있도록 관리하는 경우가 많다.
- Property 소스 : Property 파일, JVM 시스템 Property, OS 환경변수, Property객체 등
- Spring Environment 객체의 역할 : 속성을 직접 정의하거나 다양한 속성 정보를 외부 Property 소스로부터가져오는 인터페이스 제공
- Environment 객체를 이용해 직접 속성을 정의하고, 그 속성을 불러오는 코드를 작성해보자 (실습)
- Spring 프로젝트를 생성하면 application.properties 라는 파일이 자동으로 생성되는데, 이는 SpringBoot에서 디폴트로 사용하는 property 파일이다.
- Property 파일은 주로
key:value
형식으로 작성한다.
- Spring에서 이미 정의해둔 Property를 사용할수도 있고, 직접 Property를 정의해도 된다.
version = v1.0.0 kdt.version = v1.0.0 kdt.support-vendors = a, b, c, d, e, f, g kdt.minimum-order-amount = 1
@PropertySource("application.properties")
var environment = applicationContext.getEnvironment(); var version = environment.gerProperty("kdt.version"); var minimumOrderAmount = environment.getProperty("kdt.minimum-order-amount",Integer.class); var supportVendors = environment.gerProperty("kdt.support-vendors",List.class);
@Value("v1.1.1") private String version;
@Value("${kdt.version}") private String version; @Value("${JAVA_HOME}") // JVM 환경변수 private String javaHome;
@Value("${kdt.version:v0.0.0}") private String version;
그래서 property는 그냥 상수처럼 미리 작성해두고 가져다가 사용하는 개념인가?
YAML Property
- YAML : "YAML은 마크업 언어가 아니다 (YAML Ain't Markup Language)” 라는 재귀적인 문장의 약어
- 원래 뜻은 “또 다른 마크업 언어 (Yet Another Markup Language)”였으나, YAML의 핵심은 문서 마크업이 아닌 데이터 중심에 있다는 것을 보여주기 위해 변경되었다.
- 오늘날 XML과 JSON이 데이터 직렬화에 주로 쓰이기 시작하면서, 많은 사람들이 YAML을 '가벼운 마크업 언어'로 사용하려 하고 있다.
- 특히 Spring 에서는 Property 작성에 YAML을 사용할 수 있다.
kdt: version: "v1.0" minimum-order-amout: 1 support-vendors: - a - b - c - d # multiline 표현 방법 # 아래처럼 '-' 를 붙이면 마지막에 줄바꿈 미포함 description: |- line 1 hello world line 2 xxxx line 3
@PropertySource(value = "application.yaml" , factory = YamlPropertiesFactory.class) // 이게 있어야 @ConfigurationProperties를 쓸 수 있다 @EnableConfigurationProperties
- SpringFramework의 @PropertySource는 YAML을 지원하지 않기 때문에, Property Source Factory를 구현해서 사용해야 한다.
- SpringBoot는 YAML을 지원하지만 SpringFramework는 지원하지 않는다.
public class YamlPropertiesFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String s, EncodedResource encodedResource encodedResource) throws IOException { var yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean(); yamlPropertiesFactoryBean.setResources(encodedResource.getResource()); var properties = yamlPropertiesFactoryBean.getObject(); return new PropertiesPropertySource (encodedResource.getResource.getFilename(), properties); } }
@Configuration @ConfigurationProperties(prefix = "kdt) // 위 Annotation을 사용하면 @ Value Annotation을 사용하지 않아도 된다. public class OrderProperties implements InitializingBean { private String version; private int minimumOrderAmount; private List<String> supportVendors' private String description; }
Spring Profile
Profile이란?
특정한 특징이나 공통점을 찾아서 그룹지어 놓은 것을 의미- 애플리케이션 설정 일부를 분리하여 특정 환경에서만 사용 가능하게 해 준다.
- Spring에서는 설정이나 Bean 같은 것을 그룹화하여 하나의 프로파일로 정의하고, 정의된 여러개의 프로파일 중 하나를 선택하여 어플리케이션을 구동시킬 수 있다.
- DB 접속 정보를 staging이라는 프로파일로 만들어 둘 수도 있고, 운영/로컬 환경별로 profile을 생성해 둔 다음(로컬 환경변수들의 모음 등을) 개발 환경이 바뀔 때 마다 필요한 프로파일을 어플리케이션에 지정하여 환경 설정을 편리하게 할 수도 있다.
- Profile을 이용하면 여러 Bean 정의들이 특정 Profile에서만 동작하게 할 수도 있고, 특정 Property 들을 특정 Profile로 정의해서 해당 Profile이 액티브 상태일때 적용되게 할 수도 있다.
강의 코드에서 사용된 Qualifier란 무엇인가? : Profile 지정자
@Profile("local") @Profile("dev")
var environment = application.getEnvironment(); environment.setActiveProfiles("dev"); // 기존 설정(default Profile이 읽히던)을 바꿨으므로 refresh 해 주었다 applicationContext.refresh();
# local 환경에서만 작동하는 property spring.config.activate.on-profile: local kdt: version: "v1.0.0" --- # 이걸 사용하면 하나의 yaml 파일에 여러개의 작동하는 yaml을 작성할 수 있다. # dev 환경에서만 작동하는 property spring.config.activate.on-profile: dev kdt: version: "v1.0.1"
- SpringBoot 환경에서는 아래와 같이 Profile을 사용할 수 있다.
var springApplication = new SpringApplication(KdtApplication.class); springApplication.setAdditionalProfiles("local"); vat applicationContextspringApplication.run(args); var applicationContext = SpringApplication.run(LdtApplication.class, args);
- 혹은 IntelliJ의 SpringBoot Application Launcher에서 Profile을 설정해 줄 수도 있다.
- 우측 상단에서 확인가능 (Run/Debug Configurations)
- yaml 파일을 여러개로 나누어 작성한 다음 파일 이름에서 name을 지정해 줄 수도 있다.
- application.yaml (default)
- application-dev.yaml
- application-local.yaml
@Profile({"default", "local"})
Resource
- 스프링 애플리케이션을 만들다 보면 외부 리스소를 읽어야 하는 경우가 있다.
외부 리소스
이미지파일, 텍스트파일, 암/복호화를 위한 키파일 등- 이런 리소스 파일들은 파일 시스템, classpath, url을 통한 웹에서 다운로드 받는 식으로 가져올 수 있다.
- 리소스 파일을 어디서 받아오는지에 따라 원래는 다른 방식을 사용해야 하지만,
스프링은
Resource
와ResourceLoader
인터페이스를 통해 통합된 하나의 API 를 제공한다.
- 스프링 공식 문서를 보면 다양한 구현체를 볼 수 있다.
- 코드를 통해 스프링에서 어떻게 Resource 를 받아올 수 있는지 살펴보자.
var resource = applicationContext.getResource("application.yaml"); var file = resource.getFile(); var strings = Files.readAllLines(file.toPath()); // 문자열 리스트로 반환됨
Syste.out.println(strings.stream().reduce("", (a, b) -> a + "\n" + b));
var resource1 = applicationContext.getResource("classpath:application.yaml"); var resource2 = applicationContext.getResource("file:test/sample.txt");
var resource3 = applicationContext.getResource("https://stackoverflow.com/"); var readableByteChannel = Channels.newChannel(resouce3.getURL().openStream()); var bufferReader = new BufferReader(Channels.newReader(readableByteChannel, StandardCharsets UTF_8)); // 웹에서 받아온 Resource의 HTML Body를 출력할 수 있다 var contents = bufferReader.lines().collect(Cokkectors.joining("\n")); Ststem.out.println(contents);