์Šคํ”„๋ง ๊ธฐ๋ณธ ๋ณต์Šต 1 - DIP, OCP ์›์น™๊ณผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ

1. ๊ฐœ์š”

 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ •ํ•ด์ง€์ง€ ์•Š์•„์„œ ์ž„์‹œ ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ๊ฐœ๋ฐœํ•˜๋˜ ์ค‘, DB๊ฐ€ ํ™•์ •๋˜๊ณ  JPA๋กœ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ ๋ ˆ์ด์–ด๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค. DI ์ปจํ…Œ์ด๋„ˆ, ํ˜น์€ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ธฐ ์ „์—๋Š” ์˜์กด๊ด€๊ณ„๋ฅผ ์ง์ ‘ ๊ตฌํ˜„์ฒด๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ง€์ •ํ•ด ์ฃผ์—ˆ์ง€๋งŒ, ์ด๋Š” SOLID 5์›์น™ ์ค‘ DIP, OCP์— ์œ„๋ฐฐ๋˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ ์„ค๊ณ„๋ฅผ ์œ„ํ•ด์„œ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ?

 

๋ณ€๊ฒฝ ์ „
๋ณ€๊ฒฝ ํ›„

2. ์Šคํ”„๋ง์ด ์—†๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด

๋จผ์ € ์šฐ๋ฆฌ๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ ์„ฑ๊ณตํ•ด์•ผ ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑ ํ•ด๋ณด์ž.

class MemberServiceImplTest {
	
    //์•„์ง ๋ณ„๋‹ค๋ฅธ DI ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์—†์œผ๋ฏ€๋กœ ์„œ๋น„์Šค ๊ฐ์ฒด๋„ ์ง์ ‘ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋„๋ก ํ•œ๋‹ค.
    MemberService memberService = new MemberServiceImpl();

    @Test
    @DisplayName("ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต")
    void join() {
        //given
        Member member = new Member("name", MemberGrade.USER);

        //when
        Long joinedId = memberService.join(member);

        //then
        Member findMember = memberService.findOne(joinedId).get();
        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getGrade()).isEqualTo(member.getGrade());
    }

}

๋งŒ์•ฝ ์Šคํ”„๋ง์˜ ๊ธฐ๋Šฅ์ด ์—†์ด ๊ตฌํ˜„์ฒด๋ฅผ ์„ค์ •ํ•œ๋‹ค๊ณ  ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‹คํ˜•์„ฑ์„ ์ด์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

public class MemberServiceImpl implements MemberService {

//    private MemberRepository memberRepository = new MemoryMemberRepository();
    private MemberRepository memberRepository = new JpaMemberRepository();
    
    // ์ดํ•˜ ์„œ๋น„์Šค ๋กœ์ง

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜์กด์„ฑ์„ ์ง์ ‘ ์ˆ˜์ •ํ•ด ์ฃผ์—ˆ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ์ž˜ ํ†ต๊ณผํ•  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๋Š” ์‚ฌ์‹ค ์œ„์—์„œ ์„œ์ˆ ํ–ˆ๋“ฏ DIP, OCP ์›์น™์— ์œ„๋ฐฐ๋œ๋‹ค.

DIP - ์˜์กด๊ด€๊ณ„ ์—ญ์ „ ์›์น™
์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•˜๊ณ , ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

OCP - ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™
ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ์œผ๋‚˜ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค.

 

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

 

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋– ํ•œ ๋ฐฉ์‹์œผ๋กœ ์„œ๋น„์Šค ๊ฐ์ฒด ๋‚ด๋ถ€์— ์ง์ ‘ ์˜์กด๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ  ๊ฐœ๋ฐœ์ž์˜ ์˜๋„๋Œ€๋กœ ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

์šฐ์„  ์ง์ ‘์ ์ธ ์˜์กด์„ฑ ์ฃผ์ž… ์ฝ”๋“œ๋ฅผ ์‚ญ์ œ ํ•ด๋ณด๋„๋ก ํ•˜์ž.

public class MemberServiceImpl implements MemberService {

    private MemberRepository memberRepository;
    
