Spring 프레임워크의 핵심 기능 중 하나는 의존성 주입(DI, Dependency Injection)입니다. DI는 객체 간의 결합을 줄이고 코드의 유연성과 확장성을 높이는 설계 방식으로, 스프링에서는 이를 통해 객체 간의 관계를 간단하게 설정할 수 있습니다.
1. 의존성 주입(DI)란?
의존성 주입(DI)이란 객체가 사용할 의존성을 외부에서 주입해주는 설계 패턴입니다. 전통적인 객체 지향 프로그래밍에서는 객체 내부에서 필요한 의존성을 직접 생성하거나 설정합니다. 하지만 DI를 사용하면 외부에서 필요한 의존성을 전달받아 객체가 스스로 관리할 필요 없이 주어진 의존성만 사용할 수 있게 됩니다.
예시
public class OrderService {
private final PaymentService paymentService;
// 의존성 주입을 통해 OrderService는 PaymentService를 직접 생성하지 않고 주입받습니다.
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void placeOrder() {
paymentService.processPayment();
}
}
이 예시에서 OrderService는 PaymentService에 의존적이지만, 직접 생성하지 않고 생성자를 통해 외부에서 주입받습니다.
2. Spring에서의 DI 사용법
Spring에서는 다음과 같은 세 가지 주요 방식으로 의존성을 주입할 수 있습니다.
(1) 생성자 주입(Constructor Injection)
생성자를 통해 의존성을 주입하는 방식입니다. 가장 권장되는 방식으로, 불변 객체를 만들기 좋고 주입받을 의존성을 명확히 확인할 수 있습니다.
@Service
public class OrderService {
private final PaymentService paymentService;
// 생성자 주입
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
(2) 필드 주입(Field Injection)
필드에 직접 의존성을 주입하는 방식입니다. 간결하게 사용할 수 있으나, 테스트 시 주입된 필드에 접근하기 어려울 수 있으며, 의존성이 명확히 드러나지 않을 수 있어 권장되지 않습니다.
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
(3) 세터 주입(Setter Injection)
세터 메서드를 통해 의존성을 주입하는 방식입니다. 의존성이 선택적인 경우에 유용하게 사용할 수 있습니다.
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
3. Spring DI 작동 원리
Spring은 IoC(Inversion of Control) 컨테이너를 사용해 필요한 객체들을 관리하고 생성, 주입까지 처리합니다. 객체가 필요할 때마다 직접 생성하지 않고, Spring 컨테이너가 관리하는 빈(Bean)을 주입받아 사용하는 구조입니다.
- 빈 생성: Spring은 애플리케이션이 시작될 때 @Component, @Service, @Repository, @Controller 등으로 등록된 빈을 미리 생성하고 관리합니다.
- 의존성 주입: DI 설정이 완료되면, Spring이 자동으로 필요한 의존성을 주입해줍니다.
이 과정에서, Spring은 @Autowired 또는 @Inject와 같은 어노테이션을 사용해 빈 주입을 자동화합니다.
4. DI의 장점
DI를 사용하면 다음과 같은 이점이 있습니다:
- 유연성과 확장성: 객체 간의 결합도가 줄어들어 코드의 재사용성과 유지보수성이 높아집니다.
- 테스트 용이성: 의존성을 외부에서 주입받으므로, 테스트 시 다른 객체를 쉽게 대체할 수 있습니다.
- 관심사의 분리: 객체는 자신의 기능만 수행하며, 의존성 관리와 같은 부가적인 기능을 신경 쓰지 않아도 됩니다.
5. DI 활용 예제: 다양한 컴포넌트 주입
다음 예제는 스프링의 DI를 이용해 다양한 컴포넌트를 주입하고 활용하는 방법입니다.
@Component
public class EmailService {
public void sendEmail(String message) {
System.out.println("Sending email: " + message);
}
}
@Service
public class NotificationService {
private final EmailService emailService;
@Autowired
public NotificationService(EmailService emailService) {
this.emailService = emailService;
}
public void notifyUser(String message) {
emailService.sendEmail(message);
}
}
NotificationService는 EmailService에 의존적이지만, EmailService를 직접 생성하지 않고 Spring 컨테이너에서 제공된 빈을 주입받습니다.
Spring DI는 객체 간 결합도를 낮추고 유연한 애플리케이션 구성을 가능하게 해주는 강력한 도구입니다. 생성자 주입, 필드 주입, 세터 주입 방식을 적절히 활용해 객체 간 의존성을 관리함으로써, 더 유연하고 테스트하기 쉬운 코드를 작성할 수 있습니다.
'Spring' 카테고리의 다른 글
[중요!] @Transactional(AOP)가 동작하지 않는 이유 this.update(); (1) | 2024.10.31 |
---|---|
유연한 코드 설계: 의존성 주입(DI)과 그 장점 (0) | 2023.08.05 |
ArgumentResolver의 작동 원리: Spring MVC에서 파라미터 처리 흐름과 활용 방법 (0) | 2023.07.31 |
Spring Boot AutoConfiguration: 작동 원리와 성능 최적화 방법 (0) | 2023.07.30 |
스프링 MVC에서 커스텀 ArgumentResolver로 코드 중복 줄이기: @UserCache 구현 예제 (0) | 2023.07.13 |