빙응의 공부 블로그
[Spring]의존성 주입(DI) 본문
1. 의존성 주입에 대해
DI
두 객체 간의 관계를 결정해주는 디자인 패턴이다.
DI는 인터페이스를 사이에 둬서 클래스 레벨에서의 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있다.
의존성 주입의 필요성
public class Stage {
private Dicapprio dicapprio;
}
무대에서 디카프리오가 연기하고 있다고 생각해보자, 이 코드는 Stage가 Dicapprio의 기능에 의존하고 있다.
이렇게 두 객체 간의 관계를 맺어주는 것을 의존성 주입이라고 한다.
올바른 의존성 주입
public class Stage {
private Dicapprio dicapprio = new Dicapprio();
}
위 예시에는 아주 강하게 결합되어 있다는 문제점이 있다.
만약 디카프리오가 아닌 다른 연기자가 연기를 한다면 Stage 클래스의 본문을 변경해야한다.
이것은 OCP (확장에는 열려있고 변경에는 닫혀있어야한다.) 위반에 해당한다.
그렇다면 올바른 의존성 주입은 어떻게 해야할까?
관심사의 분리
- 애플리케이션을 하나의 공연이라 생각해보자, 각각의 인터페이스를 배역이라 생각하는 것이다.
- 로미오와 줄리엣 공연을 하면 로미오 역할을 누가 할지, 줄리엣 역할을 누가 할지에 대해 선택해야 한다. 그런데 배우가 배역을 정하는 것이 아니다.
- 위 예제 코드들은 배역을 하는 배우들이 직접 다른 배역을 정하고 있다.
관심사를 분리하자
- 배우는 자신의 역할만 수행하면 된다.
->클래스는 자신의 기능만 가지면 된다.
- 역할을 섭외하는 것은 공연의 기획자이다.
-> 즉 역할을 결정하는 클래스를 따로 두자
위의 방법을 하기 위해서는 우선 다형성이 필요하다. 디카프리오를 연기자로 정의하는 것이다.
public interface Actor{}
--------------------------------
public class Dicapprio implements Actor{}
이제 우리는 스테이지와 디카프리오의 강하게 결합된 것을 제거해야한다.
비어있는 무대에서 연기자가 선다고 하지 디카프리오가 선다고 하지는 않는다.
public class Stage{
private Actor actor;
public Stage(Actor actor){
this.actor = actor;
}
}
단 위 코드처럼 하면 Actor 기능을 구현하지 않아 에러가 나오게 된다.
그러므로 누군가가 인터페이스를 할당을 해줘야 한다.
public class Provider{
public void Stage(){
//Bean 생성
Actor Dicapprio = new Dicapprio();
//의존성 주입
Stage stage = new stage(Dicapprio);
}
}
이러한 이유로 우리는 Spring의 DI 컨테이너가 필요하다. Stage에서 Actor을 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체를 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야한다.
Spring의 DI 컨테이너는 객체 간의 의존성을 주입하는 작업을 개발자가 직접 수행하지 않아도 할 수 있게 도와준다.
Spring DI 컨테이너 의존성 주입 방법
생성자 주입(Constructor Injection)
생성자를 통해 의존 관계를 주입한다.
@Service //Spring MVC 지정
public class MyService {
private final MyDependency dependency;
@Autowired //의존성 주입을 위한 어노테이션
public MyService(MyDependency dependency) {
this.dependency = dependency;
}
// ...
}
생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장(싱글톤으로 빈에서 관리)된다. 그렇기 때문에 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 강제하기 위해 사용할 수 있다.
세터 주입(Setter Injection)
세터 주입은 필드 값을 변경하는 Setter를 통해서 의존 관계를 주입하는 방법이다.
주로 주입받은 객체가 변경될 가능성이 있는 경우에 사용한다
@Service
public class MyService {
private MyDependency dependency;
@Autowired
public void setDependency(MyDependency dependency) {
this.dependency = dependency;
}
// ...
}
필드 주입
필드 주입은 필드에 바로 의존 관계를 주입하는 방법이다.
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// UserService의 다양한 메서드들...
// userRepository를 사용하는 메서드들...
}
필드 주입을 이용하면 코드가 간결해진다. 하지만 필드 주입은 외부에서 접근이 불가능하다는 단점이 존재한다.
테스트 코드의 중요성이 점점 높아지는 가운데 필드의 객체를 수정할 수 없는 필드 주입은 점점 사용하지 않는 추세이다.
또한 필드 주입은 반드시 DI 프레임워크가 존재해야한다.
생성자 주입을 사용해야한다.
생성자 주입을 사용해야 하는 이유
- 객체의 불변성 확보
- 테스트 코드의 작성이 편함
- final 키워드 작성, Lombok과도 잘맞음
- 스프링에 비침투적인 코드 작성
- 순환 참조 에러 방지
1. 객체의 불변성 확보
실제로 개발을 하다 보면 의존 관계의 변경이 필요한 상황이 거의 없다. 하지만 수정자 주입이나 일반 메소드 주입을 이용하면 불필요하게 수정의 가능성을 열어두어 유지보수성을 떨어트린다.
그러므로 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장해야 좋다.
2. 테스트 코드의 작성
테스트가 특정 프레임워크에 의존하는 것은 침투적이므로 좋지 못하다, 그러므로 가능한 순수 자바로 테스트 코드를 작성하는 것이 좋은데, 생성자 주입이 아닌 다른 주입으로 작성된 코드는 다시 순수한 자바 코드로 작성해 단위 테스트를 해야한다.
5. 순환 참조 에러 방지
순환 참조란 객체 간의 의존성이 서로 무한히 참조하는 상황을 나타낸다. 즉 A 객체가 B객체를 의존하고, B객체가 A객체를 동시에 의존하는 경우를 의미한다.
참고 사이트
[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2) - MangKyu's Diary (tistory.com)
[Spring] 다양한 의존성 주입 방법과 생성자 주입을 사용해야 하는 이유 - (2/2)
Spring 프레임워크의 핵심 기술 중 하나가 바로 DI(Dependency Injection, 의존성 주입)이다. Spring 프레임워크와 같은 DI 프레임워크를 이용하면 다양한 의존성 주입을 이용하는 방법이 있는데, 각각의 방
mangkyu.tistory.com
[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의 (inflearn.com)
'Spring > 개인공부_이론' 카테고리의 다른 글
[Spring]타임리프(Thymeleaf)에 대해 알아보자! 2편 (1) | 2024.01.21 |
---|---|
[Spring]타임리프(Thymeleaf)에 대해 알아보자! (0) | 2024.01.17 |
[Spring]로그에 대해 알아보자! - 1편 (0) | 2024.01.14 |
[스프링/Validation] 유효성 검사 쉽게 하기 (0) | 2023.11.15 |
[스프링] DTO 왜 써야 해? (1) | 2023.11.15 |