์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒฑ Spring @Conditional์„ ์ด์šฉํ•œ ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก ๐ŸŒฑ

๐ŸŒฑ Spring @Conditional์„ ์ด์šฉํ•œ ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก ๐ŸŒฑ

์Šคํ”„๋ง์˜ ๋งˆ๋ฒ• ๊ฐ™์€ ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก ๊ธฐ๋Šฅ์„ ํ•จ๊ป˜ ์•Œ์•„๋ณด์ž!

์•ˆ๋…•! ์˜ค๋Š˜์€ Spring Framework์—์„œ ์ •๋ง ์œ ์šฉํ•˜๊ฒŒ ์“ฐ์ด๋Š” @Conditional ์• ๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ํ•จ๊ป˜ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ์ด ๊ธฐ๋Šฅ์€ ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋นˆ์„ ๋“ฑ๋กํ• ์ง€ ๋ง์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์Šคํ”„๋ง์˜ ๊ฟ€ ๊ธฐ๋Šฅ์ด์ง€! ๐Ÿ˜Ž

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•˜๋‹ค ๋ณด๋ฉด "์ด๋Ÿฐ ์ƒํ™ฉ์—์„  ์ด ๋นˆ์„, ์ €๋Ÿฐ ์ƒํ™ฉ์—์„  ์ € ๋นˆ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์–ด!"๋ผ๋Š” ์ƒ๊ฐ ํ•œ ๋ฒˆ์ฏค ํ•ด๋ดค์„ ๊ฑฐ์•ผ. ๋ฐ”๋กœ ๊ทธ๋Ÿฐ ์ƒํ™ฉ์—์„œ @Conditional์ด ๋น›์„ ๋ฐœํ•œ๋‹ค๊ณ ! ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ƒํ™ฉ์— ๋งž๋Š” ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์ฐพ์•„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์Šคํ”„๋ง์—์„œ๋„ ์ƒํ™ฉ์— ๋งž๋Š” ๋นˆ์„ ์œ ์—ฐํ•˜๊ฒŒ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ‘จโ€๐Ÿ’ป

๐Ÿค” @Conditional์ด ๋ญ๊ธธ๋ž˜?

@Conditional์€ Spring 4.0๋ถ€ํ„ฐ ๋„์ž…๋œ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ, ํŠน์ • ์กฐ๊ฑด์ด ๋งŒ์กฑ๋  ๋•Œ๋งŒ ๋นˆ์„ ๋“ฑ๋กํ•˜๋„๋ก ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์•ผ. ์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์œ ์—ฐ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํฌ๊ฒŒ ๋†’์—ฌ์ฃผ๊ฑฐ๋“ !

์˜ˆ๋ฅผ ๋“ค์–ด ๋ด:

  1. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋‹ค๋ฅธ ๋นˆ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  2. ํŠน์ • ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ์„ ๋•Œ๋งŒ ๋นˆ์„ ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  3. ํŠน์ • ํ”„๋กœํผํ‹ฐ ๊ฐ’์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์„ ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  4. ์šด์˜์ฒด์ œ๋‚˜ JVM ๋ฒ„์ „์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์„ ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์„ ๋•Œ

์ด๋Ÿฐ ์ƒํ™ฉ๋“ค, ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ• ๊นŒ? ๋ฐ”๋กœ @Conditional์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ! ๐Ÿ˜„

@Conditional์˜ ๋งˆ๋ฒ• โœจ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ํŠน์ • ํด๋ž˜์Šค ์กด์žฌ ์šด์˜ ํ™˜๊ฒฝ ํŠน์ • ํ”„๋กœํผํ‹ฐ ๊ฐ’ Spring ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์ด ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค!

๐Ÿš€ @Conditional ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

@Conditional์€ Condition ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„. ์ด ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋‹จ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ์–ด:

public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

matches ๋ฉ”์„œ๋“œ๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋นˆ์ด ๋“ฑ๋ก๋˜๊ณ , false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋นˆ์ด ๋“ฑ๋ก๋˜์ง€ ์•Š์•„. ์ •๋ง ๊ฐ„๋‹จํ•˜์ง€? ๐Ÿ˜Š

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž:

public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").toLowerCase().contains("linux");
    }
}

public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").toLowerCase().contains("windows");
    }
}

