์๋ ํ์ธ์!
์ด๋ฒ์๋ ์ปดํฌ๋ํธ ์ค์บ์ ๋ํด์ ๊ณต๋ถํ๊ณ ์์ต๋๋ค.
๊ทธ๋ผ ์์ํด๋ณผ๊ฒ์!!
๊ณ ๊ณ !!

1. ์ปดํฌ๋ํธ ์ค์บ๊ณผ ์์กด๊ด๊ณ ์๋ ์ฃผ์ ์์ํ๊ธฐ
์ง๊ธ๊น์ง ์คํ๋ง ๋น์ ๋ฑ๋กํ ๋๋ ์๋ฐ ์ฝ๋์ @Bean์ด๋ XML์ <bean> ๋ฑ์ ํตํด์ ์ค์ ์ ๋ณด์ ์ง์ ๋ฑ๋กํ ์คํ๋ง ๋น์ ๋์ดํ์ต๋๋ค.
์์ ์์๋ 4๊ฐ์ ๋๋ฐ์ ์ ๋์ด์ ๊ด์ฐฎ์์ง๋ง, ์ด๋ ๊ฒ ๋ฑ๋กํด์ผ ํ ์คํ๋ง ๋น์ด ์์ญ ์๋ฐฑ ๊ฐ๊ฐ ๋๋ฉด ์ผ์ผ์ด ๋ฑ๋กํ๊ธฐ๋ ๊ท์ฐฎ๊ณ , ์ค์ ์ ๋ณด๋ ์ปค์ง๊ณ ๋๋ฝํ๋ ๋ฌธ์ ๋ ๋ฐ์ํฉ๋๋ค.
๊ทธ๋์, ์คํ๋ง์ ์ค์ ์ ๋ณด๊ฐ ์์ด๋ ์คํ๋ง ๋น์ ๋ฑ๋กํ๋ ์ปดํฌ๋ํธ ์ค์บ์ด๋ผ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
๋, ์์กด๊ด๊ณ๋ ์๋์ผ๋ก ์ฃผ์ ํ๋ @Autowired ๋ผ๋ ๊ธฐ๋ฅ๋ ์ ๊ณตํฉ๋๋ค.
์ฝ๋๋ก ์์๋ณด๊ฒ ์ต๋๋ค.
๋จผ์ , ๊ธฐ์กด AppConfig.java๋ ๊ณผ๊ฑฐ ์ฝ๋์ ํ ์คํธ๋ฅผ ์ํด ๋จ๊ฒจ๋๊ณ , ์๋ก์ด AutoAppConfig.java๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
@Configuration
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
โ๏ธ@ComponentScan์ @Component๊ฐ ๋ถ์ ํด๋์ค๋ค์ ์ฐพ์์ ์๋์ผ๋ก ์คํ๋ฆฐ ๋น์ผ๋ก ๋ฑ๋ก์ ํด ์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ , excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)๋ @Component๊ฐ ๋ถ์ ํด๋์ค๋ค์ ์ฐพ๊ธด ์ฐพ๋๋ฐ, ์ ์ธํ ํด๋์ค๋ฅผ ํํฐ๋ง(@Configuration์ด ๋ถ์ ํด๋์ค๋ฅผ ์ ์ธ) ํด ์ค๋๋ค.
์ ์ ์ธํ๋์ง๋ ๋ฐ ์ฐธ๊ณ ์์ ์ค๋ช ํ ๊ฒ์ ๋๋ค.
์ง๊ธ๊น์ง ํ ๊ฒ์ ๋ค์ ์ค๋ช ํ์๋ฉด, ์ปดํฌ๋ํธ ์ค์บ์ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ @ComponentScan์ ์ค์ ์ ๋ณด์ ๋ถ์ฌ์ฃผ๋ฉด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ๊ธฐ์กด์ AppConfig์๋ ๋ค๋ฅด๊ฒ @Bean์ผ๋ก ๋ฑ๋กํ ํด๋์ค๊ฐ ํ๋๋ ์์ต๋๋ค!!
์ฐธ๊ณ !! ์ปดํฌ๋ํธ ์ค์บ์ ์ฌ์ฉํ๋ฉด @Configuration์ด ๋ถ์ ์ค์ ์ ๋ณด๋ ์๋์ผ๋ก ๋ฑ๋ก๋๊ธฐ ๋๋ฌธ์ AppConfig, TestConfig ๋ฑ ์์ ๋ง๋ค์ด๋์๋ ์ค์ ์ ๋ณด๋ ํจ๊ป ๋ฑ๋ก๋๊ณ ์คํ๋์ด ๋ฒ๋ฆฝ๋๋ค.
AppConfig ๊ฐ์ ํด๋์ค๋ ๋น์ ์ผ์ผ์ด ๋ฑ๋กํด์ ์๋์ผ๋ก ๋๋ ค๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ค๋ ํ๋ ๊ฑฐ๋ ์ถฉ๋์ด ๋ ์ ๋ฐ์ ์์ต๋๋ค.
๊ทธ๋์, excludeFilters๋ฅผ ์ด์ฉํด์ ์ค์ ์ ๋ณด๋ ์ปดํฌ๋ํธ ์ค์บ ๋์์์ ์ ์ธํ์ต๋๋ค.
๋ณดํต ์ค์ ์ ๋ณด๋ฅผ ์ปดํฌ๋ํธ ์ค์บ ๋์์์ ์ ์ธํ์ง๋ ์์ง๋ง, ๊ธฐ์กด ์์ ์ฝ๋๋ฅผ ์ต๋ํ ๋จ๊ธฐ๊ณ ์ ์งํ๊ธฐ ์ํด์ ์ด ๋ฐฉ๋ฒ์ ์ ํํ์ต๋๋ค.
์ปดํฌ๋ํธ ์ค์บ์ ์ด๋ฆ ๊ทธ๋๋ก @Component ์ ๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ฅผ ์ค์บํด์ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํฉ๋๋ค
์ฐธ๊ณ !! @Configuration์ด ์ปดํฌ๋ํธ ์ค์บ์ ๋์์ด ๋ ์ด์ ๋ @Configuration ์์ค์ฝ๋๋ฅผ ์ด์ด๋ณด๋ฉด @Component ์ ๋ ธํ ์ด์ ์ด ๋ถ์ด์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด์ , ๊ฐ ํด๋์ค๊ฐ ์ปดํฌ๋ํธ ์ค์บ์ ๋์์ด ๋๋๋ก @Component ์ ๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค. (๊ตฌํ์ฒด๋ค์๋ง ๋ถํ๋๋ค!)
์ฐ์ , MemoryMemberRepository ํด๋์ค๋ก ์ด๋ํด์ @Component๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
@Component
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
๊ทธ๋ฆฌ๊ณ , RateDiscountPolicy ํด๋์ค, MemberServiceImpl๋ก ์ด๋ํด์ ๋๊ฐ์ด ์์ @Component๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ๋ฉ๋๋ค.
๊ทผ๋ฐ, ์ด๋ ๊ฒ ์๋์ผ๋ก ์คํ๋ง ๋น์ด ๋ฑ๋ก๋๋ฉด ์ค์ ์ ๋ณด ์์ฒด๊ฐ ์๋๋ฐ, ์์กด๊ด๊ณ ์ฃผ์ ์ ์ด๋ป๊ฒ ํด์ค ์ ์์๊น์?
โ๏ธ๊ทธ๋์! ์๋ ์์กด๊ด๊ณ ์ฃผ์ ์ธ @Autowired๊ฐ ํ์ํฉ๋๋ค.
@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
์๋ ๊ฒ ๋ง์ด์ฃ !
@Autowired๋ ๋ง์น.. ac.getBean(MemberRepository.class)์ฒ๋ผ ๋์ํฉ๋๋ค.
๋ ์์ธํ ๋ด์ฉ์ ๋ฐ์์ ์ค๋ช ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ , OrderServiceImpl์๋ @Component์ @Autowired๋ฅผ ๋ถํ์ค๋๋ค.
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
//ํ
์คํธ ์ฉ๋
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
@Autowired๋ฅผ ์ฌ์ฉํ๋ฉด ์์ฑ์์์ ์ฌ๋ฌ ์์กด๊ด๊ณ๋ ํ ๋ฒ์ ์ฃผ์ ๋ฐ์ ์ ์์ต๋๋ค.
ํ ์คํธ ์ฝ๋๋ฅผ ํ ๋ฒ ์ง๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
test ํจํค์ง์ hello.core์์ scan์ด๋ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ , ๊ทธ ์์ AutoAppConfigTest๋ผ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
public class AutoAppConfigTest {
@Test
void basicScan() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
//๋น ์กฐํ
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
ํ ์คํธ๋ฅผ ์คํํด๋ณด๊ฒ ์ต๋๋ค.
AppConfig ๋ ์ฒ๋ผ ์ ์๋ํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
1-1. ์ปดํฌ๋ํธ ์ค์บ๊ณผ ์๋ ์์กด๊ด๊ณ ์ฃผ์ ๋์ ๋ฐฉ๋ฒ ๊ทธ๋ฆผ
๊ทธ๋ผ ์ปดํฌ๋ํธ ์ค์บ๊ณผ ์๋ ์์กด๊ด๊ณ ์ฃผ์ ์ ์ด๋ป๊ฒ ๋์ํ ๊น์?
๊ทธ๋ฆผ์ผ๋ก ๋์ํ๋ ๊ณผ์ ์ ๋ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
1-1(1). @ComponentScan
@ComponentScan์ @Component๊ฐ ๋ถ์ ๋ชจ๋ ํด๋์ค๋ฅผ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํฉ๋๋ค. (๋น์ฐํ ์ฑ๊ธํค์ผ๋ก ๋ฑ๋ก!)
์ด ๋, ์คํ๋ง ๋น์ ๊ธฐ๋ณธ ์ด๋ฆ์ ํด๋์ค ๋ช ์ ์ฌ์ฉํ๋, ๋งจ ์ ๊ธ์๋ง ์๋ฌธ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. (์ฃผ์ํ ์ โ ๏ธ)
- ๋น ์ด๋ฆ ๊ธฐ๋ณธ ์ ๋ต: MemberServiceImpl ํด๋์ค ๐๐ป memberServiceImpl
- ๋น ์ด๋ฆ ์ง์ ์ง์ : ๋ง์ฝ ์คํ๋ง ๋น์ ์ด๋ฆ์ ์ง์ ์ง์ ํ๊ณ ์ถ์ผ๋ฉด, @Component("memberService2") ์ด๋ฐ ์์ผ๋ก!
- ๊ผฌ์ฅ ๊ตฌํ์ฒด์ ํด์ผ ํฉ๋๋ค.
- ํน์ํ ๊ฒฝ์ฐ์๋ง ์ง์ ์ง์ ํ๋ฉด ๋ฉ๋๋ค.
1-1(2). @Autowired ์์กด๊ด๊ณ ์๋ ์ฃผ์
์์ฑ์์ @Autowired๋ฅผ ์ง์ ํ๋ฉด, ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์๋์ผ๋ก ํด๋น ์คํ๋ง ๋น์ ์ฐพ์์ ์ฃผ์ ํฉ๋๋ค.
์ด ๋, ๊ธฐ๋ณธ ์กฐํ ์ ๋ต์ ํ์ ์ด ๊ฐ์ ๋น์ ์ฐพ์์ ์ฃผ์ ํฉ๋๋ค. (ex. MemberRepository์ ํ์ ์ด ๊ฐ์ ๊ฒ์ MemoryMemberRepository)
- getBean(MemberRepository.class)์ ๋์ผํ๋ค๊ณ ๋ณด๋ฉด ๋ฉ๋๋ค.
- ๋ ์์ธํ ๋ด์ฉ์ ๋ค์์ ์ค๋ช ํ ์์
์์ฑ์์ ํ๋ผ๋ฏธํฐ๊ฐ ๋ง์๋ ๋ค ์ฐพ์์ ์๋์ผ๋ก ์ฃผ์ ํฉ๋๋ค.

2. ํ์ ์์น์ ๊ธฐ๋ณธ ์ค์บ ๋์
2-1. ํ์ํ ํจํค์ง์ ์์ ์์น ์ง์
๋ชจ๋ ์๋ฐ ํด๋์ค๋ฅผ ๋ค ์ปดํฌ๋ํธ ์ค์บํ๋ฉด ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฝ๋๋ค.
๊ทธ๋์, ๊ผญ ํ์ํ ์์น๋ถํฐ ํ์ํ๋๋ก ์์ ์์น๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
AutoAppConfig ํด๋์ค๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
@Configuration
@ComponentScan(
basePackages = "hello.core",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
โ๏ธbasePackages๋ ํ์ํ ํจํค์ง์ ์์ ์์น๋ฅผ ์ง์ ํฉ๋๋ค. ์ฆ, ์ด ํจํค์ง๋ฅผ ํฌํจํด์ ํ์ ํจํค์ง๋ฅผ ๋ชจ๋ ํ์ํฉ๋๋ค.
basePackages = {"hello.core", "hello.service"} ์ด๋ ๊ฒ ์ฌ๋ฌ ์์ ์์น๋ฅผ ์ง์ ํ ์๋ ์์ต๋๋ค.
@Configuration
@ComponentScan(
basePackages = "hello.core",
basePackageClasses = AutoAppConfig.class,
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
โ๏ธbasePackageClasses๋ ์ง์ ํ ํด๋์ค์ ํจํค์ง๋ฅผ ํ์ ์์ ์์น๋ก ์ง์ ํฉ๋๋ค.
๋ง์ฝ์ ์ง์ ํ์ง ์์ผ๋ฉด, @ComponentScan์ด ๋ถ์ ์ค์ ์ ๋ณด ํด๋์ค์ ํจํค์ง๊ฐ ์์ ์์น๊ฐ ๋ฉ๋๋ค.
<๊ถ์ฅํ๋ ๋ฐฉ๋ฒ>
๊ฐ์ธ์ ์ผ๋ก ์ฆ๊ฒจ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ํจํค์ง ์์น๋ฅผ ์ง์ ํ์ง ์๊ณ , ์ค์ ์ ๋ณด ํด๋์ค์ ์์น๋ฅผ ํ๋ก์ ํธ ์ต์๋จ์ ๋๋ ๊ฒ์ ๋๋ค.
์ต๊ทผ ์คํ๋ง ๋ถํธ๋ ์ด ๋ฐฉ๋ฒ์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํฉ๋๋ค.
ex ) ํ๋ก์ ํธ๊ฐ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ก ๋์ด ์๋ค๊ณ ๊ฐ์ ํด๋ณด๊ฒ ์ต๋๋ค.
- com.hello
- com.hello.service
- com.hello.repository
com.hello๐๐ป ํ๋ก์ ํธ ์์ ๋ฃจํธ
์ฌ๊ธฐ์ AppConfig ๊ฐ์ ๋ฉ์ธ ์ค์ ์ ๋ณด๋ฅผ ๋๊ณ , @ComponentScan ์ ๋ ธํ ์ด์ ์ ๋ถ์ด๊ณ , basePackages ์ง์ ์ ์๋ตํฉ๋๋ค.
(์ฐ๋ฆฌ๊ฐ ํ๋ ์์ ๋ก๋ AutoAppConfig๊ฐ ์ฌ๊ธฐ์ ํด๋น)
์ด๋ ๊ฒ ํ๋ฉด com.hello๋ฅผ ํฌํจํ ํ์๋ ๋ชจ๋ ์๋์ผ๋ก ์ปดํฌ๋ํธ ์ค์บ์ ๋์์ด ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ , ํ๋ก์ ํธ ๋ฉ์ธ ์ค์ ์ ๋ณด๋ ํ๋ก์ ํธ๋ฅผ ๋ํํ๋ ์ ๋ณด์ด๊ธฐ ๋๋ฌธ์ ํ๋ก์ ํธ ์์ ๋ฃจํธ ์์น์ ๋๋ ๊ฒ์ด ์ข๋ค๊ณ ์๊ฐํฉ๋๋ค.
์ฐธ๊ณ ๋ก ์คํ๋ง ๋ถํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ๋ง ๋ถํธ์ ๋ํ ์์ ์ ๋ณด์ธ @SpringBootApplication๋ฅผ ์ด ํ๋ก์ ํธ ์์ ๋ฃจํธ ์์น์ ๋๋ ๊ฒ์ด ๊ด๋ก ์ ๋๋ค. (์ด ์ค์ ์์ @ComponentScan์ด ๋ค์ด์๊ธฐ ๋๋ฌธ์ ๋๋ค.)
2-2. ์ปดํฌ๋ํธ ์ค์บ ๊ธฐ๋ณธ ๋์
์ปดํฌ๋ํธ ์ค์บ์ @Component ๋ฟ๋ง ์๋๋ผ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ๋ ์ถ๊ฐ๋ก ๋์์ ํฌํจํฉ๋๋ค.
- @Component: ์ปดํฌ๋ํธ ์ค์บ์์ ์ฌ์ฉํฉ๋๋ค.
- @Controller: ์คํ๋ง MVC ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉํฉ๋๋ค.
- @Service: ์คํ๋ง ๋น์ฆ๋์ค ๋ก์ง์์ ์ฌ์ฉํฉ๋๋ค.
- @Repository: ์คํ๋ง ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์์ ์ฌ์ฉํฉ๋๋ค.
- @Configuration: ์คํ๋ง ์ค์ ์ ๋ณด์์ ์ฌ์ฉํฉ๋๋ค.
ํด๋น ํด๋์ค์ ์์ค ์ฝ๋๋ฅผ ๋ณด๋ฉด @Component๋ฅผ ํฌํจํ๊ณ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
@Component
public @interface Controller {
}
@Component
public @interface Service {
}
@Component
public @interface Configuration {
}
์ฐธ๊ณ !! ์ฌ์ค ์ ๋ ธํ ์ด์ ์๋ ์์ ๊ด๊ณ๋ผ๋ ๊ฒ์ด ์์ต๋๋ค.
๊ทธ๋์, ์ด๋ ๊ฒ ์ ๋ ธํ ์ด์ ์ด ํน์ ์ ๋ ธํ ์ด์ ์ ๋ค๊ณ ์๋ ๊ฒ์ ์ธ์ํ ์ ์๋ ๊ฒ์ ์๋ฐ ์ธ์ด๊ฐ ์ง์ํ๋ ๊ธฐ๋ฅ์ด ์๋, ์คํ๋ง์ด ์ง์ํ๋ ๊ธฐ๋ฅ์ ๋๋ค.
์ปดํฌ๋ํธ ์ค์บ์ ์ฉ๋ ๋ฟ๋ง ์๋๋ผ ๋ค์ ์ ๋ ธํ ์ด์ ์ด ์์ผ๋ฉด ์คํ๋ง์ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ํํฉ๋๋ค.
- @Controller: ์คํ๋ง MVC ์ปจํธ๋กค๋ฌ๋ก ์ธ์ํฉ๋๋ค.
- @Repository: ์คํ๋ง ๋ฐ์ดํฐ ์ ๊ทผ ๊ณ์ธต์ผ๋ก ์ธ์ํ๊ณ , ๋ฐ์ดํฐ ๊ณ์ธต์ ์์ธ๋ฅผ ์คํ๋ง ์์ธ๋ก ๋ณํํด์ค๋๋ค.
- @Configuration: ์์ ๋ณด์๋ฏ์ด, ์คํ๋ง ์ค์ ์ ๋ณด๋ก ์ธ์ํ๊ณ , ์คํ๋ง ๋น์ด ์ฑ๊ธํค์ ์ ์งํ๋๋ก ์ถ๊ฐ ์ฒ๋ฆฌ๋ฅผ ํฉ๋๋ค.
- @Service: ์ฌ์ค @Service๋ ํน๋ณํ ์ฒ๋ฆฌ๋ฅผ ํ์ง ์์ต๋๋ค.
- ๋์ , ๊ฐ๋ฐ์๋ค์ด ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ด ์ฌ๊ธฐ์ ์๊ฒ ๊ตฌ๋.. ๋ผ๊ณ ๋น์ฆ๋์ค ๊ณ์ธต์ ์ธ์ํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
์ฐธ๊ณ !! useDefaultFilters ์ต์ ์ ๊ธฐ๋ณธ์ผ๋ก ์ผ์ ธ์๋๋ฐ, ์ด ์ต์ ์ ๋๋ฉด ๊ธฐ๋ณธ ์ค์บ ๋์๋ค์ด ์ ์ธ๋ฉ๋๋ค.
๊ทธ๋ฅ ์ด๋ฐ ์ต์ ์ด ์๊ตฌ๋.. ์ ๋ ์๊ณ ๋์ด๊ฐ๋ฉด ๋ฉ๋๋ค.

3. ํํฐ
- includeFilters: ์ปดํฌ๋ํธ ์ค์บ ๋์์ ์ถ๊ฐ๋ก ์ง์ ํฉ๋๋ค.
- excludeFilters: ์ปดํฌ๋ํธ ์ค์บ์์ ์ ์ธํ ๋์์ ์ง์ ํฉ๋๋ค.
ํํฐ๋ ์ด๋ ๊ฒ 2๊ฐ์ง ์ข ๋ฅ๊ฐ ์๊ณ , ๋น ๋ฅด๊ฒ ์์ ๋ก ํ์ธํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
3-1. ์์
test ํจํค์ง์ hello.core์ scan ์ด๋ผ๋ ํจํค์ง์ filter๋ผ๋ ํจํค์ง๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ , ์ด ํจํค์ง ์์ MyIncludeComponent๋ผ๋ Annotation์ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
์ด ์ ๋ ธํ ์ด์ ์ ์ปดํฌ๋ํธ ์ค์บ ๋์์ ์ถ๊ฐํ ์ ๋ ธํ ์ด์ ์ ๋๋ค.
์๋ฒ์๋ ์ด๋ฆ์ MyExcludeComponent๋ก ์ ๋ ธํ ์ด์ ๋ช ์ ์ง์ ํด๋ณผ๊ฒ์.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
์ด ์ ๋ ธํ ์ด์ ์ ์ปดํฌ๋ํธ ์ค์บ ๋์์์ ์ ์ธํ ์ ๋ ธํ ์ด์ ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ , filter ํจํค์ง ์์ ํด๋์ค๋ฅผ 2๊ฐ ๋ง๋ค์ด๋ณผ๊ฑฐ์์.
ํด๋์ค ๋ช ์ BeanA, BeanB๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
@MyIncludeComponent
public class BeanA {
}
@MyExcludeComponent
public class BeanB {
}
๊ทธ๋ฆฌ๊ณ , ํ ์คํธ ์ฝ๋๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
Filter ํจํค์ง ์์ ComponentFilterAppConfigTest ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
}
@Configuration
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {
}
}
์ด ํด๋์ค ์์ static์ผ๋ก ํด์ ComponentFilterAppConfig ์ค์ ์ ๋ณด ํด๋์ค๋ฅผ ๋ง๋ค์ด์คฌ์ต๋๋ค.
์์๋ @ComponentScan์ผ๋ก ํด์ ์๊น ๋ง๋ค์๋ MyIncludeComponent, MyExcludeComponent๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
@ComponentScan.Filter๋ฅผ static import ํด์ @Filter๋ก ์ค์ฌ๋ ๋ฉ๋๋ค.
์ฐ์ , beanA๋ฅผ ์กฐํํ์ต๋๋ค.
beanA๋ ๊ฒฐ๊ณผ๊ฐ ์ด๋ป๊ฒ ๋์ค๋์ง ๋ด๋ณด๊ฒ ์ต๋๋ค.
์ด ์ํ์์ beanB๋ฅผ ์กฐํํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
ac.getBean("beanB", BeanB.class);
}
ํ ์คํธ ๊ฒฐ๊ณผ๋ ์ด๋ป๊ฒ ๋์ฌ๊น์?
BeanB๊ฐ ์๋ค๊ณ ๋์ต๋๋ค.
์๋ํ๋ฉด, BeanB๋ฅผ MyExcludeComponentํด์ excludeFilter๋ก ์ง์ ํด๋จ๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ ์ค์บ ์ ์ธ ๋์์ด ๋์ด๋ฒ๋ ค์ ์ด๋ ๊ฒ NoSuchBeanDefinitionException ์ด๋ผ๋ ์ค๋ฅ๊ฐ ๋จ๊ฒ ๋ฉ๋๋ค.
BeanB๋ ๊ฒ์ฆ ์ฝ๋๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ์ต๋๋ค.
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
assertThrows(
NoSuchBeanDefinitionException.class,
() -> ac.getBean("beanB", BeanB.class));
}
์ด ์๋ฌ๊ฐ ๋์ผ ํ ์คํธ ์ฑ๊ณต์ธ๋ฐ
ํ ์คํธ๊ฐ ์์ฃผ ์ ์คํ์ด ๋์์ต๋๋ค.
์ ๋ฆฌ๋ฅผ ํด๋ณด์๋ฉด..
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
- includeFilters์ MyIncludeComponent ์ ๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ BeanA๊ฐ ์คํ๋ง ๋น์ ๋ฑ๋ก๋ฉ๋๋ค.
- excludeFilters์ MyExcludeComponent ์ ๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ BeanB๋ ์คํ๋ง ๋น์ ๋ฑ๋ก๋์ง ์์ต๋๋ค.
3-2. FilterType ์ต์
FilterType์ 5๊ฐ์ง ์ต์ ์ด ์์ต๋๋ค.
- ANNOTATION: ๊ธฐ๋ณธ๊ฐ์
๋๋ค, ์ ๋
ธํ
์ด์
์ ์ธ์ํด์ ๋์ํฉ๋๋ค. (์๋ตํด๋ ์ ๋์ํฉ๋๋ค.)
- ex) org.example.SomeAnnotation
- ASSIGNABLE_TYPE: ์ง์ ํ ํ์
๊ณผ ์์ ํ์
์ ์ธ์ํด์ ๋์ํฉ๋๋ค.
- ex) org.example.SomeClass
- ASPECTJ: AspectJ ํจํด์ ์ฌ์ฉํฉ๋๋ค.
- ex) org.example..*Service+
- REGEX: ์ ๊ท ํํ์ ์
๋๋ค.
- ex) org\.example\.Default.*
- CUSTOM: TypeFilter ์ด๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ์ฒ๋ฆฌํฉ๋๋ค.
- ex) org.example.MyTypeFilter
์๋ฅผ ๋ค์ด์ BeanA๋ ๋นผ๊ณ ์ถ์ผ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐํ๋ฉด ๋ฉ๋๋ค.
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class)
}
)
์ฐธ๊ณ !! @Component๋ฉด ์ถฉ๋ถํ๊ธฐ ๋๋ฌธ์, includeFilters๋ฅผ ์ฌ์ฉํ ์ผ์ ๊ฑฐ์ ์์ต๋๋ค.
excludeFilters๋ ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๋ก ๊ฐํน ์ฌ์ฉํ ๋๊ฐ ์์ง๋ง ๋ง์ง๋ ์์ต๋๋ค.
ํนํ, ์ต๊ทผ ์คํ๋ง ๋ถํธ๋ ์ปดํฌ๋ํธ ์ค์บ์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋๋ฐ, ๊ฐ์ธ์ ์ผ๋ก๋ ์ต์ ์ ๋ณ๊ฒฝํ๋ฉด์ ์ฌ์ฉํ๊ธฐ ๋ณด๋ค๋ ์คํ๋ง์ ๊ธฐ๋ณธ ์ค์ ์ ์ต๋ํ ๋ง์ถ์ด ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๊ณ ์ ํธํ๋ ํธ์ ๋๋ค.

