스프링부트 핵심원리

김파란·2024년 5월 6일

Spring

목록 보기
4/10

1. 자동구성

1). @Conditional

  • 특정 조건에서만 해당 기능을 활성화 할 수 있도록 하는 것
@Slf4j
public class MemoryCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String memory = context.getEnvironment().getProperty("memory");
        log.info("memory = {}",memory);
        return "on".equals(memory);
    }
}

@Configuration
//@Conditional(MemoryCondition.class) // Condition을 직접 쓰는방법
@ConditionalOnProperty(name = "memory",havingValue = "on") // Condition없이 환경정보로 작동
public class MemoryConfig {

    @Bean
    public MemoryController memoryController(){
        return new MemoryController(memoryFinder());
    }

    @Bean
    public MemoryFinder memoryFinder(){
        return new MemoryFinder();
    }
}

2). ImportSelector

  • 정적인 방법: @Import(클래스)
  • 동적인 방법: @Import(ImportSelector)
public class HelloImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"hello.selector.HelloConfig"};
    }
}

@Test
    void staticConfig(){
        AnnotationConfigApplicationContext ac = 
        new AnnotationConfigApplicationContext(StaticConfig.class);
        
        HelloBean bean = ac.getBean(HelloBean.class);

    }

    @Test
    void selectorConfig(){
        AnnotationConfigApplicationContext ac = 
        new AnnotationConfigApplicationContext(SelectorConfig.class);
        
        HelloBean bean = ac.getBean(HelloBean.class);
    }

    @Configuration
    @Import(HelloImportSelector.class)
    public static class SelectorConfig{

    }

    @Configuration
    @Import(HelloConfig.class)
    public static class StaticConfig{

    }

3. 외부설정

1). 외부 설정 방법

  1. OS 환경변수: 해당 OS를 사용하는 모든 프로세스에서 사용
  2. 자바 시스템 속성: 자바에서 지원하는 외부설정, 해당 JVM안에서 사용
  3. 자바 커맨드 라인 인수: main(args) 메서드에서 사용
  4. 외부파일: 프로그램에서 외부 파일을 직접 읽어서 사용
	// os 환경변수                                                           
	Map<String, String> envMap = System.getenv();
        
	//DBURL=dev.db.com 개발서버
	//DBURL=prod.db.com 운영서버
	String dburl = System.getenv("DBURL");

	// 자바 시스템 속성
	Properties properties = System.getProperties();

	String url = System.getProperty("url");
	String username = System.getProperty("username");
	String password = System.getProperty("password");
    
    // 커맨드 라인 옵션 인수
    // 빌드할때 --url=devdb --username=dev_user 이런식으로 하면 키밸류 형식으로 받을수 있다
    ApplicationArguments appArgs = new DefaultApplicationArguments(args);
    Set<String> optionNames = appArgs.getOptionNames();
    List<String> username = appArgs.getOptionValues("username");
    
    //  스프링빈 커맨드 라인 옵션 인수
    @Slf4j
	@Component
	public class CommandLineBean {
    
    private final ApplicationArguments arguments;

    public CommandLineBean(ApplicationArguments arguments) {
        this.arguments = arguments;
    }

2). Environment

  • 이 방법들 모두 키밸류 형식으로 읽는것이다. 그래서 추상화를 이용했다
  • 모든 외부설정을 Environment를 이용한다
@Slf4j
@Component
public class EnvironmentCheck {

    private final Environment env;

    public EnvironmentCheck(Environment env) {
        this.env = env;
    }

    @PostConstruct
    public void init() {
        String url = env.getProperty("url");
        String username = env.getProperty("username");
    }

3). 파일로 설정

// 내부 파일 분리
application-dev.properties
application-prod.properties
만들어서 Run/Debug Configurations에서 --spring.profiles.active=dev 넣고 실행

// 내부 파일 합체
spring.config.activate.on-profile=dev
url=prod.db.com
username=prod_user
password=prod_pw
#---  >> 이걸로 구분
spring.config.activate.on-profile=prod
url=dev.db.com
username=dev_user
password=dev_pw

//똑같이 Run/Debug Configurations에서 --spring.profiles.active=dev 넣고 실행

