์ปดํฌ๋„ŒํŠธ ์Šค์บ”

2022. 2. 7. 17:42ใ†Backend/๐ŸŒฟ Spring

1. ์ปดํฌ๋„ŒํŠธ ์Šค์บ”๊ณผ ์˜์กด๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

์ปดํฌ๋„ŒํŠธ ์Šค์บ” ๋“ฑ์žฅ ๋ฐฐ๊ฒฝ

@Bean ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ XML ์˜ ํƒœ๊ทธ ๋“ฑ์„ ํ†ตํ•ด ์„ค์ • ์ •๋ณด์— ์ง์ ‘ ๋“ฑ๋กํ•  ์Šคํ”„๋ง ๋นˆ์„ ๋‚˜์—ดํ–ˆ์ง€๋งŒ, ๋“ฑ๋กํ•  ๋นˆ์ด ์ˆ˜๋ฐฑ๊ฐ€์ง€๊ฐ€ ๋˜๋ฉด ๋ฒˆ๊ฑฐ๋กญ๋‹ค.

@Component Scan

@Component ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋นˆ์„ ๋ชจ์กฐ๋ฆฌ ์ฐพ์•„๋‚ด๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

@Configuration ์€ ์• ์ดˆ์— @Component ๋ฅผ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋Œ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด filter ๋ฅผ ์‚ฌ์šฉ.

@Configuration   // ํ•„ํ„ฐ ํƒ€์ž…์€ annotation ์œผ๋กœ ์ง€์ •
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {
}

๊ธฐ์กด์˜ AppConfig ์™€ ๋‹ฌ๋ฆฌ @Bean ์œผ๋กœ ๋“ฑ๋กํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ AppConfig ์— ์‚ฌ์šฉํ•  ๊ฐ์ฒด๋ฅผ ์ผ์ผ์ด ๋นˆ ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ , ๊ทธ์ € @Component ๋งŒ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค. (ํ›จ์”ฌ ๊ฐ„ํŽธํ•˜๋‹ค!)

์˜์กด๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…

Q. ๊ทธ๋ ‡๋‹ค๋ฉด AppConfig ์—์„œ ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ•œ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์€ ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜?

A. @Autowired ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ๋‹ค.

์ด์ „ AppConfig ํŒŒ์ผ์—์„œ๋Š” ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์„ ์ˆ˜๋™์œผ๋กœ (์ฝ”๋“œ๋กœ) ์ž‘์„ฑํ•˜์˜€์ง€๋งŒ, AutoAppConfig ํŒŒ์ผ์—์„œ๋Š” ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์ด ์ž๋™์œผ๋กœ ์ด๋ค„์ง„๋‹ค. ์–ด๋–ป๊ฒŒ? ๋ฐ”๋กœ @Autowired ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ.

์ปดํฌ๋„ŒํŠธ ์Šค์บ”์œผ๋กœ ์ธํ•ด ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ ํด๋ž˜์Šค๋Š”, ์ž์‹ ์˜ ์ƒ์„ฑ์ž์— @Autowired ๋ฅผ ์ง€์ •ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๋‹น ์Šคํ”„๋ง ๋นˆ์„ ์ฐพ์•„์„œ ์ฃผ์ž…ํ•œ๋‹ค. ์ด ๋•Œ ๊ธฐ๋ณธ ์กฐํšŒ ์ „๋žต์€ ํƒ€์ž…์ด ๊ฐ™์€ ๋นˆ์„ ์ฐพ์•„ ๋จผ์ € ์ฃผ์ž…ํžŒ๋‹ค.

์ƒ์„ฑ์ž์˜ @Autowired ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ณด๊ณ , ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ์ž๋™์œผ๋กœ ํ•ด๋‹น ์Šคํ”„๋ง ๋นˆ์„ ์ฐพ์•„ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์ค€๋‹ค.

์Šค์บ”์‹œ์ž‘ ์œ„์น˜ ์„ค์ •

basePackages ๋ฅผ ํƒ์ƒ‰ํ•  ํŒจํ‚ค์ง€์˜ ์‹œ์ž‘ ์œ„์น˜๋กœ ์„ค์ •ํ•œ๋‹ค.

ํ•ด๋‹น ํŒจํ‚ค์ง€๋ฅผ ํฌํ•จํ•˜์—ฌ ํ•˜์œ„ ํŒจํ‚ค์ง€๋ฅผ ๋ชจ๋‘ ํƒ์ƒ‰ํ•œ๋‹ค .

๊ด€๋ก€

