2 ๋ถ„ ์†Œ์š”

์Šคํ”„๋ง์˜ Environment ์ถ”์ƒํ™”

image

์Šคํ”„๋ง์˜ Environment ์ถ”์ƒํ™”๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋‘ ๊ฐ€์ง€ ํ™˜๊ฒฝ ์ •๋ณด ๋ชจ๋ธ์ธ profile๊ณผ properties ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

์ž๋™ ๊ตฌ์„ฑ ์ •๋ณด์˜ ์ผ๋ถ€ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์„ค์ •ํ•ด์•ผํ•  ๋•Œ Environment๋ฅผ ํ†ตํ•ด์„œ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ฐ€์ ธ์™€ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ปค์Šคํ†ฐ ๋นˆ ๋“ฑ๋ก์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋น„ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž๋™ ๊ตฌ์„ฑ์˜ ๋””ํดํŠธ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

ํ”„๋กœํผํ‹ฐ ์ •๋ณด๋Š” ์‹œ์Šคํ…œ ํ”„๋กœํผํ‹ฐ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ์„œ๋ธ”๋ฆฟ ํŒŒ๋ผ๋ฏธํ„ฐ, JNDI ๋“ฑ์—์„œ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ์„œ ๊ฐ€์ ธ์˜จ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ @PropertySource๋กœ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋Œ€์ƒ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ application.properties, application.xml, application.yml ๋“ฑ์˜ ํŒŒ์ผ์—์„œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฝ์–ด์˜ค๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

์ž๋™ ๊ตฌ์„ฑ์— Environment ํ”„๋กœํผํ‹ฐ ์ ์šฉ

์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ดˆ๊ธฐํ™” ์ž‘์—…์ด ๋๋‚˜๊ณ  ๋‚˜๋ฉด ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ๋•Œ ApplicationRunner ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์˜ค๋ธŒ์ ํŠธ ๋˜๋Š” ๋žŒ๋‹ค์‹์„ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ๋œ๋‹ค.

@Bean
ApplicationRunner applicationRunner(Environment environment) {
    return args -> {
        String name = environment.getProperty("my.name");
        System.out.println("my.name: " + name);
    };
}

์ž๋™ ๊ตฌ์„ฑ ํด๋ž˜์Šค์˜ ๋ฉ”์†Œ๋“œ์—๋„ Environment๋ฅผ ์ฃผ์ž… ๋ฐ›์•„์„œ ๋นˆ ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•  ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory(Environment env) {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setContextPath(env.getProperty("contextPath"));
    return factory;
}

@Value์™€ PropertySourcesPlaceholderConfigurer

@Value ์• ๋…ธํ…Œ์ด์…˜์€ ์—˜๋ฆฌ๋จผํŠธ๋กœ ์น˜ํ™˜์ž(placeholder)๋ฅผ ์ง€์ •ํ•˜๊ณ  ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™”์‹œ ํ”„๋กœํผํ‹ฐ ๊ฐ’์œผ๋กœ ์ด๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.

@Value์˜ ์น˜ํ™˜์ž๋ฅผ ํ”„๋กœํผํ‹ฐ ๊ฐ’์œผ๋กœ ๊ต์ฒดํ•˜๋ ค๋ฉด PropertySourcesPlaceholderConfigurer ํƒ€์ž…์˜ ๋นˆ์„ ๋“ฑ๋กํ•ด์ค˜์•ผ ํ•œ๋‹ค. PropertySourcesPlaceholderConfigurer๋Š” ๋นˆ ํŒฉํ† ๋ฆฌ์˜ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋กœ ๋™์ž‘ํ•ด์„œ ์ดˆ๊ธฐ ๊ตฌ์„ฑ ์ •๋ณด์—์„œ ์น˜ํ™˜์ž๋ฅผ ์ฐพ์•„์„œ ๊ต์ฒดํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ด๋‹นํ•œ๋‹ค.

PropertySourcesPlaceholderConfigurer๋„ ์ž๋™ ๊ตฌ์„ฑ ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋˜๊ฒŒ ํ•œ๋‹ค.

@MyAutoConfiguration
public class PropertyPlaceholderConfig {
    @Bean PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

ํ”„๋กœํผํ‹ฐ ํด๋ž˜์Šค์˜ ๋ถ„๋ฆฌ

์ž๋™ ๊ตฌ์„ฑ์— ์ ์šฉํ•  ํ”„๋กœํผํ‹ฐ์˜ ๊ฐฏ์ˆ˜๊ฐ€ ๋งŽ์•„์ง€๊ณ  ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋กœ์ง์ด ์ถ”๊ฐ€๋˜์•ผ ํ•œ๋‹ค๋ฉด, ํ”„๋กœํผํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ๋Šฅ์„ ๋ณ„๋„์˜ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๊ธฐ๋ณธ์ ์ธ ํ”„๋กœํผํ‹ฐ ํด๋ž˜์Šค๋Š” ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋‹จ์ˆœํ•œ ํด๋ž˜์Šค๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

public class ServerProperties {
    private String contextPath;
    private int port;

