당니의 개발자 스토리
DI는 왜 필요할까? 객체 생성 책임과 Spring Container 본문
Java로 프로그램을 만들다 보면 자연스럽게 객체를 생성하게 된다.
예를 들어 이런 코드가 있다고 해보자.
public class WasherUser {
private SWasher washer = new SWasher();
public void wash(){
washer.wash();
}
}
겉으로 보면 문제 없어 보인다.
하지만 이 코드는 심각한 문제를 가지고 있다...!!
바로 강한 결합이다.
이 코드는 WasherUser가 SWasher에 강하게 묶여 있는 구조다.
왜냐하면 코드 안에 이렇게 박혀 있기 때문이다.
new SWasher()
즉 WasherUser는 이렇게 말하는 것과 같다.
나는 무조건 SWasher만 쓸 거야
문제는 나중에 세탁기를 바꾸고 싶을 때 발생한다.
class TurboWasher {}
같은 새로운 세탁기가 생겼다고 해보자.
그럼 코드를 이렇게 바꿔야 한다.
private TurboWasher washer = new TurboWasher();
즉 사용하는 쪽 코드까지 수정해야 한다.
이게 바로 강한 결합이다.
객체에 의존하는 코드가 객체를 직접 생성하고 있는
구조가 유지보수를 어렵게 만든다.
그래서 등장한 개념이 DI, 의존성 주입이다.
DI는 말 그대로 '필요한 객체를 외부에서 넣어준다'는 의미다.
코드를 이렇게 바꿔보자.
public class WasherUser {
private Washer washer;
public WasherUser(Washer washer) {
this.washer = washer;
}
public void wash(){
washer.wash();
}
}
여기서 중요한 변화는 new가 사라졌다는 점이다.
이제 WasherUser는 이렇게 말한다.
나는 washer가 필요해
누가 만들어줄지는 몰라
그리고 외부에서 객체를 넣어준다.
Washer washer = new SWasher();
WasherUser user = new WasherUser(washer);
이 구조가 DI다.
이렇게 바꾸면 어떤 일이 생길까?
세탁기를 바꾸고 싶다면 사용하는 코드를 건드릴 필요가 없다.
Washer washer = new TurboWasher();
WasherUser user = new WasherUser(washer);
이렇게만 바꾸면 된다.
객체 생성 책임과 객체 사용 책임을 분리한다
즉, 여기까지는 순수 Java DI다.
이제 Spring이 등장한다.
Spring은 이 DI를 자동으로 해주는 역할을 한다.
개발자가 직접 객체를 생성하지 않아도, Spring이 대신 객체를 만들고 넣어준다.
@Component
public class SWasher implements Washer {}
@Component
public class WasherUser {
private final Washer washer;
public WasherUser(Washer washer) {
this.washer = washer;
}
}
이 상태에서 Spring이 하는 일은 다음과 같다.
1. SWasher 발견
2. WasherUser 발견
3. SWasher 객체 생성
4. WasherUser 생성 시 washer 자동 주입
개발자는 new를 한 번도 쓰지 않았다.
여기서 중요한 개념이 하나 더 나온다.
IoC (Inversion of Control), 제어의 역전이다.
기존 방식은 이렇다.
개발자가 객체 생성
개발자가 객체 연결
개발자가 전체 흐름 제어
Spring 방식은 이렇다.
Spring이 객체 생성
Spring이 객체 연결
Spring이 전체 흐름 제어
즉 제어권이 개발자에서 Spring으로 넘어간다.
이걸 제어의 역전이라고 한다.
이 구조를 관리하는 공간이 바로 Spring Container다.
Container는 쉽게 말하면 객체 창고다.
Bean을 저장
Bean을 생성
Bean을 관리
Bean을 주입
이 역할을 한다.
여기서 Bean이라는 개념이 등장한다.
Bean은 단순하다.
Spring이 관리하는 객체
이다.
SWasher
WasherUser
MemberService
이런 객체들이 전부 Bean이 된다.
그리고 Container에서 이렇게 꺼낼 수도 있다.
ApplicationContext ctx = ...;
WasherUser user = ctx.getBean(WasherUser.class);
이건 이렇게 해석하면 된다.
Spring아 WasherUser 하나 줘
Spring이 이미 만들어둔 객체를 꺼내 쓰는 것이다.
여기까지 정리하면 DI의 핵심은 다음과 같다.
객체를 직접 만들지 않는다
필요한 객체를 외부에서 받는다
생성과 관리는 Spring이 담당한다
그리고 이 구조 덕분에 얻는 장점은 명확하다.
첫 번째, 결합도가 낮아진다.
구현체를 바꿔도 사용하는 코드를 수정할 필요가 없다.
두 번째, 테스트가 쉬워진다.
예를 들어 테스트에서는 가짜 객체를 넣을 수 있다.
Washer fake = new FakeWasher();
WasherUser user = new WasherUser(fake);
Spring 없이도 테스트가 가능하다.
세 번째, 객체 생명주기를 통제할 수 있다.
Spring이 Bean을 생성하고 관리하기 때문에,
객체 생성 시점과 종료 시점을 제어할 수 있다.
마지막으로 한 줄 정리하면 이렇다.
DI는 객체 생성 책임을 외부로 넘겨 결합도를 낮추는 설계
'Java, Spring' 카테고리의 다른 글
| @Autowired와 @Qualifier (0) | 2026.05.06 |
|---|---|
| @Component vs @Bean (0) | 2026.05.06 |
| Maven, 빌드 자동화 도구에 대해서 (0) | 2026.05.06 |
| JUnit과 Assertion으로 테스트 코드 작성하기 (0) | 2026.05.06 |
| Spring Bean과 Spring Container, 객체를 Spring이 관리하는 이유 (0) | 2026.05.06 |