์ด์ œ ์ด Condition ๊ตฌํ˜„์ฒด๋“ค์„ ์‚ฌ์šฉํ•ด์„œ ์กฐ๊ฑด๋ถ€๋กœ ๋นˆ์„ ๋“ฑ๋กํ•ด๋ณด์ž:

@Configuration
public class AppConfig {
    
    @Bean
    @Conditional(LinuxCondition.class)
    public CommandService linuxCommandService() {
        return new LinuxCommandService();
    }
    
    @Bean
    @Conditional(WindowsCondition.class)
    public CommandService windowsCommandService() {
        return new WindowsCommandService();
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์—์„œ๋Š” LinuxCommandService ๋นˆ์ด, ์œˆ๋„์šฐ ํ™˜๊ฒฝ์—์„œ๋Š” WindowsCommandService ๋นˆ์ด ๋“ฑ๋ก๋ผ. ์šด์˜์ฒด์ œ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ตฌํ˜„์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฑฐ์ง€! ๐Ÿ‘

๐ŸŒˆ Spring Boot์˜ ๋‹ค์–‘ํ•œ @Conditional ์• ๋…ธํ…Œ์ด์…˜๋“ค

Spring Boot๋Š” @Conditional์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ ๋” ํŽธ๋ฆฌํ•œ ์• ๋…ธํ…Œ์ด์…˜๋“ค์„ ์ œ๊ณตํ•ด. ์ด๊ฒƒ๋“ค์„ ๋ฉ”ํƒ€ ์• ๋…ธํ…Œ์ด์…˜์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ, ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์กฐ๊ฑด๋“ค์„ ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•ด๋†“์€ ๊ฑฐ์•ผ. ๐Ÿ˜Ž

  1. @ConditionalOnClass: ํŠน์ • ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ์„ ๋•Œ ๋นˆ ๋“ฑ๋ก
  2. @ConditionalOnMissingClass: ํŠน์ • ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์—†์„ ๋•Œ ๋นˆ ๋“ฑ๋ก
  3. @ConditionalOnBean: ํŠน์ • ๋นˆ์ด ์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด ์žˆ์„ ๋•Œ ๋นˆ ๋“ฑ๋ก
  4. @ConditionalOnMissingBean: ํŠน์ • ๋นˆ์ด ๋“ฑ๋ก๋˜์–ด ์žˆ์ง€ ์•Š์„ ๋•Œ ๋นˆ ๋“ฑ๋ก
  5. @ConditionalOnProperty: ํŠน์ • ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ง€์ •๋œ ๊ฐ’์„ ๊ฐ€์งˆ ๋•Œ ๋นˆ ๋“ฑ๋ก
  6. @ConditionalOnResource: ํŠน์ • ๋ฆฌ์†Œ์Šค๊ฐ€ ์กด์žฌํ•  ๋•Œ ๋นˆ ๋“ฑ๋ก
  7. @ConditionalOnWebApplication: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ผ ๋•Œ ๋นˆ ๋“ฑ๋ก
  8. @ConditionalOnNotWebApplication: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋‹ ๋•Œ ๋นˆ ๋“ฑ๋ก
  9. @ConditionalOnExpression: SpEL ํ‘œํ˜„์‹์ด true์ผ ๋•Œ ๋นˆ ๋“ฑ๋ก
  10. @ConditionalOnJava: ํŠน์ • ์ž๋ฐ” ๋ฒ„์ „์ผ ๋•Œ ๋นˆ ๋“ฑ๋ก

์ด ์• ๋…ธํ…Œ์ด์…˜๋“ค์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ Condition ๊ตฌํ˜„์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์ง€ ์•Š์•„๋„ ๋ผ์„œ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•ด์ง„๋‹ค๊ตฌ! ๐Ÿ‘

Spring Boot์˜ @Conditional ๋ณ€ํ˜•๋“ค ๐Ÿงฉ @Conditional @ConditionalOnClass @ConditionalOnBean @ConditionalOnProperty @ConditionalOnWebApplication @ConditionalOnResource @ConditionalOnExpression ๋” ๊ฐ„ํŽธํ•˜๊ฒŒ ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก์„ ํ•  ์ˆ˜ ์žˆ์–ด์š”!

๐Ÿ” ์‹ค์ „ ์˜ˆ์ œ๋กœ ์•Œ์•„๋ณด๋Š” @Conditional

1. @ConditionalOnProperty ์‚ฌ์šฉ ์˜ˆ์ œ

ํŠน์ • ํ”„๋กœํผํ‹ฐ ๊ฐ’์— ๋”ฐ๋ผ ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ:

@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConditionalOnProperty(name = "datasource.type", havingValue = "mysql")
    public DataSource mysqlDataSource() {
        // MySQL DataSource ์„ค์ •
        return new MysqlDataSource();
    }
    
    @Bean
    @ConditionalOnProperty(name = "datasource.type", havingValue = "postgresql")
    public DataSource postgresqlDataSource() {
        // PostgreSQL DataSource ์„ค์ •
        return new PGSimpleDataSource();
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด application.properties๋‚˜ application.yml์— ์„ค์ •๋œ datasource.type ๊ฐ’์— ๋”ฐ๋ผ ๋‹ค๋ฅธ DataSource ๋นˆ์ด ๋“ฑ๋ก๋ผ. ์ •๋ง ํŽธ๋ฆฌํ•˜์ง€? ๐Ÿ™Œ

2. @ConditionalOnMissingBean ์‚ฌ์šฉ ์˜ˆ์ œ

ํŠน์ • ๋นˆ์ด ์—†์„ ๋•Œ๋งŒ ๊ธฐ๋ณธ ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ์˜ˆ์ œ์•ผ:

@Configuration
public class SecurityConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public PasswordEncoder passwordEncoder() {
        // ๊ธฐ๋ณธ PasswordEncoder ์ œ๊ณต
        return new BCryptPasswordEncoder();
    }
}

์ด ์ฝ”๋“œ๋Š” ๋‹ค๋ฅธ ๊ณณ์—์„œ PasswordEncoder ํƒ€์ž…์˜ ๋นˆ์„ ๋“ฑ๋กํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋งŒ ๊ธฐ๋ณธ BCryptPasswordEncoder๋ฅผ ๋“ฑ๋กํ•ด. ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•˜๋ฉด์„œ๋„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ํ—ˆ์šฉํ•˜๋Š” ํŒจํ„ด์ด์ง€! ๐Ÿ‘จโ€๐ŸŽจ

3. @ConditionalOnClass์™€ @ConditionalOnMissingClass ์‚ฌ์šฉ ์˜ˆ์ œ

ํŠน์ • ํด๋ž˜์Šค์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ์˜ˆ์ œ:

@Configuration
public class CacheConfig {
    
    @Bean
    @ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
    public CacheManager redisCacheManager() {
        // Redis ๊ธฐ๋ฐ˜ ์บ์‹œ ๋งค๋‹ˆ์ €
        return new RedisCacheManager();
    }
    
    @Bean
    @ConditionalOnMissingClass("org.springframework.data.redis.core.RedisTemplate")
    public CacheManager simpleCacheManager() {
        // ๊ธฐ๋ณธ ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ๋งค๋‹ˆ์ €
        return new ConcurrentMapCacheManager();
    }
}

์ด ์˜ˆ์ œ์—์„œ๋Š” Redis ๊ด€๋ จ ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ์œผ๋ฉด Redis ๊ธฐ๋ฐ˜ ์บ์‹œ ๋งค๋‹ˆ์ €๋ฅผ, ์—†์œผ๋ฉด ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ๋งค๋‹ˆ์ €๋ฅผ ์‚ฌ์šฉํ•ด. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ์ ์ ˆํ•œ ๋นˆ์ด ๋“ฑ๋ก๋˜๋Š” ๊ฑฐ์ง€! ๐Ÿง 

๐Ÿ› ๏ธ ๋‚˜๋งŒ์˜ ์ปค์Šคํ…€ @Conditional ๋งŒ๋“ค๊ธฐ

Spring Boot๊ฐ€ ์ œ๊ณตํ•˜๋Š” @Conditional ๋ณ€ํ˜•๋“ค์ด ๋งŽ์ง€๋งŒ, ๋•Œ๋กœ๋Š” ๋” ํŠน๋ณ„ํ•œ ์กฐ๊ฑด์ด ํ•„์š”ํ•  ์ˆ˜๋„ ์žˆ์–ด. ๊ทธ๋Ÿด ๋• ์ง์ ‘ ๋งŒ๋“ค๋ฉด ๋ผ! ๐Ÿ˜„

์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ์‹œ๊ฐ„๋Œ€์—๋งŒ ํ™œ์„ฑํ™”๋˜๋Š” ๋นˆ์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๊ณ  ํ•ด๋ณด์ž:

// 1. Condition ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
public class TimeOfDayCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnTimeOfDay.class.getName());
        int startHour = (int) attributes.get("startHour");
        int endHour = (int) attributes.get("endHour");
        
