DI는 Dependency Injection의 약자로 한국어로는 의존성 주입이라고 한다
📌의존성이란
A 코드 없이 B코드가 동작할수 없는 상태를 말한다 즉 B 코드가 A코드에게 의존하고 있는 상황이다
예시) 리모컨(B)은 건전지(A)가 없으면 동작하지 않는다. 즉, 리모컨은 건전지에 의존한다.
여기서 결합도랑 뭐가 다른지 생각해볼수 있는데 차이점은
코드의 변형이 생겼을때 다른 코드도 수정해야하는 것이 결합도의 개념이고
이 코드가 없으면 아예 작동이 안된다면 의존성이다
📌의존성을 주입하는 방법 두가지
- 생성자 인젝션
- setter인젝션
크게 이 두가지 방법이 존재한다 성능이나 동작방식에서 차이가 있지만 크게 중요한것은 아니다
🛠️의존성 주입 적용을 위한 문제 원인 만들기
public class Client {
public static void main(String[] args) {
//스프링 컨네이너 시작
AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
Phone phone = (Phone)factory.getBean("apple");//룩업= 객체요청
phone.turnOn();
phone.volumeUp();
phone.volumeDown();
phone.turnOff();
//스프링 컨테이너 종료
factory.close();
}
}
지난수업에 사용했던 예제를 가져온다
여기서 나는 볼륨업다운을 할때 아이폰이 아닌 애플워치로 볼륨을 조절하고싶다
즉 아이폰은 실제로 기능을 수행하지 않고 애플워치에게 일을 맡기는 역할이다
아이폰은 컨트롤러 애플워치는 DAO역할을 한다고 볼수있다
아이폰이 애플워치에게 일을 넘겼기때문에 실행을 위해 메서드 안에서 뉴 키워드를 이용해 객체를 생성했다
@Override
public void volumeUp() {
watch = new AppleWatch();//애플워치 객체 생성
watch.volumeUp();
}
@Override
public void volumeDown() {
watch = new AppleWatch();
watch.volumeDown();
}
- 볼륨업 두번 → 애플워치 객체 두번생성
- 볼륨다운 두번 → 애플워치 객체 두번생성
즉 같은 역할을 하는 객체를 계속 생성하고 버리는 방식이기때문에 불필요한 객체 생성을 만들고 비효율적이다
즉 문제 상황의 핵심은 내가 수행하려는 기능을 다른애가 해줬으면 좋겟어 인것이다
그렇다고 해서 코드가 돌아가지 않는건 아니지지만 좋은 구조는 아니다
🛠️의존성 주입하기
위의 코드의 문제상황
public class IPhone implements Phone {
private AppleWatch watch; // 아이폰의 멤버변수
}
멤버변수가 생겼는데 생성자로 초기화 해주지 않아서 생긴 문제다 즉 아이폰을 만들때 마다 애플워치가 나오면 안된다
따라서 초기화 해주기 위해 생성자를 사용하여 인잇 메서드 행동을 해야한다
🔎생성자 인젝션
Constructor injection 이라고도한다 이 Constructor가 생성자 라는 의미이다
public IPhone() {
this.watch = new AppleWatch();
System.out.println("아이폰 객체 생성");
}
위와 같은 형태로 기본 생성자를 사용하게 되면 아이폰살거야? 워치도 사의 늬앙스를 가지고 있기때문에 클린하지 않은 코드가 발생된다
해당생성자의 인자로 watch를 주게 되면 여기서 부터 의존성이 발생하는 것이다
public IPhone(Watch watch) { // IPhone 생성 시 외부에서 Watch 객체를 주입받는다
this.watch = watch; // 멤버변수 초기화
System.out.println("아이폰 객체 생성");
}
이렇게 변경해주면 워치가 없으면 아이폰을 쓸 수 없는 구조가 된다
생성자를 여려개 만들때 인자에 타입을 주면 반드시 꼭 필요한것이기 때문에 만든것으로 이것이 의존성이라고 할수 있다
주의 할점은 생성자 오버로딩 자체가 의존성을 의미하는 것은 아니다.
✔️애플워치에 생성자를 통해 외부에서 의존성을 주입하기
아이폰 객체의 생성은 스프링 컨테이너가 한다 따라서 애플워치도 스프링 컨테이너가 가지고 있어야한다
xml에서 빈클래스를 만든다 이게 new AppleWatch()의 역할을 한다
<bean class="test06.IPhone" id="apple">
<constructor-arg ref="aw" /> <!--빈클래스 아이디와 이름을 맞춰준다-->
</bean>
<bean class="test06.AppleWatch" id="aw" />
타입은 이미 알고 있기 때문에 안정해줘도 되지만 자동형변환 되는것은 아니라 아이디에 사용한 이름과 <constructor-arg ref= 이름/>이 같아야 한다
또한 오버로딩때문에 같은 생성자는 존재할수 없기때문에 순서대로 진행된다
❗xml설정파일이 망가지면 에러찾기가 어렵다❗
생성순서가 꼬일수 있고 컨테이너를 여러개 사용하다보면 각 컨테이너에 맞춰야하기때문에
기본생성자를 하나 만들고 로그를 만들어서 확인하는 습관을 들이면 좋다
🔎setter 인젝션
아이폰에는 생성자 인젝션을 사용해 보았으니 갤럭시에는 세터인젝션을 사용해보자
public void setWatch(Watch watch) { //세터가 실행될때 워치를 외부에서 받아온다
this.watch = watch;
System.out.println("갤럭시 세터 호출");
}
생성자 인젝션과 같은 방식으로 xml설정 파일에 추가해준다
<bean class="test06.GalaxyPhone" id="galaxy">
<property name="watch" ref="aw" />
</bean>
빈클래스에 세터를 호출하기 위해 프로퍼티스를 사용하고 멤버변수 이름을 넣는다 그리고 레퍼런스에 위에 만들어둔 aw를 넣고 실행하면 문제없이 실행된다
생성자 인젝션 vs 세터인젝션의 차이점
생성자인젝션은 애플워치가 없으면 아이폰을 만들 수 없다 즉 의존성을 띄는 주체가 반드시 있어야한다
생성자 주입은 강제성이 있어서 먼저 생성된다
세터인젝션은 스마트워치가 없어도 갤럭시를 만들 수있다 즉 없어도 갤럭시 객체를 생성하는데 문제가 없다
세터 호출 자체를 갤럭시가 한다 따라서 갤럭시 객체가 생성된 다음 세터가 호출된다
🔎@ Annotation
어노테이션 사용시 xml에 컨텍스트 네임 스페이스를 추가해야한다
어노테이션설정이 아니더라도 뭔가설정을 추가할때는 xml에 스키마를 추가해야한다
네임스페이스는 쉬운말로 별명 덩어리라고 생각하면 좋다
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
네임스페이스와 가져와야할 링크들을 가져와서 xml에 설정해주었다
이제 아이폰을 가져오고 싶을때 @component()로 아이폰객체를 새로 만들어줄수있다
@Component("apple")
public class IPhone implements Phone {
}
여기서 컴포넌트는 new를 대신하는 bean을 @컴포넌트가 대신한다고 볼수있다
@Component("apple")
// <bean class="test06.IPhone" id="apple"></bean>
// IPhone apple = new IPhone();
지금 폴더 구조를 보면 6개의 패키지가 하나의 컨테이너 안에서 돌고있는데 @은 패키지 6에만 작성되어있다
따라서 컨테이너가 모든 패키지를 확인하는것은 비효율적이므로 xml에
<context:component-scan base-package="test06" />
를 추가한다 이 컴포넌트 스캔을 읽기 위해
xmlns:context="http://www.springframework.org/schema/context"
이 컨텍스트가 필요한것이다
어노테이션 설정이 끝났으며 이제 의존성을 주입해야한다 의존시켜야할 대상에
@Autowired
private Watch watch;
@Autowired 를 추가해주면 의존성 주입이 완료된다 자료형 판단 능력이 있기때문에 이름을 지정해주지 않아도 된다
주의할점은 의존성 주입해줘의 역할이지 객체를 생성하지는 않는다 따라서 객체가 없으면 주입할수 없고
@Autowired만 보고 어떤 워치인지 알수 없다
@Component("aw")
public class AppleWatch implements Watch{
}
따라서 애플워치도 @컴포넌트를 해줘야한다 이렇게 설정하고 클라이언트에서 실행시켰을 때 에러는 안나는데
이것은 지금 애플워치만 @이 작성 되있기때문에 문제가 없다
워치는 인터페이스인데 인터페이스는 자료형과 동일클래스도 마찬가지다
따라서 애플워치에 부모클래스가 인터페이스라고 생각하면 편하다
@Component("sw")
public class SmartWatch implements Watch {
}
스마트워치에도 컴포넌트해주면 에러가 발생하는데 이게 오토와이어드의 대표적인 모호성이슈다
아까는 한개밖에 없어서 어떤 워치인지 구분할 필요가 없었지만 이제 두개가 되어서 구분을 해줘야 에러가 해결된다
해결방법으로는 두가지가 있는데
1. xml에서 주석으로 처리하는 방법
<!--<bean class="test06.AppleWatch" id="aw" />-->
<bean class="test06.SmartWatch" id="sw" />
이렇게 필요없는 빈을 주석으로 가려놓고 필요할때 주석을 지우고 사용한다
장점은 xml은 컴파일 대상이 아니라서 비용이 안든다
2.@컴포넌트에 이름 붙이기
@Qualifier를 사용해 사용할 워치의 이름을 지정해준다
재컴파일 해야되서 조금 무거운 단점이 있다
@Component("apple")
public class IPhone implements Phone {
@Autowired
@Qualifier("aw")
private Watch watch;
}
'🐢 꼬부기 LV.1 | 개념•기초 > 💧물대포(핵심개념)' 카테고리의 다른 글
| 인텔리제이 특징과 프로젝트 생성하기 (0) | 2026.01.15 |
|---|---|
| 인텔리제이 설치하기 (0) | 2026.01.14 |
| Spring 프레임워크 기초 사용과 에러 발생 해결 (0) | 2026.01.13 |
| Spring 프레임워크 개념 알아보기 (0) | 2026.01.13 |
| MySQL설치하기 (0) | 2026.01.13 |