    public String getContextPath() {
        return contextPath;
    }
    public void setContextPath(String contextPath) {
        this.contextPath = contextPath;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
}

์ด ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ์ž๋™ ๊ตฌ์„ฑ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. Environment์—์„œ ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ๊ฐ€์ ธ์™€ ์˜ค๋ธŒ์ ํŠธ์— ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์€ ์Šคํ”„๋ง๋ถ€ํŠธ์˜ Binder ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•˜๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.

@MyAutoConfiguration
public class ServerPropertiesConfig {
    @Bean
    public ServerProperties serverProperties(Environment environment) {
        return Binder.get(environment).bind("", ServerProperties.class).get();
    }
}

ํ”„๋กœํผํ‹ฐ ํด๋ž˜์Šค๋กœ ๋งŒ๋“  ๋นˆ์€ ์ž๋™ ๊ตฌ์„ฑ ๋นˆ์„ ๋งŒ๋“ค ๋•Œ ์ฃผ์ž… ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•œ๋‹ค.

@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory(ServerProperties properties) {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

    factory.setContextPath(properties.getContextPath());
    factory.setPort(properties.getPort());

    return factory;
}

ํ”„๋กœํผํ‹ฐ ๋นˆ์˜ ํ›„์ฒ˜๋ฆฌ๊ธฐ ๋„์ž…

ํ”„๋กœํผํ‹ฐ ํด๋ž˜์Šค๋ฅผ ๋นˆ ๋“ฑ๋ก์„ ์œ„ํ•œ ์ž๋™ ๊ตฌ์„ฑ์„ ๋”ฐ๋กœ ๋งŒ๋“œ๋Š” ํ•„์š”ํ•œ ๊ณณ์—์„œ @Import ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@MyConfigurationProperties ๋ผ๋Š” ๋งˆ์ปค ์• ๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค๊ณ , ์ด๋ฅผ BeanPostProcessor๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋นˆ ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ ํ›„์— ํ›„์ฒ˜๋ฆฌ ์ž‘์—…์„ ์ง„ํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

๋งˆ์ปค ์• ๋…ธํ…Œ์ด์…˜์„ ์ฐพ์•„์„œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ๋งŒ๋“ค๊ณ  ์ž๋™ ๊ตฌ์„ฑ์œผ๋กœ ๋“ฑ๋ก๋˜๊ฒŒ ํ•œ๋‹ค.

@MyAutoConfiguration
public class PropertyPostProcessorConfig {
    @Bean BeanPostProcessor propertyPostProcessor(Environment env) {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                MyConfigurationProperties annotation = findAnnotation(bean.getClass(), MyConfigurationProperties.class);
                if (annotation == null) return bean;

                Map<String, Object> attrs = getAnnotationAttributes(annotation);
                String prefix = (String) attrs.get("prefix");

                return Binder.get(env).bindOrCreate(prefix, bean.getClass());
            }
        };
    }
}

๋งˆ์ปค ์• ๋…ธํ…Œ์ด์…˜์— prefix ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ์ง€์ •ํ•˜๊ฒŒ ํ•˜๊ณ , ์ด๋ฅผ ์ด์šฉํ•ด์„œ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„ ์•ž์— ์ ‘๋‘์–ด๋ฅผ ๋ถ™์ด๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
public @interface MyConfigurationProperties {
    String prefix();
}

์• ๋…ธํ…Œ์ด์…˜๊ณผ ImportSelector๋ฅผ ์กฐํ•ฉํ•ด์„œ ์• ๋…ธํ…Œ์ด์…˜์˜ ์—˜๋ฆฌ๋จผํŠธ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•œ ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

public class MyConfigurationPropertiesImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        MultiValueMap<String, Object> attr = importingClassMetadata.getAllAnnotationAttributes(EnableMyConfigurationProperties.class.getName());
        Class propertyClass = (Class) attr.getFirst("value");
        return new String[] {propertyClass.getName()};
    }
}

์ด๋•Œ๋Š” ๊ธฐ๋ณธ ์—˜๋ฆฌ๋จผํŠธ ํƒ€์ž…์„ Class ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•œ๋‹ค.

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