        LocalTime now = LocalTime.now();
        int currentHour = now.getHour();
        
        return currentHour >= startHour && currentHour < endHour;
    }
}

// 2. ์ปค์Šคํ…€ ์• ๋…ธํ…Œ์ด์…˜ ์ •์˜
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(TimeOfDayCondition.class)
public @interface ConditionalOnTimeOfDay {
    int startHour() default 0;
    int endHour() default 24;
}

// 3. ์‚ฌ์šฉ ์˜ˆ์ œ
@Configuration
public class SchedulerConfig {
    
    @Bean
    @ConditionalOnTimeOfDay(startHour = 9, endHour = 18)
    public TaskScheduler businessHoursScheduler() {
        // ์—…๋ฌด ์‹œ๊ฐ„ ์ค‘์—๋งŒ ํ™œ์„ฑํ™”๋˜๋Š” ์Šค์ผ€์ค„๋Ÿฌ
        return new ConcurrentTaskScheduler();
    }
    
    @Bean
    @ConditionalOnTimeOfDay(startHour = 18, endHour = 9)
    public TaskScheduler afterHoursScheduler() {
        // ์—…๋ฌด ์‹œ๊ฐ„ ์™ธ์—๋งŒ ํ™œ์„ฑํ™”๋˜๋Š” ์Šค์ผ€์ค„๋Ÿฌ
        return new ConcurrentTaskScheduler();
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹œ๊ฐ„๋Œ€์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ํ™œ์„ฑํ™”๋ผ! ์žฌ๋Šฅ๋„ท์—์„œ ์‹œ๊ฐ„๋Œ€๋ณ„๋กœ ๋‹ค๋ฅธ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋„ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์ง€. ๐Ÿ˜‰

์ปค์Šคํ…€ @Conditional ๋งŒ๋“ค๊ธฐ ๊ณผ์ • ๐Ÿ”„ 1๋‹จ๊ณ„ Condition ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ matches() ๋ฉ”์„œ๋“œ์— ์กฐ๊ฑด ๋กœ์ง ๊ตฌํ˜„ 2๋‹จ๊ณ„ ์ปค์Šคํ…€ ์• ๋…ธํ…Œ์ด์…˜ ์ •์˜ @Conditional๊ณผ ๊ตฌํ˜„์ฒด ์—ฐ๊ฒฐ 3๋‹จ๊ณ„ ์ปค์Šคํ…€ ์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ @Bean ๋ฉ”์„œ๋“œ๋‚˜ @Component ํด๋ž˜์Šค์— ์ ์šฉ ๋‚˜๋งŒ์˜ ์กฐ๊ฑด๋ถ€ ๋กœ์ง์œผ๋กœ ๋นˆ ๋“ฑ๋ก์„ ์ œ์–ดํ•ด๋ณด์„ธ์š”!

๐Ÿ’ก @Conditional ์‚ฌ์šฉ ์‹œ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

@Conditional์„ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ! ๐Ÿค“

  1. ์กฐ๊ฑด ๋กœ์ง์€ ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ
    matches() ๋ฉ”์„œ๋“œ ๋‚ด์˜ ๋กœ์ง์ด ๋ณต์žกํ•ด์ง€๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด. ๊ฐ€๋Šฅํ•œ ํ•œ ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๊ฒŒ ์ข‹์•„!
  2. ์กฐ๊ฑด๋ถ€ ๋นˆ์˜ ์˜์กด์„ฑ ์ฃผ์˜ํ•˜๊ธฐ
    ์กฐ๊ฑด๋ถ€๋กœ ๋“ฑ๋ก๋˜๋Š” ๋นˆ์— ์˜์กดํ•˜๋Š” ๋‹ค๋ฅธ ๋นˆ์ด ์žˆ๋‹ค๋ฉด, ๊ทธ ๋นˆ๋„ ์กฐ๊ฑด๋ถ€๋กœ ๋“ฑ๋ก๋˜์–ด์•ผ ํ•  ์ˆ˜ ์žˆ์–ด. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด NoSuchBeanDefinitionException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.
  3. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ
    ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก์€ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ๊นŒ๋‹ค๋กœ์šธ ์ˆ˜ ์žˆ์–ด. ๋‹ค์–‘ํ•œ ์กฐ๊ฑด์—์„œ ๋นˆ์ด ์ œ๋Œ€๋กœ ๋“ฑ๋ก๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๊ผญ ์ž‘์„ฑํ•˜์ž!
  4. ๋ฌธ์„œํ™”ํ•˜๊ธฐ
    ์–ด๋–ค ์กฐ๊ฑด์—์„œ ์–ด๋–ค ๋นˆ์ด ๋“ฑ๋ก๋˜๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฌธ์„œํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด. ๋‚˜์ค‘์— ์ฝ”๋“œ๋ฅผ ์œ ์ง€๋ณด์ˆ˜ํ•  ๋•Œ ํฐ ๋„์›€์ด ๋  ๊ฑฐ์•ผ.
  5. Spring Boot์˜ ์ž๋™ ๊ตฌ์„ฑ ์ดํ•ดํ•˜๊ธฐ
    Spring Boot๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋งŽ์€ @Conditional์„ ์‚ฌ์šฉํ•ด. ์ด ๋™์ž‘ ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ฉด ์ž์‹ ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋„ ๋” ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

์ด๋Ÿฐ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ๋”ฐ๋ฅด๋ฉด ๋” ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ณ  ์•ˆ์ •์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์ด ์ œ๊ณตํ•˜๋Š” ๊ณ ํ’ˆ์งˆ ์„œ๋น„์Šค์ฒ˜๋Ÿผ ๋ง์ด์•ผ! ๐Ÿš€

๐ŸŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ์˜ @Conditional ํ™œ์šฉ ์‚ฌ๋ก€

1. ํ”„๋กœํ•„๋ณ„ ๊ตฌ์„ฑ ๊ด€๋ฆฌ

๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ, ์šด์˜ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ๊ฒฝ์šฐ:

@Configuration
public class DatabaseConfig {
    
    @Bean
    @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }
    
    @Bean
    @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "prod")
    public DataSource prodDataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://production-db:3306/myapp");
        config.setUsername("prod_user");
        config.setPassword("prod_password");
        return new HikariDataSource(config);
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ์†Œ์Šค๊ฐ€ ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋ผ! ๐Ÿ‘จโ€๐Ÿ’ป

2. ๊ธฐ๋Šฅ ํ† ๊ธ€(Feature Toggle) ๊ตฌํ˜„

ํŠน์ • ๊ธฐ๋Šฅ์„ ๋™์ ์œผ๋กœ ์ผœ๊ณ  ๋Œ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ ํ† ๊ธ€ ํŒจํ„ด:

@Configuration
public class FeatureConfig {
    
