728x90
📌 Spring 삼각형
스프링이란, IoC와 AOP를 지원하는 경량의 컨테이너 프레임 워크다.
📌 DI (Dependency Injection)
- DI는 의존관계 주입이라고 부른다.
- 의존관계 주입은 어떤 객체 내부에서 특정 객체를 직접 생성해서 사용하는 것이 아니라 외부에서 생성한 후 주입 받은 후 사용하는 방법이다.
- 의존관계 주입은 어떤 객체 내부에서 특정 객체를 직접 생성해서 사용하는 것이 아니라 외부에서 생성한 후 주입 받은 후 사용하는 방법이다.
- 의존관계는 정적인 클래스 의존관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존관계 둘을 분리해서 생각해야 한다.
- 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입이라고 한다.
- 의존관계 주입을 사용하면 클라이언트 코드를 변경하지 않고, 클라이언트가 호출하는 대상의 타입 인스턴스를 변경할 수 있다.
- DI(의존관계 주입 or 의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.
- DI는 IoC(제어의 역전) 프로그래밍 모델을 구현하는 방식 중 하나다.
- 스프링에서는 IoC를 구체적으로 DI라는 방식을 통해서 제어의 역전을 형성하고 있다.
🔍 DI 실생활 예시
- 장난감들은 배터리가 있어야 움직일 수 있다. 즉, 배터리에 의존하고 있다.
- 장난감에 배터리를 넣는 것이 의존성 주입이다.
- 배터리 일체형인 경우에는 생성자에서만 의존성을 주입해주는 상황이라 배터리를 교체할수가 없다. 유연하지 못한 설계 방식이다.
- 배터리 일체형처럼 클래스 내부에 new 연산자를 통해 새로운 클래스를 만드는 방식은 강현 결합력을 가지고 있다.
- 배터리 분리형인 경우 생성자 주입 방식 또는 setter 메서드를 이용하여 배터리를 외부에서 주입받고 있다. 이처럼 외부에서 주입 받는 방식은 유연한 설계가 가능하다. 스프링에서는 이 방법을 권장하고 있다.
- 배터리 분리형처럼 클래스 외부에서 인터페이스 타입으로 다른 클래스의 객체를 받는 방식은 느슨한 결합이라고 한다.
🔍 의존관계 주입하는 방법
- 생성자 주입 (Consturctor Injection)
- 수정자 주입 (Setter Injection)
- 필드 주입 (Field Injection)
- 일반 메서드 주입 (method Injection)
✔ 생성자 주입
- 객체가 생성되는 시점에 의존성이 주입된다.
- 최근에 가장 많이 쓰이며 생성자를 1개만 만들어서 @Autowired를 생략한다.
- 한 번에 여러개의 의존관계를 주입 받을 수 있다.
- 필드값의 불변성(final)을 보장한다.
✔ 수정자 주입
- 객체의 SetXxxx()가 호출되는 시점에 의존성이 주입된다.
- 필드값을 변경할 수 있기 때문에 특별한 경우에만 쓴다.
- 한 번에 오직 한 개의 의존관계를 주입 받을 수 있다.
✔ 필드 주입
- 객체의 인스턴스 필드에 의존성을 주입한다.
- 안 쓰는것이 좋다.
💡 수정자 주입 방식 단점
- 런타임에 수정자(set)메서드를 통해서 객체를 건드리는 습관은 좋지 않다.
- 이 경우 NullPointerException이 발생하거나 예상치 못한 버그가 발생할 확률이 높다.
- 그렇기 때문에 객체의 멤버변수는 항상 불변객체(final)로 해주는 습관이 좋다.
🧷 생성자 주입 방식이 제일 안전하다.
✔ 단일 책임의 원칙(SRP)을 지키기 쉽다.
한눈에 해당 객체에 대한 의존관계와 복잡도를 알 수 있다.
✔ 테스트가 용이하다.
DI 컨테이너를 사용하지 않고도 인스턴스화 할 수 있고, 다른 DI 프레임워크로 쉽게 바꿀 수 있다.
✔ Immutablity
객체의 불변성(final)을 보장할 수 있다.
✔ 순환 의존성
생성자 주입에서는 객체가 서로 순환 의존성을 가질 경우 BeanCurrentlyCreationException이 발생한다.
🔍 의존성 예시
@Component
public class BookService {
private BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
@Component
public class BookRepository {
}
- 위 코드와 같이 BookService 클래스는 BookRepository 클래스가 필요하다. 이 때 BookService 클래스는 BookRepository 클래스의 의존성을 가진다 라고 한다.
- 외부 클래스 객체를 어떤 방식으로 받냐에 따라서 결합도가 높고 낮음이 결정된다.
- 위 코드는 낮은 결합 방식이다. 스프링은 낮은 결합 방식을 선호한다.
💡 강한 결합
@Component
class B {}
@Component
class C {}
@Component
class A {
B b = new B();
}
- 객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 가진다.
- A 클래스 내부에서 B 객체를 직접 생성하고 있다면, B 객체를 C 객체로 바꾸고 싶은 경우에 A 클래스도 수정해야하는 방식이기 때문에 강한 결합이다.
💡 느슨한 결합
interface Repository {}
@Component
class B implements Repository {}
@Component
class C implements Repository {}
@Component
class A {
private final Repository repository;
A(Repository repository) {
this.repository = repository;
}
}
- 객체를 주입 받는 것은 외부에서 생성된 객체를 인터페이스 타입을 통해서 넘겨 받는 것이다.
- 이렇게 하면 결합도를 낮출 수 있고, 런타임시에 의존관계가 결정되기 때문에 유연한 구조를 가진다.
- 위 코드처럼 생성자 주입 방식이 제일 많이 쓰인다.
👀 참고 자료
- https://biggwang.github.io/2019/08/31/Spring/IoC,%20DI%EB%9E%80%20%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C/#-3
- https://devlog-wjdrbs96.tistory.com/165?category=882236
728x90
'[ Spring ] > Spring' 카테고리의 다른 글
[Spring] HttpServletRequest 객체 (0) | 2022.02.06 |
---|---|
[Spring] 빈(Bean) 스코프(scope) (0) | 2022.01.31 |
[Spring] 빈(Bean) 생명주기 콜백 시작 (0) | 2022.01.29 |
[Spring] 조회한 빈(Bean)을 List, Map에 담기 (0) | 2022.01.24 |
[Spring] 스프링 빈(Bean) 등록 수동, 자동 결정 기준 (0) | 2022.01.24 |