    // ์ดํ•˜ ์„œ๋น„์Šค ๋กœ์ง

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ๋‹น์—ฐํžˆ memberRepository ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ NullpointerException์ด ๋ฐœ์ƒํ•œ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ์•„๋ฌด๋„ memberRepository ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•ด์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์„ค์ •์ž์ธ Config ๊ฐ์ฒด์˜ ๊ฐœ๋…์ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋œ๋‹ค.

 

๋‹ค์Œ๊ณผ ๊ฐ™์€ MemberConfig ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

public class MemberConfig {

    public MemberRepository memberRepository() {
        return new JpaMemberRepository();
    }

    public MemberService memberService() {
        return new MemberServiceImpl();
    }

}

 

Member ๋„๋ฉ”์ธ์— ๊ด€ํ•œ ์„ค์ • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์„ ์ด Config ๊ฐ์ฒด์—๊ฒŒ ์™„์ „ํžˆ ์ผ์ž„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

๊ทธ๋ ‡๋‹ค๋ฉด MemberServiceImpl ํด๋ž˜์Šค ๋‚ด๋ถ€์˜ ์˜์กด์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

public class MemberServiceImpl implements MemberService {

    private MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    
    // ์ดํ•˜ ์„œ๋น„์Šค ๋กœ์ง
class MemberServiceImplTest {

    MemberService memberService;
   