    @Bean
    @ConditionalOnProperty(name = "features.new-recommendation-engine", havingValue = "true")
    public RecommendationEngine newRecommendationEngine() {
        return new AIBasedRecommendationEngine();
    }
    
    @Bean
    @ConditionalOnProperty(name = "features.new-recommendation-engine", havingValue = "false", matchIfMissing = true)
    public RecommendationEngine legacyRecommendationEngine() {
        return new RuleBasedRecommendationEngine();
    }
}

์ด ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ๋ฐฐํฌํ•˜์ง€ ์•Š๊ณ ๋„ ๊ธฐ๋Šฅ์„ ์ผœ๊ณ  ๋Œ ์ˆ˜ ์žˆ์–ด. ์„ค์ • ํŒŒ์ผ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๊ฐ’๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋˜๋‹ˆ๊นŒ! ๐Ÿ”„

3. ์„ ํƒ์  ์˜์กด์„ฑ ์ฒ˜๋ฆฌ

์„ ํƒ์  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒฝ์šฐ:

@Configuration
public class MonitoringConfig {
    
    @Bean
    @ConditionalOnClass(name = "io.micrometer.prometheus.PrometheusConfig")
    public MeterRegistry prometheusMeterRegistry() {
        return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
    }
    
    @Bean
    @ConditionalOnClass(name = "io.micrometer.influx.InfluxConfig")
    @ConditionalOnMissingBean(MeterRegistry.class)
    public MeterRegistry influxMeterRegistry() {
        return new InfluxMeterRegistry(InfluxConfig.DEFAULT, Clock.SYSTEM);
    }
    
