도대체 IOC?
1. IoC(Inversion of Control)란?
의존성 관리를 외부에 위임
하여 객체의 생성과 관리를 제어하는 개발 패턴
2. 누가? 왜?
- 누구한테 위임하는가?
주로 프레임 워크에 위임합니다. 대표적으로 Spring 프로레임워크라고 볼 수 있겠습니다.
- 왜 위임하는가?
개인적으로 생각하는 위임하는 주요 장점중 하나는 개발자가 비즈니스 로직에 집중할 수 있다는 것입니다.
그렇다면, 의존성을 프레임워크에 위임하고 개발자는 비즈니스 로직 개발에 집중 할 수 있다.
정도로 보면 되겠네요.
3. 예시
DI에서 활용했던 코드를 사용해서 탐구해보겠습니다.
일반적으로 개발자가 객체를 생성관리 한다면 다음과 같을 것입니다.
LottoService lottoService = new LottoService(new RandomNumberGenerator());
그렇다면, 객체생성을 프레임워크에 위임하다고 하는데 그러한 패턴을 코드로 우선 보겠습니다.
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
LottoService myService = context.getBean(LottoService.class);
ApplicationContext
란 객체에게 LottoService.class타입
의 Bean
을 달라고 하네요 🤔
그런데 여기서 중요한 부분은 확실하게 new
를 사용하지 않은것으로 봐서는 객체 생성을 프레임워크에 위임한 것으로 볼 수 있습니다.
🚫그런데 실행했더니
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.LottoService' available
에러가 발생하네요 🤔
Spring 의 마법을 보기위해서 무언가 설정이 더 필요한가 봅니다. 😂
생각해보니 그럴 수 있을것 같아요.
왜냐면 무엇을 Bean
으로 관리해줘라고 설정하지 않았거든요.
Bean
등록 방법은 엄청 간단합니다!
XML
,Java Config
, Component Scan
이렇게 Bean
을 등록하는 방법이 있는데요. 이번에는 많이 사용하는 Component Scan
방식을 사용해보겠습니다.
@Service
어노테이션이란 것만 붙여주면 됩니다. 그러면 Bean
으로 등록해줍니다.
@Service
public class LottoService {
private final NumberGenerator numberGenerator;
public LottoService(NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}
}
다시 실행해보겠습니다.
Parameter 0 of constructor in com.example.demo.LottoService required a bean of type 'com.example.demo.NumberGenerator' that could not be found.
에러가 발생하네요 🤔
LottoService
생성자의 0번째 파라미터(NumberGenerator
)를 Bean
에서 찾을 수 가 없데요 😱
Bean
으로 관리하는 객체라면 당연히 생성자 주입해야하는 객체 또한 Bean
에서 찾을수 있어야 할 것같네요 😂
그럼 NumberGenerator
도 Bean
으로 등록해주면 됩니다.
@Component
어노테이션을 붙여주면 됩니다. 그러면 Bean
으로 등록해줍니다.
@Component
public class RandomNumberGenerator implements NumberGenerator {
public RandomNumberGenerator() {
}
@Override
public int generate() {
Random random = new Random();
return random.nextInt(45) + 1;
}
}
드디어 장상작동 되네요.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.0.6)
2023-05-17T17:10:59.007+09:00 INFO 73775 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 17.0.6 with PID 73775
2023-05-17T17:10:59.008+09:00 INFO 73775 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2023-05-17T17:10:59.167+09:00 INFO 73775 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.267 seconds (process running for 0.447)
LottoNumber{value=41}
@Component, @Service… ??
@Component, @Service, @Repository, @Controller
등의 어노테이션을 사용하여 Bean으로 등록할 클래스에 지정 할 수 있습니다.
스프링 부트는 기본적으로 클래스패스를 스캔하여 어노테이션이 지정된 클래스를 자동으로 Bean으로 등록합니다.
이와 같은 방법을 사용하여 Bean을 등록하면 Spring IoC 컨테이너가 해당 Bean을 인식하고 필요한 곳에서 주입할 수 있습니다.
🤔그런데 NumberGenerator
구현체가 여러개면 무엇을 주입할지 어떻게 정할까?
@Primary
어노테이션을 사용하면 됩니다.
@Primary
어노테이션을 사용하면 여러개의 Bean 중에서 우선적으로 주입할 Bean을 지정할 수 있습니다.
@Component
@Primary
public class RandomNumberGenerator implements NumberGenerator {
public RandomNumberGenerator() {
}
@Override
public int generate() {
Random random = new Random();
return random.nextInt(45) + 1;
}
}
하지만 이렇게 사용하면 NumberGenerator
를 주입받는 모든 곳에서 RandomNumberGenerator
가 주입됩니다.
@Qualifier 어노테이션을 사용하면 주입할 Bean을 지정할 수 있습니다.
public LottoService(@Qualifier("manualNumberGenerator") NumberGenerator numberGenerator) {
this.numberGenerator = numberGenerator;
}
🧐어노테이션???
@Service
이런것을 클래스나 메소드 위에 붙이는 것을 말합니다.
쉽게 말해 주석입니다. 하지만 좀 더 의미있는 주석이라고 보면 됩니다.
프레임워크단에서 @Service
이게 붙어 있으면 아 이건 이런 기능을 하는 클래스 혹은 메소드구나 인식하도록 주석을 달아 놓았다고 보면 됩니다.
어노테이션은 런타임에 리플렉션을 통해 읽어들일 수도 있으며, 프레임워크나 라이브러리에서 특정 기능을 활성화하거나 설정할 때 사용될 수도 있습니다.