4. ์ค๋ณต ๋ฑ๋ก๊ณผ ์ถฉ๋
์ปดํฌ๋ํธ ์ค์บ์์ ๊ฐ์ ๋น ์ด๋ฆ์ ๋ฑ๋กํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
์ํฉ์ ์ด 2๊ฐ์ง๊ฐ ์์ต๋๋ค.
4-1. ์๋ ๋น ๋ฑ๋ก vs ์๋ ๋น ๋ฑ๋ก
์ปดํฌ๋ํธ ์ค์บ์ ์ํด ์๋์ผ๋ก ์คํ๋ง ๋น์ด ๋ฑ๋ก๋๋๋ฐ, ๊ทธ ์ด๋ฆ์ด ๊ฐ์ ๊ฒฝ์ฐ ์คํ๋ง์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
- ConflictingBeanDefinitionException ์์ธ ๋ฐ์
- ex) MemberService์ @Component("Service")๊ฐ ์๋๋ฐ, OrderService์ @Component("Service")๋ก ๋๊ฐ์ด ์ด๋ฆ์ ์ง์์ ๋
- ๊ทผ๋ฐ, ๊ฑฐ์ ์ผ์ด๋์ง ์์ต๋๋ค.
4-2. ์๋ ๋น ๋ฑ๋ก vs ์๋ ๋น ๋ฑ๋ก
๋ง์ฝ ์๋ ๋น ๋ฑ๋ก๊ณผ ์๋ ๋น ๋ฑ๋ก์์ ๋น ์ด๋ฆ์ด ์ถฉ๋๋๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
@Component
public class MemoryMemberRepository implements MemberRepository {}
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
์ด ๊ฒฝ์ฐ ์๋ ๋น ๋ฑ๋ก์ด ์ฐ์ ๊ถ์ ๊ฐ์ง๋๋ค.
(์๋ ๋น์ด ์๋ ๋น์ ์ค๋ฒ๋ผ์ด๋ฉ ํด๋ฒ๋ฆฝ๋๋ค.)
<์๋ ๋น ๋ฑ๋ก ์ ๋จ๋ ๋ก๊ทธโ ๏ธ>
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
์คํ๋ง ๋ถํธ์ธ CoreApplication์ ์คํํด๋ณด๋ฉด ์ค๋ฅ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
์ค์ํ๊ฑฐ..
๋ช ํํ์ง ์์ ๊ฒ์ ํ์ง ๋ง์๋ค!! ์ ๋งคํ๊ณ ์๋ฆฌ๊น๋ฆฌํ ๊ฒ์ ์ง์ด์น์!