    @Bean
    @ConditionalOnMissingBean(MeterRegistry.class)
    public MeterRegistry simpleMeterRegistry() {
        return new SimpleMeterRegistry();
    }
}

์ด ์˜ˆ์ œ์—์„œ๋Š” ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ MeterRegistry๋ฅผ ๋“ฑ๋กํ•ด. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™”๋˜๋Š” ๊ฑฐ์ง€! ๐Ÿงฉ

๐Ÿ”ฅ ๊ณ ๊ธ‰ @Conditional ํ…Œํฌ๋‹‰

1. ์—ฌ๋Ÿฌ ์กฐ๊ฑด ์กฐํ•ฉํ•˜๊ธฐ

์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ AND๋‚˜ OR๋กœ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

@Configuration
public class AdvancedConfig {
    
    @Bean
    @ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
    @ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
    @ConditionalOnBean(RedisConnectionFactory.class)
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.builder(redisConnectionFactory).build();
    }
}

์ด ์˜ˆ์ œ์—์„œ๋Š” ์„ธ ๊ฐ€์ง€ ์กฐ๊ฑด์ด ๋ชจ๋‘ ์ถฉ์กฑ๋  ๋•Œ๋งŒ redisCacheManager ๋นˆ์ด ๋“ฑ๋ก๋ผ:

  1. cache.enabled ํ”„๋กœํผํ‹ฐ๊ฐ€ true์ผ ๋•Œ
  2. RedisTemplate ํด๋ž˜์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ์„ ๋•Œ
  3. RedisConnectionFactory ํƒ€์ž…์˜ ๋นˆ์ด ์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด ์žˆ์„ ๋•Œ

