Home > SpringBoot > πŸ›οΈ[SpringBoot] μŠ€ν”„λ§ λΆ€νŠΈμ˜ 핡심 κ°œλ…`:` μ œμ–΄μ˜ μ—­μ „(IoC) - μŠ€ν”„λ§μ—μ„œμ˜ μ œμ–΄μ˜ μ—­μ „

πŸ›οΈ[SpringBoot] μŠ€ν”„λ§ λΆ€νŠΈμ˜ 핡심 κ°œλ…`:` μ œμ–΄μ˜ μ—­μ „(IoC) - μŠ€ν”„λ§μ—μ„œμ˜ μ œμ–΄μ˜ μ—­μ „
SpringBoot

πŸ›οΈ[SpringBoot] μŠ€ν”„λ§ λΆ€νŠΈμ˜ 핡심 κ°œλ…: μ œμ–΄μ˜ μ—­μ „(IoC) - μŠ€ν”„λ§μ—μ„œμ˜ μ œμ–΄μ˜ μ—­μ „

πŸ“‹ λͺ©μ°¨

  1. μ˜μ‘΄μ„± μ£Όμž…μ˜ κΈ°λ³Έ
  2. 닀쀑 빈 λ¬Έμ œμ™€ ν•΄κ²°μ±…
  3. μ‹€μ œ μ‚¬μš© 예제
  4. 정리

μ˜μ‘΄μ„± μ£Όμž…μ˜ κΈ°λ³Έ

Springμ—μ„œ @Autowired μ• λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ©΄, μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 객체λ₯Ό μžλ™μœΌλ‘œ μ°Ύμ•„μ„œ μ£Όμž…ν•΄μ€λ‹ˆλ‹€.

기본 ꡬ쑰 예제

// μΈν„°νŽ˜μ΄μŠ€
public interface CoffeeMachine {
    String brew();
}

// κ΅¬ν˜„μ²΄
@Component
public class EspressoMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Espresso Machine";
    }
}

// μ˜μ‘΄μ„± μ£Όμž…
@Component
public class CoffeeMaker {
    @Autowired
    private CoffeeMachine coffeeMachine;
    
    @PostConstruct
    public void makeCoffee() {
        System.out.println(coffeeMachine.brew());
    }
}

닀쀑 빈 λ¬Έμ œμ™€ ν•΄κ²°μ±…

🚨 문제 상황

λ™μΌν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ ν΄λž˜μŠ€κ°€ μ—¬λŸ¬ 개 μžˆμ„ λ•Œ λ¬Έμ œκ°€ λ°œμƒν•©λ‹ˆλ‹€:

@Component
public class EspressoMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Espresso Machine";
    }
}

@Component
public class DripCoffeeMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Drip Coffee Machine";
    }
}

이 경우 λ‹€μŒκ³Ό 같은 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€:

*********************
APPLICATION FAILED TO START
*********************

Description:
Field coffeeMachine in com.example.demo.CoffeeMaker required a single bean
but 2 were found:
    - dripCoffeeMachine: defined in file [...\DripCoffeeMachine.class]
    - espressoMachine: defined in file [...\EspressoMachine.class]

Action:
Consider marking one of the beans as @Primary, updating the consumer to accept
multiple beans, or using @Qualifier to identify the bean that should be consumed

πŸ’‘ ν•΄κ²° 방법

방법 1: @Primary μ‚¬μš©

μš°μ„ μˆœμœ„κ°€ 높은 λΉˆμ„ μ§€μ •ν•©λ‹ˆλ‹€:

@Component
@Primary  // 기본으둜 선택될 빈
public class DripCoffeeMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Drip Coffee Machine";
    }
}

@Component
public class EspressoMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Espresso Machine";
    }
}

방법 2: @Qualifier μ‚¬μš©

λΉˆμ— 이름을 μ§€μ •ν•˜κ³ , μ£Όμž…λ°›μ„ λ•Œ νŠΉμ • 이름을 λͺ…μ‹œν•©λ‹ˆλ‹€:

// 1. 빈 등둝 μ‹œ 이름 μ§€μ •
@Component("dripCoffeeMachine")
public class DripCoffeeMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Drip Coffee Machine";
    }
}

@Component("espressoMachine")
public class EspressoMachine implements CoffeeMachine {
    @Override
    public String brew() {
        return "Brewing coffee with Espresso Machine";
    }
}

// 2. μ£Όμž…λ°›μ„ λ•Œ νŠΉμ • 빈 μ§€μ •
@Component
public class CoffeeMaker {
    @Autowired
    @Qualifier("dripCoffeeMachine")  // νŠΉμ • 빈 선택
    private CoffeeMachine coffeeMachine;
    
