DI(의존관계 주입, Dependency Injection)란 무엇인가?
의존관계 주입(Dependency Injection, DI)은 객체 간의 의존성을 외부에서 주입받는 설계 방식을 말한다.
이 방식은 객체를 직접 생성하거나 내부에서 의존성을 설정하지 않고, 외부에서 필요한 의존성을 주입함으로써 결합도를 낮추고 코드의 유연성과 재사용성을 높인다.
DI의 핵심 개념
- 의존성(Dependency)
- 한 객체가 다른 객체의 기능이나 데이터를 사용할 때, 해당 객체는 그 다른 객체에 의존한다고 말한다.
- 예를 들어, OrderService가 PaymentService를 호출해 결제를 처리해야 한다면, OrderService는 PaymentService에 의존한다.
- 의존관계 주입(Dependency Injection)
- 객체가 필요한 의존성을 스스로 생성하지 않고, 외부에서 주입받는 방식이다.
- 이 과정은 보통 Spring Framework의 IoC 컨테이너에 의해 자동으로 처리된다.
DI의 주입 방법
Spring에서는 다음 세 가지 방법으로 의존성을 주입할 수 있다.
1. 필드 주입 (Field Injection)
필드에 직접 @Autowired를 붙여 의존성을 주입하는 방법이다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
public void processOrder(String orderId) {
paymentService.processPayment(orderId);
}
}
- 장점: 간결하다.
- 단점: 테스트 시 의존성을 직접 주입하기 어려우며, 객체 생성 시 의존성 설정이 명확하지 않다.
2. Setter 주입 (Setter Injection)
Setter 메서드를 통해 의존성을 주입하는 방법이다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void processOrder(String orderId) {
paymentService.processPayment(orderId);
}
}
- 장점: 선택적 의존성을 처리하기에 적합하다.
- 단점: 객체가 생성된 이후에도 의존성을 변경할 수 있어 불변성을 보장하지 못한다.
3. 생성자 주입 (Constructor Injection)
생성자를 통해 의존성을 주입하는 방법이다. 가장 권장되는 방식이다.
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void processOrder(String orderId) {
paymentService.processPayment(orderId);
}
}
- 장점:
- 불변성 보장: final 키워드를 사용해 주입된 의존성이 변경되지 않음을 명확히 할 수 있다.
- 필수 의존성 명시: 생성자에 명시적으로 필요한 의존성을 선언할 수 있다.
- 테스트 용이성: 의존성을 주입받는 객체를 쉽게 Mock 객체로 대체할 수 있다.
Spring에서 DI를 사용하는 이유
- 결합도 감소
- DI는 객체 간 강한 결합을 제거하고, 인터페이스를 통해 의존성을 주입받아 결합도를 낮춘다.
- 코드 재사용성 증가
- DI를 통해 객체가 독립적으로 설계되므로 다른 환경에서도 재사용이 가능하다.
- 테스트 용이성
- 의존성을 외부에서 주입받기 때문에, 테스트 환경에서 Mock 객체를 쉽게 주입할 수 있다.
TIP: 기본적으로 알아야할 JAVA의 final 키워드
Java에서 final과 값 할당
Java에서 final 키워드는 변수의 값이 한 번만 할당될 수 있음을 보장한다.
즉, final 변수는 초기화 후 값이 변경될 수 없다. 이 속성은 **불변성(Immutability)**을 제공한다.
final 사용 시 값 할당 규칙
1. 즉시 초기화
final 변수는 선언과 동시에 값을 할당해야 한다.
public class FinalExample {
private final String message = "Hello, Java!";
public String getMessage() {
return message;
}
}
2. 생성자에서 초기화
클래스에 final 필드가 있을 경우, 모든 생성자에서 해당 필드에 값을 할당해야 한다.
public class FinalExample {
private final String message;
// 생성자를 통한 초기화
public FinalExample(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
3. 초기화되지 않은 경우 컴파일 오류 발생
만약 final 필드에 값이 할당되지 않으면 컴파일 오류가 발생한다.
public class FinalExample {
private final String message;
public FinalExample() {
// message 값을 초기화하지 않았으므로 컴파일 오류 발생
}
}
4. final 필드는 변경 불가능
값이 한 번 할당된 후에는 수정이 불가능하다.
public class FinalExample {
private final String message = "Hello, Java!";
public void setMessage(String newMessage) {
// 컴파일 오류: final 변수는 값을 변경할 수 없음
// this.message = newMessage;
}
}
final이 제공하는 이점
- 불변성 보장
- 한 번 초기화된 값은 변경되지 않아, 안정성과 예측 가능성을 제공한다.
- 스레드 안전성
- final 필드는 여러 스레드가 동시에 접근하더라도 값이 변경되지 않으므로 스레드 안전하다.
- 가독성 증가
- final 키워드를 사용하면 해당 변수가 변경되지 않음을 명시적으로 알릴 수 있어 코드의 가독성이 높아진다.
DI의 총 정리 + final 키워드 정리
- DI는 Spring의 핵심 기능이며, 이를 효과적으로 활용하면 유연성과 재사용성이 높은 코드를 작성할 수 있다.
- 또한, final 키워드를 활용해 불변성을 보장함으로써 안정적인 객체 설계를 할 수 있다.
- 이 글을 읽게 될 독자는 Spring 프로젝트를 설계하면서 이 두 가지를 적극 활용하면 추가 학습에 큰 도움이 될 수도 있다(mins)
'JAVA > Spring' 카테고리의 다른 글
Spring @ComponentScan의 includeFilters와 excludeFilters 완벽 이해 (0) | 2024.12.21 |
---|---|
Spring의 ComponentScan 이해하기: @ComponentScan의 동작 원리와 활용법 (1) | 2024.12.21 |
Spring에서 생성자에 @Autowired 어노테이션을 사용하는 이유와 장점 (0) | 2024.12.20 |
[스프링 싱글톤] : 왜 @Bean은 한 번만 호출될까? (0) | 2024.12.20 |
[스프링 빈과 싱글톤 패턴: 무상태로 설계해야 하는 이유?] (0) | 2024.12.17 |
댓글