2. ์ปค์Šคํ…€ ์กฐ๊ฑด ๋กœ์ง์—์„œ ์• ๋…ธํ…Œ์ด์…˜ ์†์„ฑ ์‚ฌ์šฉํ•˜๊ธฐ

์ปค์Šคํ…€ @Conditional์—์„œ ์• ๋…ธํ…Œ์ด์…˜์˜ ์†์„ฑ๊ฐ’์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBeanProperty {
    String beanName();
    String propertyName();
    String expectedValue();
}

public class OnBeanCondition implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnBeanProperty.class.getName());
        String beanName = (String) attributes.get("beanName");
        String propertyName = (String) attributes.get("propertyName");
        String expectedValue = (String) attributes.get("expectedValue");
        
        // ๋นˆ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
        if (!context.getBeanFactory().containsBean(beanName)) {
            return false;
        }
        
        // ๋นˆ์˜ ํ”„๋กœํผํ‹ฐ ๊ฐ’ ํ™•์ธ
        Object bean = context.getBeanFactory().getBean(beanName);
        try {
            BeanWrapper wrapper = new BeanWrapperImpl(bean);
            Object actualValue = wrapper.getPropertyValue(propertyName);
            return expectedValue.equals(String.valueOf(actualValue));
        } catch (Exception e) {
            return false;
        }
    }
}

์ด ์˜ˆ์ œ๋Š” ํŠน์ • ๋นˆ์˜ ํŠน์ • ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ํ™•์ธํ•˜๋Š” ์กฐ๊ฑด์„ ๋งŒ๋“ค์—ˆ์–ด. ์ •๋ง ๊ฐ•๋ ฅํ•˜์ง€? ๐Ÿ’ช

3. ์กฐ๊ฑด๋ถ€ ์ž๋™ ๊ตฌ์„ฑ(Auto-Configuration) ๋งŒ๋“ค๊ธฐ

Spring Boot์˜ ์ž๋™ ๊ตฌ์„ฑ ๋ฐฉ์‹์„ ํ™œ์šฉํ•œ ์˜ˆ์ œ:

@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "app.audit", name = "enabled", havingValue = "true")
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class AuditAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public AuditService auditService(DataSource dataSource) {
        return new JdbcAuditService(dataSource);
    }
    
    @Bean
    @ConditionalOnMissingBean
    public AuditAspect auditAspect(AuditService auditService) {
        return new AuditAspect(auditService);
    }
}