    @BeforeEach
    void beforeEach() {
    	//Config ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๋Š”๋‹ค.
        MemberConfig memberConfig = new MemberConfig();
        this.memberService = memberConfig.memberService();
    }

 

์ด๋ ‡๊ฒŒ MemberConfig ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…๋ฐ›๋Š”๋‹ค๋ฉด, ์•„๋ฌด๋ฆฌ MemberRepository ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ Config ๊ฐ์ฒด ๋‚ด๋ถ€์—์„œ๋งŒ ์˜์กด๊ด€๊ณ„๋ฅผ ์„ ํƒํ•˜๊ณ , ์„œ๋น„์Šค ๊ฐ์ฒด ๋‚ด๋ถ€์— ์ง์ ‘ ์˜์กด๊ด€๊ณ„๋ฅผ ์„ ํƒํ•  ์ผ์ด ์‚ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค. 

 

์ฆ‰, ์˜์กด๊ด€๊ณ„ ์„ ํƒ ์ฑ…์ž„์„ ์™„์ „ํžˆ ์ผ์ž„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

์ด์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋žจ(๊ฐ์ฒด)์ด ๊ตฌํ˜„ ๊ฐ์ฒด ์ œ์–ด๋ฅผ ์ง์ ‘ ํ•˜์ง€ ์•Š๊ณ , ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ œ์–ด์˜ ์—ญ์ „ IoC(Inversion of Control) ์ด๋ผ๊ณ  ํ•œ๋‹ค.

 

๋˜ํ•œ MemberConfig ์ฒ˜๋Ÿผ ์ œ์–ด ํ๋ฆ„์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์—ญํ• ์˜ ๊ฐ์ฒด๋ฅผ IoC ์ปจํ…Œ์ด๋„ˆ ํ˜น์€ DI ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ฑ…์ž„์„ ๋ถ„๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ๊ฐ€ ๋œ๋‹ค.

DI ์ปจํ…Œ์ด๋„ˆ

 

 

 

์ด์ œ OCP ์›์น™๊ณผ DIP ์›์น™์€ ์ค€์ˆ˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์ง€๋งŒ, ํ˜„์žฌ๋Š” ์Šคํ”„๋ง์˜ ๋„์›€ ์—†์ด ๊ตฌํ˜„ํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด๋„ ์ค€์ˆ˜๋˜์ง€ ์•Š๊ณ  ์žˆ๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์ผ์ผํžˆ ์˜์กด์„ฑ์„ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋‚จ์•„์žˆ๋‹ค.

 

์ž‘์€ ์„œ๋น„์Šค๋ผ๋„ ์ƒ๋‹นํžˆ ๋งŽ์€ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌํ˜„์ฒด๊ฐ€ ์ƒ์„ฑ๋ ํ…๋ฐ ๋ชจ๋“  ์˜์กด์„ฑ์„ ์‚ฌ์šฉ์ž๊ฐ€ Config ๊ฐ์ฒด์— ์ง์ ‘ ์ฃผ์ž…ํ•ด์•ผ ํ• ๊นŒ?

 

3. ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ

๊ธฐ์กด ์†Œ์Šค์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ DI ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋ก๋œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๊ฐ’์„ ๋ฐ›์•„์™€์„œ ์‚ฌ์šฉํ•˜์˜€๋‹ค. 

 

ํ•˜์ง€๋งŒ ์Šคํ”„๋ง์—์„œ ์ง€์›ํ•˜๋Š” ์Šคํ”„๋ง DI ์ปจํ…Œ์ด๋„ˆ (์•ž์œผ๋กœ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ผ ํ•˜๊ฒ ์Œ)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๊ฐ์ฒด๋“ค์„ '์Šคํ”„๋ง ๋นˆ' ์œผ๋กœ ๋“ฑ๋กํ•˜์—ฌ ์ €์žฅํ•ด์ฃผ๊ณ  ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ฐ์ฒด๋“ค์„ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋นˆ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

Spring Boot ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์‹œ์ž‘ํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด๋ผ๋Š” ๋™์ž‘์ด ์ผ์–ด๋‚˜๋Š”๋ฐ ์ด ๋•Œ ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ง€์ • ๋ฒ”์œ„์— ํฌํ•จ๋œ ๊ฐ์ฒด๋“ค์„ ๋ชจ๋‘ ์Šค์บ”ํ•˜์—ฌ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋นˆ์œผ๋กœ ํฌํ•จ์‹œํ‚จ๋‹ค.

 

@SpringBootApplication ํ•˜์œ„์— ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ์˜ต์…˜์ด ๋‚˜์—ด๋˜์–ด ์žˆ๋‹ค.

์ด ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”ํ›„ ํ›„์ˆ ํ•˜๋„๋ก ํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์— ์›ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํฌํ•จ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์šฐ์„  ์ •๋ฆฌํ•ด๋ณด์ž.

 

ํฌ๊ฒŒ ๋‚˜๋ˆ„์ž๋ฉด ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ์ž๋™์œผ๋กœ ๋“ฑ๋ก ๋Œ€์ƒ์— ํฌํ•จ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

 

3.1 ์ˆ˜๋™ ๋“ฑ๋ก

์•ž์„œ ์‚ดํŽด๋ณธ Config ๊ฐ์ฒด, ์ฆ‰ DI ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ์•ž์„œ ์ž‘์„ฑํ•œ MemberConfig ๊ฐ์ฒด์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด @Configuration ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์˜์กด์„ฑ์„ ๋ฐ˜ํ™˜ํ•  ๋ฉ”์„œ๋“œ์— @Bean ์• ๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜์ž.

@Configuration
public class MemberConfig {

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

}

๋‹ค์Œ๊ณผ ๊ฐ™์ด @Configuration ๊ณผ @Bean ์• ๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด DI ์ปจํ…Œ์ด๋„ˆ์™€ ์ฃผ์ž…ํ•  ๊ฐ์ฒด๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜๋ฉด, ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ธฐ๋™๋˜๋ฉด์„œ ์œ„์—์„œ ๊ธฐ์ˆ ํ•œ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์˜ ์ˆ˜์ง‘ ๋Œ€์ƒ์ด ๋œ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด Cofig ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ˆ˜์ง‘ํ•œ ๋นˆ๋“ค์ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ์ž˜ ๋˜๋Š”์ง€ ์กฐํšŒํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋„๋ก ํ•˜์ž.

 

public class ApplicationContextTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MemberConfig.class);

    @Test
    @DisplayName("์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์กฐํšŒํ•˜๊ธฐ")
    void findAllBeans() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames) {

            BeanDefinition beanDefinition = ac.getBeanDefinition(beanName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {

                Object bean = ac.getBean(beanName);
                System.out.println("beanName = " + beanName + " | bean = " + bean);
            }
        }
    }
    