ํŒจํ‚ค์ง€ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ , ์„ค์ • ์ •๋ณด ํด๋ž˜์Šค์˜ ์œ„์น˜๋ฅผ ํ”„๋กœ์ ํŠธ ์ตœ์ƒ๋‹จ์— ๋‘๋Š” ๊ฒƒ์ด๋‹ค. ์ตœ๊ทผ ์Šคํ”„๋ง ๋ถ€ํŠธ๋„ ์ด ๋ฐฉ๋ฒ•์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ๊ธฐ๋ณธ ๋Œ€์ƒ

@Component : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์—์„œ ์‚ฌ์šฉ

@Controller, @Service, @Repository @Configuration: ๋ชจ๋‘ ๋‚ด๋ถ€์— @Component ๊ฐ€ ๋ถ™์–ด์žˆ๋‹ค.

์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ๋Š”

@SpringBootApplication ๋‚ด๋ถ€์— @Component ๊ฐ€ ๋ถ™์–ด์žˆ์–ด ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ๋Œ€์ƒ์ด ๋œ๋‹ค. ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค.

 

@Component ์™€ @Bean ์–ด๋…ธํ…Œ์ด์…˜์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•ด์•ผํ•œ๋‹ค.

  1. @Bean ์˜ ๊ฒฝ์šฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปจํŠธ๋กค ๋ถˆ๊ฐ€๋Šฅํ•œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ Bean ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์— ์‚ฌ์šฉ๋œ๋‹ค.
  2. ๋ฐ˜๋Œ€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ปจํŠธ๋กœ ๊ฐ€๋Šฅํ•œ ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ์—” @Compnent ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  3. @Bean ์€ setter, builder ๋“ฑ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ ์ƒ์„ฑํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์Šคํ”„๋ง์—๊ฒŒ ๊ด€๋ฆฌํ•˜๋ผ๊ณ  ๋งก๊ธฐ๋Š” ๊ฒƒ
  4. @Component ๋Š” ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง ๋ณด๊ณ  ์•Œ์•„์„œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑํ•œ ํ›„์— ๋“ฑ๋กํ•˜๋ผ๊ณ  ๋งก๊ธฐ๋Š” ๊ฒƒ ์ฐจ์ด๊ฐ€ ์žˆ๋”ฐ.

2. ํ•„ํ„ฐ

includeFilter : ์Šค์บ”์˜ ๋Œ€์ƒ์„ ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

excludeFilter : ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์—์„œ ์ œ์™ธํ•  ๋Œ€์ƒ์„ ์ง€์ •

์• ๋…ธํ…Œ์ด์…˜ ์ž‘์„ฑ

@Target(ElementType.TYPE): TYPE → Class ๋ ˆ๋ฒจ์— ๋ถ™๋Š” ์• ๋…ธํ…Œ์ด์…˜

@MyIncludeComponent
public class BeanA {
}

@MyExcludeComponent
public class BeanB {
}

public class ComponentFilterAppConfigTest {

    @Test
    void filterScan() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
        BeanA beanA = ac.getBean("beanA", BeanA.class);
        Assertions.assertThat(beanA).isNotNull();

        assertThrows(
                NoSuchBeanDefinitionException.class,
                () -> ac.getBean("beanB", BeanB.class)
        );
    }

    @Configuration
    @ComponentScan(
            includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
            excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
    )
    static class ComponentFilterAppConfig {
     }

}

3. ์ค‘๋ณต ๋“ฑ๋ก๊ณผ ์ถฉ๋Œ

๋™๋ช…์˜ ๋นˆ์ด ์กด์žฌํ•  ๋•Œ

  1. ์ž๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก
    1. ConflictBeanDefinitionException ์˜ˆ์™ธ ๋ฐœ์ƒ
  2. ์ž๋™ ๋นˆ ๋“ฑ๋ก vs ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก

์ž๋™ ๋นˆ ๋“ฑ๋ก๊ณผ ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก์—์„œ ๋™์ผํ•œ ๋นˆ ์ด๋ฆ„์œผ๋กœ ์ธํ•ด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด, ์šฐ์„  ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก์ด ์šฐ์„ ๊ถŒ์„ ๊ฐ€์ง„๋‹ค.

>>> ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก์‹œ ๋‚จ๋Š” ๋กœ๊ทธ: Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก์‹œ ์—๋Ÿฌ

>>> Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

์Šคํ”„๋ง์—์„œ๋Š” ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•œ ๋นˆ์ด ์šฐ์„ ๊ถŒ์„ ๊ฐ€์ง€๋ฉฐ ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ํ•˜์ง€๋งŒ, ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ์˜ค๋ฒ„๋ผ์ด๋“œ ์„ค์ •์„ false ๋กœ ๋’€๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฒ„๋ผ์ด๋”ฉ ๋˜์ง€ ์•Š๊ณ  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. => ์ฆ‰ ๋™๋ช…์˜ ๋นˆ์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ์‚ฌ์ „์— ๋ง‰๋Š”๋‹ค.