[1주차 과제]
1. 의존성 주입방식 3가지(생성자, Setter, Field)에 대해서 예제 코드와 함께 이론 정리
2. 스프링 공식 문서에서 생성자 주입 방식 사용을 정식으로 권고하고 있는데 그 이유가 무엇인지 정리하고 예제코드 작성하기
3. 객체지향 관점에서 DI 방식으로 의존성 문제를 해결하면 어떤 장점이 있는지 예제코드로 정리하기
4. 주변 동료들의 블로그에 들어가서 내 글과 비교해보고 회고록 작성해보기!
@Autowired
: Spring의 의존성 주입 기능에 의해 자동으로 연결되도록 생성자, 필드, setter 메서드 또는 구성 메서드를 표시합니다. 한마디로 "나는 여기에 스프링이 의존성을 자동으로 주입해 주기를 바란다." 라고 명시하는 어노테이션
생성자 주입은 객체를 생성할 때, 해당 객체가 의존하는 다른 객체들을 생성자 매개변수로 받아들이는 방식입니다. 이렇게 주입받은 의존성을 객체 내부의 멤버 변수에 할당하여 사용합니다.
@Service
public class Client {
private final CocoService cocoService;
@Autowired
public Client(CocoService cocoService) {
this.cocoService = cocoService;
}
Setter 주입은 의존성을 주입하는 메서드(Setter)를 통해 의존성을 설정하는 방식입니다. Setter 메서드는 객체 생성 후에 호출되어 의존성을 주입합니다.
@Service
public class Client {
private CocoService cocoService;
@Autowired
public void setCocoService(CocoService cocoService) {
this.cocoService = cocoService;
}
}
필드 주입은 의존성을 클래스의 멤버 변수로 직접 주입하는 방식입니다. 주로 리플렉션(Reflection)을 사용하여 의존성을 주입합니다.
@Service
class Client {
private CocoService cocoService;
@Autowired
private CocoService cocoService;
}
생성자 주입 방법은 필드 주입이나 수정자 주입과는 빈을 주입하는 순서가 다릅니다.
먼저 Setter 주입을 살펴보면 우선 주입 받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩터리에 등록합니다. 그 후에 생성자 인자에 사용하는 빈을 찾거나 만듭니다. 그 이후에 주입하려는 빈 객체의 수정자를 호출하여 주입합니다.
다음으로 필드 주입은 수정자 주입 방법과 동일하게 먼저 빈을 생성한 후에 어노테이션이 붙은 필드에 해당하는 빈을 찾아서 주입하는 방법입니다. 그러니까, 먼저 빈을 생성한 후에 필드에 대해서 주입합니다.
마지막으로 생성자 주입은 생성자로 객체를 생성하는 시점에 필요한 빈을 주입합니다. 조금 더 자세히 살펴보면, 먼저 생성자의 인자에 사용되는 빈을 찾거나 빈 팩터리에서 만듭니다. 그 후에 찾은 인자 빈으로 주입하려는 빈의 생성자를 호출합니다. 즉, 먼저 빈을 생성하지 않습니다. 수정자 주입과 필드 주입과 다른 방식입니다.
그렇기 때문에 순환 참조는 생성자 주입에서만 문제가 됩니다. 객체 생성 시점에 빈을 주입하기 때문에 서로 참조하는 객체가 생성되지 않은 상태에서 그 빈을 참조하기 때문에 오류가 발생합니다.
순환 참조가 있는 객체 설계는 잘못된 설계이기 때문에 오히려 생성자 주입을 사용하여 순환 참조되는 설계를 사전에 막아야 합니다.
대부분의 의존관계 주입은 한 번 일어나면 애플리케이션 종료 시점까지 의존 관계를 변경할 일이 거의 없습니다.
생성자 주입을 사용하면 객체를 생성할 때 한번만 호출되므로 객체가 불변하게 설계할 수 있습니다.
@Service
public class Client {
private final CocoService cocoService;
@Autowired
public Client(CocoService cocoService) {
this.cocoService = cocoService;
}
생성자 주입은 의존성을 외부로부터 주입받기 때문에 테스트 시 의존성을 직접 주입하여 단위 테스트를 쉽게 수행할 수 있습니다. 생성자 주입을 사용하면 Mock 객체 등을 주입하여 의존성을 대체하고 테스트하기 용이합니다.
public class cocoService {
private CocoRepository cocoRepository;
public cocoService() {
this.cocoRepository = new CocoRepository(); // 의존성 직접 생성
}
public User getUserById(Long userId) {
return cocoRepository.getUserById(userId);
}
}
DI를 사용하여 의존성을 주입받는 방식으로 변경하면, cocoService cocoRepository를 생성하지 않고 외부에서 주입받아 사용합니다. 이로 인해 cocoService cocoRepository를 사이의 결합도가 낮아집니다.
public class CocoService {
private CocoRepository cocoRepository;
public CocoService(CocoRepository cocoRepository) {
this.cocoRepository = cocoRepository; // 의존성 주입
}
public User getUserById(Long userId) {
return cocoRepository.getUserById(userId);
}
}
단일 책임 원칙 준수 :
DI를 사용하여 의존성을 주입받는 방식으로 변경하면, cocoService 자신의 주요 기능에만 집중할 수 있습니다. cocoRepository를 생성과 관련된 책임은 외부에서 담당하므로, 단일 책임 원칙을 준수하게 됩니다.
테스트 용이성:
DI를 사용하여 의존성을 주입받는 방식으로 변경하면, 테스트 시에 cocoRepository를 Mock 객체로 대체하여 주입할 수 있습니다. 이렇게 하면 cocoService 테스트할 때 외부 의존성을 제어하고 테스트할 수 있습니다.