[Index]
▷ 의존성 주입 (DI)란?
▷ 의존성 주입을 위한 설정 (DI 컨테이너에 Spring Bean(객체)을 등록하기 위한 설정)
▷ 의존성 주입 사용 방법 (DI를 통해 만들어진 객체를 다른 클래스에서 사용하는 방법)
▷ 사용 방법 중 어떤 방법이 가장 권장되는 방법이고, 그 이유는 무엇일까?
의존성 주입 (DI)란?
우리는 스프링프레임워크나 스프링부트 프로젝트를 하게되면
스프링의 특성을 사용하게 된다.
그 중 중요한 특성인 DI에 대해 설명하려한다.
DI (Dependency Injection) : 의존성 주입. (필요한, 원하는 객체 알아서 주입해줘.)
[의존성 주입에 대한 예시]
A클래스 내에서 B클래스 사용이 필요하게 될 때,
우리는 'A클래스가 B클래스를 의존한다' 라고 표현한다.
(이는 의존대상 B가 변하면, 그것이 A에 영향을 미치는 것이다.)
필요한 A클래스를 B클래스에서 직접 객체 생성하여 사용하지 않는다.
스프링이 의존성 주입을 도와줘서
(빌드가 끝나자마자, DI 컨테이너에 이미 객체들이 만들어져 있다. 물론 객체가 미리 만들어지도록 개발자가 설정 가능)
B클래스에서는 이미 A클래스로 만들어진 A객체를 불러서 사용할 수 있다.
의존성 주입을 위한 설정 (DI 컨테이너에 Spring Bean(객체)을 등록하기 위한 설정)
1) XML 설정 파일을 주로 사용한 DI
초기 스프링 버전에서는 이렇게 xml 설정 파일을 사용하여 DI를 구현했다.
<bean id="myService" class="com.example.MyServiceImpl"> <property name="myRepository" ref="myRepository" /> </bean> <bean id="myRepository" class="com.example.MyRepositoryImpl" />
2) Java 기반의 DI (Java Config)
스프링 3.0 이후로 Java 설정 클래스를 사용하여 DI를 구성할 수 있다.
@Configuration 어노테이션을 사용하여 설정 클래스를 정의하고
@Bean 어노테이션을 사용하여 Bean을 생성하고 의존성을 주입한다.
@Configuration public class AppConfig { @Bean public MyRepository myRepository() { return new MyRepositoryImpl(); } @Bean public MyService myService() { return new MyServiceImpl(myRepository()); } }
3) 어노테이션 기반의 DI
스프링 2.5버전부터 어노테이션 기반의 DI가 지원되기 시작했다.
1),2)와 같은 방식으로 하게되면 스프링 빈이 수십, 수백개가 된다면 일일이 등록하기도 귀찮고, 설정 정보도 커지고, 누락하는 문제도 발생한다.
따라서, 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
또 의존관계도 자동으로 주입하는 @Autowired라는 기능도 제공한다.
ComponentScan과 함께 @Autowired, @Inject, @Resource 등의 어노테이션을 사용하여
자동으로 의존성 주입을 할 수 있게 되었다.
즉, Bean으로 등록된 객체를 사용할 수 있게 되었다는 의미이다.
@Component, @Controller, @Service, @Repository 등의 어노테이션을 사용하여
Bean으로 등록할 클래스를 지정한다.@Service public class MyServiceImpl implements MyService { @Autowired private MyRepository myRepository; }
의존성 주입 사용 방법 (DI를 통해 만들어진 객체를 다른 클래스에서 사용하는 방법)
DI는 의존관계를 외부에서 결정하는 것이기 때문에,
클래스 변수를 결정하는 방법들이 곧 DI를 구현하는 방법이다.
구현방법에는 크게 3가지가 있다.
● 생성자 주입
● Setter 주입
● 필드 주입
● 생성자 주입
생성자를 통해 의존성을 주입받는다.
가장 추천되는 방법이다.
그 이유는 객체가 생성되는 시점에 모든 의존성이 주입되므로
객체의 불변성을 보장할 수 있기 때문이다.
@Autowired 어노테이션을 생성자에 붙여 사용할 수 있으나,
스프링 4.3부터는 단 하나의 생성자만 있는 경우에는 @Autowired를 생략할 수 있다.
@Service public class MyService { private final MyRepository myRepository; public MyService(MyRepository myRepository) { this.myRepository = myRepository; } }
* 나는 프로젝트를 하면서,
private final로 DI를 통해 만들어진 객체를 다른 클래스에서 사용하도록 설정했다.
그리고 그 다른 클래스 상단에 @RequiredArgsConstructor 어노테이션을 붙였다.
이 어노테이션을 통해 final 키워드가 붙은 필드를 초기화하는 생성자를 자동으로 생성해 주도록 했다.
● Setter 주입
의존하는 객체의 setter 메서드를 정의하고,
그 메서드를 통해 의존성을 주입받는다.
@Autowired 어노테이션을 setter 메서드에 붙여 사용할 수 있다.
@Service public class MyService { private MyRepository myRepository; @Autowired public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository; } }
● 필드 주입
직접 필드에 @Autowired 어노테이션을 붙여 의존성을 주입받는다.
이 방법은 테스트하기 어렵다는 단점이 있어 추천되지 않는다.
@Service public class MyService { @Autowired private MyRepository myRepository; }
사용 방법 중 어떤 방법이 가장 권장되는 방법이고, 그 이유는 무엇일까?
위에서 이미 언급했지만
생성자 주입이다.
개발자와 조직에서 생성자 주입을 권장하고 있는 이유는 다음과 같다.
1) 불변성 :
생성자 주입을 사용하면 해당 필드를 final로 선언할 수 있다. 이렇게 하면 해당 객체가 생성된 후에는 해당 필드의 참조를 변경할 수 없게 되어 불변성을 갖게 된다. 이로 인해 객체의 상태가 안정적이 되며, 불변성은 여러 멀티 스레드 환경에서도 안정성을 보장한다.
2) 명시성:
생성자를 통해 어떤 의존성이 필요한지 명확하게 볼 수 있다. 그렇기 때문에 해당 클래스가 어떤 외부 의존성을 필요로 하는지 쉽게 파악할 수 있다.
3) 순환 참조 감지:
생성자 주입을 사용하면 스프링은 빈이 생성될 때 순환 참조가 있는지 확인한다. 순환 참조가 발생하면 애플리케이션은 시작되지 않는다. 이는 애플리케이션에 잠재적인 문제가 있는 경우 미리 알려준다.
4) 의존성 주입을 강제:
필요한 의존성 없이 객체를 생성할 수 없도록 강제한다. 이는 개발 중에 필요한 의존성을 놓칠 가능성을 줄여준다.
5) Setter 주입의 문제점을 피할 수 있음:
Setter 주입은 객체가 완전히 초기화된 상태로 사용되지 않을 위험이 있다. 생성자 주입은 모든 의존성이 제공되지 않으면 객체 생성 자체가 불가능하므로, 이런 문제를 피할 수 있다.
6) 단순함:
대부분의 경우, 생성자는 한번만 호출되므로 의존성 주입 로직이 여러 군데에 퍼져 있지 않고 중앙 집중화되어 있다.
'개인 공부 (23.07~' 카테고리의 다른 글
Docker? 가상화? 리눅스? 클라우드 서비스? 커널? 이해 (0) | 2023.08.21 |
---|---|
MyBatis 파라미터 바인딩 방법 2가지 (1) | 2023.08.18 |
[HTTP Method] 8가지 개념 (0) | 2023.08.15 |
[REST API] 개념, 특징, URI 규칙 이해하기 (0) | 2023.08.15 |
MVC 패턴 쉽게 이해하기 (0) | 2023.08.15 |