| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- DB
- GoF 23
- 프로그래머스
- java
- 트러블슈팅
- 김영한
- Til
- 디자인 패턴
- 빌더 패턴
- 프록시 패턴
- 이펙티브 자바
- 로드밸런서
- 스프링
- 스케줄러
- 스프링 배치
- spring boot
- Spring
- 계산기
- redis
- 토스
- Spring Batch
- Effective Java
- 백엔드
- lv1
- 추상클래스
- 코드카타
- 성능 개선
- 배치
- 자바
- 템플릿 메서드 패턴
- Today
- Total
김코딩
의존성 주입(Dependency Injection)이란? 본문
스프링을 공부하다 보면 의존성 주입(Dependency Injection, DI) 이나 제어의 역전(Inversion of Control, IoC) 같은 용어를 자주 마주하게 됩니다. 처음 들었을 땐 생소하게 느껴질 수 있지만, 객체지향 프로그래밍과 스프링을 이해하는 데 있어 꼭 짚고 넘어가야 할 핵심 개념입니다.
오늘은 이 중에서 의존성 주입(DI) 에 대해 정리해보았습니다.
의존성(Dependency)이란?
의존성(Dependency) 이란, 어떤 객체가 자신의 기능을 수행하기 위해 다른 객체에 의존하는 관계를 의미합니다.
public class PaymentService {
public void pay() {
System.out.println("결제를 진행합니다.");
}
}
public class OrderService {
private PaymentService paymentService = new PaymentService(); // 직접 생성
public void order() {
System.out.println("주문을 시작합니다.");
paymentService.pay(); // PaymentService에 의존
}
}
public class Main {
public static void main(String[] args) {
OrderService orderService = new OrderService();
orderService.order();
}
}
이 코드에서 OrderService는 내부에서 PaymentService를 직접 생성하고 사용하고 있습니다.
즉, OrderService는 PaymentService의 구체적인 구현에 강하게 의존하고 있으며, 이를 “OrderService가 PaymentService를 의존하고 있다”고 표현합니다.
그런데 이게 왜 문제가 될까요?
이처럼 한 클래스가 다른 클래스의 구체적인 구현을 직접 알고 있을 경우 강한 결합(tight coupling) 이라고 부릅니다.
이 구조에는 다음과 같은 문제가 있습니다:
- 나중에 PaymentService의 pay() 메서드 이름이 변경되면 OrderService도 함께 수정해야 합니다.
- PaymentService를 KakaoPayService로 교체하고 싶을 때도 OrderService 내부 코드를 수정해야 합니다.
이러한 강한 결합은 확장성이나 유지보수가 어려워집니다.
바로 이러한 문제를 해결해주는 방법이 의존성 주입(Dependency Injection) 입니다.
의존성 주입(Dependency Injection)이란?
의존성 주입(Dependency Injection) 이란, 객체가 직접 필요한 의존 객체를 생성하는 것이 아니라 외부로부터 주입받는 방식을 말합니다.
이제 위 코드를 의존성 주입 방식으로 바꾸어 보겠습니다.
OrderService는 더 이상 직접 PaymentService를 생성하지 않고, 외부에서 주입받도록 개선합니다.
public interface Pay {
void pay();
}
public class PaymentService implements Pay {
@Override
public void pay() {
System.out.println("일반 결제를 진행합니다.");
}
}
public class KakaoPayService implements Pay {
@Override
public void pay() {
System.out.println("카카오페이로 결제합니다.");
}
}
public class OrderService {
private final Pay pay;
public OrderService(Pay pay) {
this.pay = pay;
}
public void order() {
System.out.println("주문을 진행합니다.");
pay.pay();
}
}
public class Main {
public static void main(String[] args) {
Pay kakaoPay = new KakaoPayService();
Pay payment = new PaymentService();
OrderService orderService = new OrderService(kakaoPay);
OrderService orderService1 = new OrderService(payment);
orderService.order();
orderService1.order();
}
}
위 예제에서는 Pay라는 인터페이스를 만들고,PaymentService와 KakaoPayService가 이 인터페이스를 구현하게 했습니다.
이제 OrderService는 Pay 타입에만 의존하고 있기 때문에, KakaoPayService, PaymentService 등 어떤 구현체가 들어오든 전혀 알 필요가 없습니다.
- 코드 수정 없이 다양한 결제 방식으로 교체할 수 있고,
- 클래스 간 결합도는 낮아지고, 확장성과 유지보수성은 높아졌습니다.
'스프링' 카테고리의 다른 글
| 반복되는 로그인 체크, 필터 하나로 끝내기(Filter 사용법) (0) | 2025.05.22 |
|---|---|
| 여긴 못지나간다.(@Valid) (0) | 2025.05.19 |
| Spring Container와 Bean이 헷갈리는 이들에게 (0) | 2025.05.15 |
| 프로퍼티 바인딩이 뭔데 자꾸 헷갈려? (0) | 2025.05.01 |
| 스프링이란? (0) | 2024.03.28 |