์ด ์˜ˆ์ œ๋Š” ์ž๋™ ๊ตฌ์„ฑ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ํŠน์ • ์กฐ๊ฑด์ด ์ถฉ์กฑ๋  ๋•Œ ๊ฐ์‚ฌ(Audit) ๊ธฐ๋Šฅ์„ ์ž๋™์œผ๋กœ ํ™œ์„ฑํ™”ํ•ด. Spring Boot์˜ ํ™•์žฅ์„ฑ์„ ๋ณด์—ฌ์ฃผ๋Š” ์ข‹์€ ์˜ˆ์‹œ์ง€! ๐ŸŒŸ

โš ๏ธ @Conditional ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

@Conditional์„ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด ๋ช‡ ๊ฐ€์ง€ ํ•จ์ •์— ๋น ์งˆ ์ˆ˜ ์žˆ์–ด. ๋ฏธ๋ฆฌ ์•Œ์•„๋‘๋ฉด ๋„์›€์ด ๋  ๊ฑฐ์•ผ! ๐Ÿšง

  1. ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฌธ์ œ
    ์กฐ๊ฑด๋ถ€ ๋นˆ์ด ๋‹ค๋ฅธ ๋นˆ์— ์˜์กดํ•˜๊ณ , ๊ทธ ๋นˆ์ด ๋‹ค์‹œ ์กฐ๊ฑด๋ถ€ ๋นˆ์— ์˜์กดํ•˜๋ฉด ์ˆœํ™˜ ์˜์กด์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด. ๋นˆ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ์ž˜ ์„ค๊ณ„ํ•ด์•ผ ํ•ด!
  2. ์กฐ๊ฑด ํ‰๊ฐ€ ์‹œ์  ์ดํ•ดํ•˜๊ธฐ
    @Conditional์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ ๋กœ๋”ฉ ์ค‘์— ํ‰๊ฐ€๋ผ. ์ด ์‹œ์ ์—๋Š” ์ผ๋ถ€ ๋ฆฌ์†Œ์Šค๋‚˜ ์‹œ์Šคํ…œ ์ƒํƒœ๊ฐ€ ์•„์ง ์ค€๋น„๋˜์ง€ ์•Š์•˜์„ ์ˆ˜ ์žˆ์–ด. ์ด ์ ์„ ๊ณ ๋ คํ•ด์„œ ์กฐ๊ฑด ๋กœ์ง์„ ์„ค๊ณ„ํ•ด์•ผ ํ•ด.
  3. ๊ณผ๋„ํ•œ ์‚ฌ์šฉ ํ”ผํ•˜๊ธฐ
    ๋„ˆ๋ฌด ๋งŽ์€ @Conditional์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์„ฑ์ด ๋ณต์žกํ•ด์ง€๊ณ  ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ์–ด. ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„!
  4. ํ…Œ์ŠคํŠธ ์–ด๋ ค์›€
    ์กฐ๊ฑด๋ถ€ ๋นˆ์€ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด. ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์กฐ๊ฑด์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ๋งˆ๋ จํ•ด๋‘๋Š” ๊ฒŒ ์ข‹์•„.
  5. ๋””๋ฒ„๊น… ๋ณต์žก์„ฑ
    ๋นˆ์ด ๋“ฑ๋ก๋˜์ง€ ์•Š์•˜์„ ๋•Œ ๊ทธ ์ด์œ ๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด. Spring Boot๋Š” --debug ์˜ต์…˜์„ ์ œ๊ณตํ•ด์„œ ์ž๋™ ๊ตฌ์„ฑ ๋ณด๊ณ ์„œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋‹ˆ ํ™œ์šฉํ•ด๋ด!

์ด๋Ÿฐ ์ฃผ์˜์‚ฌํ•ญ์„ ์—ผ๋‘์— ๋‘๊ณ  @Conditional์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์•ˆ์ •์ ์ด๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๐Ÿ˜Š

๐Ÿ” Spring Boot ๋‚ด๋ถ€์—์„œ์˜ @Conditional ํ™œ์šฉ

Spring Boot๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ @Conditional์„ ๋งค์šฐ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด. ์ด ๋ถ€๋ถ„์„ ์ดํ•ดํ•˜๋ฉด Spring Boot์˜ ์ž๋™ ๊ตฌ์„ฑ(Auto-Configuration) ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋” ์ž˜ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿง 