์ค๋๋ ์ด๋ ๊ฒ ๋์ด ๋ฌ์ต๋๋ค!!
๋ค์ ์น์ ์์๋ ์์กด๊ด๊ณ ์๋ ์ฃผ์ ์ ๊ดํด์ ํด๋ณผ ๊ฒ์ ๋๋ค.
์ค์ํ ๋งํผ ๋ฌด์ง์ฅ ๊ธธ์ด์..ใ ๊ณต๋ถํ๊ธฐ ์๋ฅด๋ค.. ํ
๊ทธ๋ผ ์๋ฝ!!

'STUDY๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring ๊ธฐ๋ณธ] 7. ์์กด ๊ด๊ณ ์๋ ์ฃผ์ (1) | 2025.04.23 |
---|---|
[Spring ๊ธฐ๋ณธ] 5. ์ฑ๊ธํค ์ปจํ ์ด๋ (1) | 2025.04.15 |
[Spring ๊ธฐ๋ณธ] 4. ์คํ๋ง ์ปจํ ์ด๋์ ์คํ๋ง ๋น๐ซ (0) | 2025.04.05 |
[Spring ๊ธฐ๋ณธ] 3. ์คํ๋ง ํต์ฌ ์๋ฆฌ ์ดํด2- ๊ฐ์ฒด ์งํฅ ์๋ฆฌ ์ ์ฉ (2) | 2025.03.26 |
[Spring ๊ธฐ๋ณธ] 2. ์คํ๋ง ํต์ฌ ์๋ฆฌ ์ดํด1 - ์์ ๋ง๋ค๊ธฐ (0) | 2025.03.21 |