    @Test
    @DisplayName("ํšŒ์› ์„œ๋น„์Šค ๋นˆ ์ฐพ์•„์˜ค๊ธฐ")
    void findMemberServiceBean() {
        MemberService memberService = ac.getBean(MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

    @Test
    @DisplayName("ํšŒ์› ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋นˆ ์ฐพ์•„์˜ค๊ธฐ")
    void findMemberRepositoryBean() {
        MemberRepository memberRepository = ac.getBean(MemberRepository.class);
        Assertions.assertThat(memberRepository).isInstanceOf(MemoryMemberRepository.class);
    }

}

 

AnnotationApplicationContext ๊ฐ์ฒด๋Š” ApplicationContext ์˜ ๊ตฌํ˜„์ฒด ์ค‘ ํ•˜๋‚˜๋กœ, ์Šคํ”„๋ง ๋นˆ์˜ ๊ด€๋ฆฌ๋ฅผ ์ž๋ฐ”๋กœ ๊ตฌํ˜„ํ•˜๋Š” ํƒ€์ž…์˜ ๊ตฌํ˜„์ฒด์ด๋‹ค. ๊ณผ๊ฑฐ XML ๊ด€๋ฆฌ ๋ฐฉ์‹ ๋ฐ Groovy ๋“ฑ์„ ์ด์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด๋„ ์žˆ๋‹ค.

 

์—„๋ฐ€ํžˆ ๋งํ•˜์ž๋ฉด ApplicationContext ๋ณด๋‹ค ๋นˆ์„ ๊ด€๋ฆฌํ•˜๋Š” BeanFactory ๊ฐ์ฒด๋ฅผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ๋งž์ง€๋งŒ, ์ตœ๊ทผ์—๋Š” BeanFactory ์—์„œ ๊ตญ์ œํ™” ๊ธฐ๋Šฅ, ํ™˜๊ฒฝ๋ณ€์ˆ˜, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฒคํŠธ, ํŽธ๋ฆฌํ•œ ๋ฆฌ์†Œ์Šค ์กฐํšŒ ๋“ฑ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ๋ง๋ถ™์ธ ApplicationContext ๊ฐ์ฒด๋ฅผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

 

์†Œ์Šค์— ๋Œ€ํ•œ ์„ค๋ช…์œผ๋กœ ๋Œ์•„๊ฐ€์ž๋ฉด, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ „์ฒด ๋“ฑ๋ก๋œ ์Šคํ”„๋ง ๋นˆ์„ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋“ฑ๋กํ•œ ๊ฐ์ฒด (ROLE_APPLICATION) ๋“ค๋งŒ ์ •๋ฆฌํ•˜์—ฌ ์ถœ๋ ฅํ•œ๋‹ค. BeanDefinition์€ ๊ฐ์ฒด์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์ด๋‹ค.

 

์ „์ฒด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ

์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด Config ๊ฐ์ฒด๋ฅผ ํฌํ•จํ•˜์—ฌ Bean์œผ๋กœ ๋“ฑ๋กํ•œ ๊ฐ์ฒด๋“ค์ด ์ž˜ ์ถœ๋ ฅ ๋˜๊ณ , ์ง์ ‘ ์ง€์ •ํ•œ ๊ฐ์ฒด ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ์ถœ๋ ฅ๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

3.2 ์ž๋™ ๋“ฑ๋ก

์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘ํ•˜๋ฉด์„œ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์„ ํ†ตํ•ด ๊ฐ์ฒด๋“ค์„ ์ˆ˜์ง‘ํ•˜๊ณ  ์Šคํ”„๋ง ๋นˆ์œผ๋กœ์จ ์ž๋™์œผ๋กœ ์ €์žฅํ•œ๋‹ค๊ณ  ์•ž์„œ ์„ค๋ช…ํ•œ ๋ฐ” ์žˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์ž๋™์œผ๋กœ ๋นˆ์œผ๋กœ ์ˆ˜์ง‘๋˜๋Š” ๊ฐ์ฒด๋“ค์€ ์–ด๋– ํ•œ ๊ฐ์ฒด๋“ค์ผ๊นŒ?

 

์‚ฌ์‹ค์€ ์œ„์—์„œ ์ˆ˜๋™ ๋“ฑ๋ก์„ ํ…Œ์ŠคํŠธํ•˜๋ฉฐ ํ•œ ๊ฐ€์ง€ ์ž๋™ ๋“ฑ๋ก์˜ ์˜ˆ๋ฅผ ์ด๋ฏธ ์‚ดํŽด๋ณด์•˜๋‹ค. ๋ฐ”๋กœ @Configuration ์• ๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ์„ค์ • ๊ฐ์ฒด์˜€๋‹ค.

 

์Šคํ”„๋ง์—์„œ๋Š” ์ž์ฃผ ์“ฐ๋Š” ๊ฐ์ฒด๋“ค ๋ช‡ ๊ฐ€์ง€๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ์• ๋…ธํ…Œ์ด์…˜์ด ์กด์žฌํ•˜๋Š”๋ฐ ์ด ์• ๋…ธํ…Œ์ด์…˜๋“ค์„ ์Šคํ”„๋ง์ด ๊ธฐ๋™ํ•˜๋ฉฐ ์ž๋™์œผ๋กœ ์ˆ˜์ง‘ํ•œ๋‹ค.

 

๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ๋Š” @Configuration , @Controller , @Service , @Repository ๋“ฑ์ด ์žˆ๋‹ค.

 

์ด๋“ค์€ ๋ชจ๋‘ @Component ์• ๋…ธํ…Œ์ด์…˜์„ ์ƒ์†๋ฐ›๋Š”๋ฐ, ๊ฒฐ๊ตญ @Component ์• ๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๊ฐ์ฒด๋ฅผ ๋ชจ๋‘ ์ˆ˜์ง‘ํ•˜๋‹ค๊ณ  ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Configuration ์• ๋…ธํ…Œ์ด์…˜ ๋‚ด๋ถ€

์• ๋…ธํ…Œ์ด์…˜์˜ ์ƒ์†์€ ๊ณต์‹์ ์ธ ์ž๋ฐ” ๋ฌธ๋ฒ•์ด ์•„๋‹ˆ๊ณ , ์Šคํ”„๋ง์ด ํŽธ์˜์ƒ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

 

์›๋ž˜๋ผ๋ฉด ์Šคํ”„๋ง์ด ๊ธฐ๋™๋˜๋ฉด์„œ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด ํ•œ๋ฒˆ ์ด๋ฃจ์–ด์ง€์ง€๋งŒ, ์šฐ๋ฆฌ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์„ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•ด ์ฃผ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ์„ค์ • ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด ๋ณด์ž.

@Configuration
@ComponentScan(
        basePackages = "jwjung.spring.remind",
        excludeFilters = @ComponentScan.Filter(
        	type = FilterType.ANNOTATION, classes = Configuration.class
        )
)
public class MemberAutoConfig {
}
@Repository
public class MemoryMemberRepository implements MemberRepository {
// ์ดํ•˜ ์ƒ๋žต
@Service
public class MemberServiceImpl implements MemberService {
// ์ดํ•˜ ์ƒ๋žต

์ปดํฌ๋„ŒํŠธ ์Šค์บ”๋งŒ์„ ๋ช…์‹œํ•˜๋Š” Config ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์Šค์บ”์˜ ๋Œ€์ƒ์ด ๋  ๊ฐ์ฒด๋“ค์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด @Component์˜ ํ•˜์œ„ ์• ๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ค€๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์•„๊นŒ ํ–ˆ๋˜ ํ…Œ์ŠคํŠธ์™€ ๋น„์Šทํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž.

 

public class ComponentScanTest {

    AnnotationConfigApplicationContext ac = 
    	new AnnotationConfigApplicationContext(MemberAutoConfig.class);

    @Test
    @DisplayName("์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ ์กฐํšŒํ•˜๊ธฐ")
    void findAllBeans() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames) {

            BeanDefinition beanDefinition = ac.getBeanDefinition(beanName);

            if (beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {

                Object bean = ac.getBean(beanName);
                System.out.println("beanName = " + beanName + " | bean = " + bean);
            }
        }
    }
}

ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ

๋ถ„๋ช… Config ๊ฐ์ฒด ๋‚ด์— ๋ช…์‹œํ•œ ์Šคํ”„๋ง ๋นˆ์ด ์—†์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  Service ๊ฐ์ฒด์™€ Repository ๊ฐ์ฒด๊ฐ€ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ ์•ˆ์— ์ €์žฅ๋˜์–ด ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ์—ฌํƒœ๊นŒ์ง€ ์ผ์ผํžˆ ๋ช…์‹œ์ ์ธ DI๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ์˜์กด์„ฑ์ด ์ฃผ์ž…๋˜์–ด ์™”๋Š”์ง€๋ฅผ ์•Œ์•„๋ณด์•˜๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜น์‹œ ๊ฐ™์€ ๋ถ€๋ชจ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ตฌํ˜„์ฒด ๋‘๊ฐœ๊ฐ€ ๋ชจ๋‘ ์ปดํฌ๋„ŒํŠธ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ๋‹ค๋ฉด, ์Šคํ”„๋ง์ด MemberServiceImpl ์— ์ฃผ์ž…ํ•  ๊ฐ์ฒด๋ฅผ ์–ด๋–ป๊ฒŒ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

MemberRepository๋ฅผ ์ƒ์†๋ฐ›๋Š” MemoryMemberRepository ์™€ JpaMemberRepository ์–‘์ชฝ์— ๋ชจ๋‘ @Repository ์• ๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๊ณ  ๊ฐ™์€ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์นœ์ ˆํ•œ ์—๋Ÿฌ ๋ฌธ๊ตฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

No qualifying bean of type 'jwjung.spring.remind.repository.MemberRepository' available: expected single matching bean but found 2: jpaMemberRepository,memoryMemberRepository

 

MemberServiceImpl ๊ฐ์ฒด์— ์ฃผ์ž…ํ•ด์•ผ ํ•  ์˜์กด์„ฑ์„ ์Šคํ”„๋ง์ด ํŒ๋‹จ ํ•  ์ˆ˜๊ฐ€ ์—†์œผ๋ฏ€๋กœ NoUniqueBeanDefinitionException ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

์ด๋Ÿด๋•Œ ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ๊ฐ์ฒด ์ฃผ์ž… ์„ ํƒ๊ถŒ์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ฒซ๋ฒˆ์งธ๋Š” @Primary ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ, @Qualifer ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

@Primary ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์• ๋งคํ•œ ์ƒํ™ฉ์˜ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๊ตฌํ˜„์ฒด์ค‘, ์‹ค์ œ๋กœ ์ฃผ์ž…๋ฐ›์„ ๊ฐ์ฒด ์œ„์— @Primary ์• ๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

@Repository
@Primary
public class MemoryMemberRepository implements MemberRepository {
// ์ดํ•˜ ์ƒ๋žต

 

๋˜ ํ•œ๊ฐ€์ง€ @Qualifer ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€, ๊ฐ ๊ตฌํ˜„์ฒด์— @Qualifer ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋ช…์นญ์„ ๋ช…์‹œํ•˜๊ณ , ์‹ค์ œ๋กœ ์ฃผ์ž…๋ฐ›๋Š” ๊ฐ์ฒด์—๋„ ์ฃผ์ž…๋ฐ›๋Š” ๋Œ€์ƒ์˜ @Qualifer ์• ๋…ธํ…Œ์ด์…˜์„ ์ผ์น˜์‹œ์ผœ์ฃผ๋ฉด ๋œ๋‹ค.

 

@Repository
@Qualifier("memoryMemberRepository")
public class MemoryMemberRepository implements MemberRepository {
// ์ดํ•˜ ์ƒ๋žต
@Repository
@Qualifier("jpaMemberRepository")
public class JpaMemberRepository implements MemberRepository {
// ์ดํ•˜ ์ƒ๋žต
@Service
public class MemberServiceImpl implements MemberService {

    private MemberRepository memberRepository;

    public MemberServiceImpl(@Qualifier("memoryMemberRepository") 
    	MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    
    // ์ดํ•˜ ์ƒ๋žต

 

4. ๋งˆ๋ฌด๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ ๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ ์„ค๊ณ„๋ฅผ ๋„์™€์ฃผ๋Š” ์Šคํ”„๋ง์˜ ๊ธฐ๋Šฅ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์•Œ์•„๋ณด์•˜๋‹ค. ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ์— ์˜์กดํ•œ ์„ค๊ณ„๋ณด๋‹ค ์Šคํ”„๋ง์ด ์™œ ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”์ง€, ์ข‹์€ ๊ฐ์ฒด์ง€ํ–ฅ ๋””์ž์ธ์ด ์–ด๋– ํ•œ ๊ฒƒ์ธ์ง€๋ฅผ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹๊นŒ ์‹ถ๋‹ค.

 

ํ•ด๋‹น ์‹œ๋ฆฌ์ฆˆ๋Š” ์ธํ”„๋Ÿฐ์˜ ๊น€์˜ํ•œ ๊ฐ•์‚ฌ๋‹˜ [๋งํฌ]'์Šคํ”„๋ง ํ•ต์‹ฌ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ'  ๊ฐ•์˜๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ
๋‚ด์šฉ๊ณผ ์†Œ์Šค๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.