    @PostConstruct
    public void makeCoffee() {
        System.out.println(coffeeMachine.brew());
    }
}

방법 3: λͺ¨λ“  λΉˆμ„ 리슀트둜 μ£Όμž…

λͺ¨λ“  κ΅¬ν˜„μ²΄λ₯Ό μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ List둜 μ£Όμž…λ°›μŠ΅λ‹ˆλ‹€:

@Component
public class CoffeeMaker {
    @Autowired
    private List<CoffeeMachine> coffeeMachines;  // λͺ¨λ“  κ΅¬ν˜„μ²΄ μ£Όμž…
    
    @PostConstruct
    public void makeCoffee() {
        for (CoffeeMachine coffeeMachine : coffeeMachines) {
            System.out.println(coffeeMachine.brew());
        }
    }
}

좜λ ₯ κ²°κ³Ό:

Brewing coffee with Drip Coffee Machine
Brewing coffee with Espresso Machine

μ‹€μ œ μ‚¬μš© 예제

μ‹œλ‚˜λ¦¬μ˜€λ³„ μ‚¬μš©λ²•

🎯 νŠΉμ • κ΅¬ν˜„μ²΄λ§Œ μ‚¬μš©ν•˜κ³  싢은 경우

@Service
public class CafeService {
    @Autowired
    @Qualifier("premiumEspressoMachine")
    private CoffeeMachine coffeeMachine;
}

🎯 κΈ°λ³Έ κ΅¬ν˜„μ²΄λ₯Ό μ„€μ •ν•˜κ³  싢은 경우

@Component
@Primary
public class DefaultCoffeeMachine implements CoffeeMachine {
    // 기본으둜 μ‚¬μš©ν•  κ΅¬ν˜„μ²΄
}

🎯 λͺ¨λ“  κ΅¬ν˜„μ²΄μ— λŒ€ν•΄ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κ³  싢은 경우

@Service
public class CoffeeTestService {
    @Autowired
    private List<CoffeeMachine> allMachines;
    
    public void testAllMachines() {
        allMachines.forEach(machine -> 
            System.out.println("Testing: " + machine.brew())
        );
    }
}

정리

πŸ”‘ 핡심 포인트

  1. κΈ°λ³Έ λ™μž‘: @AutowiredλŠ” μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ— λ§žλŠ” λΉˆμ„ μžλ™μœΌλ‘œ μ£Όμž…
  2. 닀쀑 빈 문제: 같은 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ 빈이 μ—¬λŸ¬ 개일 λ•Œ 좩돌 λ°œμƒ
  3. ν•΄κ²° 방법:
    • @Primary: μš°μ„ μˆœμœ„ μ§€μ •
    • @Qualifier: νŠΉμ • 빈 선택
    • List<T>: λͺ¨λ“  빈 μ£Όμž…

⚑ 베슀트 ν”„λž™ν‹°μŠ€

  • λͺ…ν™•ν•œ 빈 이름 μ‚¬μš©: @Component("ꡬ체적인이름")
  • @Primary 적절히 ν™œμš©: 기본값이 λͺ…ν™•ν•  λ•Œ
  • @Qualifier 적극 ν™œμš©: νŠΉμ • κ΅¬ν˜„μ²΄κ°€ ν•„μš”ν•  λ•Œ
  • μΈν„°νŽ˜μ΄μŠ€ 섀계: μ—­ν• κ³Ό μ±…μž„μ„ λͺ…ν™•νžˆ 뢄리

🌟 Spring IoC의 핡심 μ² ν•™

Spring μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλŠ” λͺ¨λ“  ν΄λž˜μŠ€κ°€ μŠ€ν”„λ§ 빈으둜 λ“±λ‘λ˜κ³ , μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆκ°€ μ˜μ‘΄μ„±μ„ κ΄€λ¦¬ν•©λ‹ˆλ‹€. κ°œλ°œμžλŠ” 객체 생성과 μ˜μ‘΄μ„± μ£Όμž…μ„ 직접 κ΄€λ¦¬ν•˜μ§€ μ•Šκ³ , Spring이 이λ₯Ό λŒ€μ‹  μ²˜λ¦¬ν•˜μ—¬ λŠμŠ¨ν•œ κ²°ν•©(Loose Coupling) κ³Ό 높은 ν…ŒμŠ€νŠΈ κ°€λŠ₯성을 μ œκ³΅λ°›μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.