(1). 우선순위

  • 위에서 아래로 순서대로 읽으면서 값을 설정한다. 이때 기존 데이터가 있으면 덮어쓴다
  • 만약 dev,prod둘다 있다고 치면 디폴트 값을 저장했다가 dev 값들로 바껴버리고 최종적으로 prod값들로 변경된다
url=local.db.com
username=local_user
password=local_pw
#---
spring.config.activate.on-profile=dev
url=prod.db.com
username=prod_user
password=prod_pw
#---
spring.config.activate.on-profile=prod
url=dev.db.com
username=dev_user
password=dev_pw
#---
url=hello.db.com

// 이렇게 된다면 무조건 마지막 url은 hello.db.com으로 된다
// username과 password는 변경되지 않기때문에 profile로 설정된 것들로 된다

4. 외부설정 읽기

1). 스프링이 지원하는 다양한 외부 설정 조회 방법

  1. Environment
  2. @Value
  3. @ConfigurationProperties
// properties
my.datasource.url=local.db.com
my.datasource.username=username
my.datasource.password=password
my.datasource.etc.max-connection=1
my.datasource.etc.timeout=3500mx
my.datasource.etc.options=CACHE,ADMIN

// Environment
String url = env.getProperty("my.datasource.url");
Integer maxConnection = env.getProperty("my.datasource.etc.max-connection", Integer.class);
Duration timeout = env.getProperty("my.datsource.etc.timeout", Duration.class);

// @Value
 	@Value("${my.datasource.url}")
    private String url;
    @Value("${my.datasource.username}")
    private String username;
    @Value("${my.datasource.password}")
    private String password;
    @Value("${my.datasource.etc.maxConnection}")
    private int maxConnection;
    @Value("${my.datasource.etc.timeout}")
    private Duration timeout;
    @Value("${my.datasource.etc.options}")
    private List<String> options;
    
    // 디폴트 값도 설정가능
    @Value("${my.datasource.etc.maxConnection:2}") // 이렇게 :2 하면 디폴트로 2설정
    
  • @ConfigurationProperties => Validation, Default 사용가능
    // @ConfigurationProperties
    
@Data
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertyV1 {
    private String url;
    private String username;
    private String password;
    private Etc etc;

    @Data
    public static class Etc{
        private int maxConnection;
        private Duration timeout;
        private List<String> options = new ArrayList<>();
    }
}

@Slf4j
@EnableConfigurationProperties(MyDataSourcePropertyV1.class) // 이렇게 또 설정하면 귀찮으니까
public class MyDataSourceConfigV1 {
    
    private final MyDataSourcePropertyV1 properties;

    public MyDataSourceConfigV1(MyDataSourcePropertyV1 properties) {
        this.properties = properties;
    }
    
    @Bean
    public MyDataSource dataSource(){
        return new MyDataSource(properties.getUrl(),properties.getUsername(),...)
    }
}
// 이런식으로 자동으로 스캔도 가능하게 해줌
@SpringBootApplication
@ConfigurationPropertiesScan
public class ExternalReadApplication {

    public static void main(String[] args) {
        SpringApplication.run(ExternalReadApplication.class, args);
    }

}

// 생성자로 주입받는 방법
@Getter
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertyV2 {
	@NotEmpty
    private String url;
    private String username;
    private String password;
    private Etc etc;

    public MyDataSourcePropertyV2(String url, String username, String password, Etc etc) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.etc = etc;
    }

    @Getter
    public static class Etc{

        private int maxConnection;
        private Duration timeout;
        private List<String> options = new ArrayList<>();
		// Default를 이용할 수도 있음
        public Etc(int maxConnection, Duration timeout, @DefaultValue("default") List<String> options) {
            this.maxConnection = maxConnection;
            this.timeout = timeout;
            this.options = options;
        }
    }
}

2). @Profile

  • 스프링 빈 등록되는 시점에 profile등록
  • Spring.config.activate.on-profile: prod 똑같이 profile로 된걸 쓴다
@Slf4j
@Configuration
public class PayConfig {

    @Bean
    @Profile("default")
    public LocalPayClinet localPayClinet(){
        return new LocalPayClient();
    }

    @Bean
    @Profile("prod")
    public ProdPayClinet localPayClinet(){
        return new ProdPayClient();
    }
}

0개의 댓글