Spring Boot์˜ ์ž๋™ ๊ตฌ์„ฑ ํด๋ž˜์Šค๋“ค์„ ์‚ดํŽด๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„ ์ด๋Ÿฐ ํŒจํ„ด์„ ๋”ฐ๋ฅด๊ณ  ์žˆ์–ด:

@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(JdbcOperations.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class JdbcTemplateAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean(JdbcOperations.class)
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
    
    @Bean
    @ConditionalOnMissingBean(NamedParameterJdbcOperations.class)
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate(JdbcTemplate jdbcTemplate) {
        return new NamedParameterJdbcTemplate(jdbcTemplate);
    }
}

์ด๋Ÿฐ ํŒจํ„ด์˜ ์žฅ์ ์€:

  1. ํ•„์š”ํ•œ ํด๋ž˜์Šค๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ๊ตฌ์„ฑ์ด ํ™œ์„ฑํ™”๋จ (@ConditionalOnClass)
  2. ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋นˆ์„ ์ •์˜ํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋งŒ ๊ธฐ๋ณธ ๋นˆ์ด ์ œ๊ณต๋จ (@ConditionalOnMissingBean)
  3. ์ž๋™ ๊ตฌ์„ฑ ๊ฐ„์˜ ์ˆœ์„œ๊ฐ€ ๋ช…ํ™•ํžˆ ์ •์˜๋จ (@AutoConfigureAfter)

์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ Spring Boot๋Š” "๊ด€๋ก€์  ๊ตฌ์„ฑ(Convention over Configuration)" ์›์น™์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์–ด. ๊ธฐ๋ณธ๊ฐ’์„ ์ œ๊ณตํ•˜๋ฉด์„œ๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋ฉด ์–ธ์ œ๋“  ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฑฐ์ง€! ๐Ÿ‘

Spring Boot์˜ ์ž๋™ ๊ตฌ์„ฑ์„ ๋” ๊นŠ๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด spring-boot-autoconfigure ๋ชจ๋“ˆ์˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ๋งŽ์€ ์˜๊ฐ์„ ์–ป์„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ“š

Spring Boot ์ž๋™ ๊ตฌ์„ฑ์˜ ๋งˆ๋ฒ• โœจ ์กฐ๊ฑด ํ‰๊ฐ€ @ConditionalOnClass, @ConditionalOnBean, @ConditionalOnProperty, ... ๋นˆ ๋“ฑ๋ก @Bean, @Component, @Service, @Repository, ... ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ ์กฐ๊ฑด์— ๋งž๋Š” ๋นˆ๋“ค๋งŒ ๋“ฑ๋ก๋œ ์ตœ์ ํ™”๋œ ์ปจํ…์ŠคํŠธ

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌ: @Conditional์˜ ํž˜์„ ํ™œ์šฉํ•˜์ž

์ง€๊ธˆ๊นŒ์ง€ Spring์˜ @Conditional ์• ๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ๊นŠ์ด ์•Œ์•„๋ดค์–ด! ์ด ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์œ ์—ฐ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ์ง€. ๐Ÿ˜„

@Conditional์„ ํ™œ์šฉํ•˜๋ฉด:

  1. ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์–ด
  2. ํŠน์ • ์กฐ๊ฑด์ด ์ถฉ์กฑ๋  ๋•Œ๋งŒ ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์–ด
  3. ์„ ํƒ์  ์˜์กด์„ฑ์„ ํšจ๊ณผ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด
  4. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์„ฑ์„ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด
  5. ์‚ฌ์šฉ์ž ์ •์˜ ์กฐ๊ฑด์œผ๋กœ ๋นˆ ๋“ฑ๋ก์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด

Spring๊ณผ Spring Boot๋Š” ์ด๋ฏธ ๋‚ด๋ถ€์ ์œผ๋กœ @Conditional์„ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด. ์ด ํŒจํ„ด์„ ์ดํ•ดํ•˜๊ณ  ํ™œ์šฉํ•˜๋ฉด ๋” ์Šค๋งˆํŠธํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿš€

์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์ฐพ์•„ ํ™œ์šฉํ•˜๋“ฏ์ด, Spring์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด์„œ ๋” ์ข‹์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด์ž! ๐ŸŒŸ

๋‹ค์Œ์— ๋˜ ๋‹ค๋ฅธ Spring์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ๋งŒ๋‚˜์ž! ๐Ÿ‘‹

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.