Spring Framework/Spring

[Spring] 2. Dependency Injection

hyomee2 2024. 9. 4. 21:37

Dependency Injection(의존성 주입, DI)이란?

: 객체 간의 의존 관계를 빈 설정 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것

- 이를 통해 객체 간의 결합도를 낮출 수 있으며, 유지보수성과 유연성이 증가한다.

 

1. 의존 관계와 결합도

- 클래스 A가 클래스 B를 필드로 가질 때 A는 B에 의존한다.

public class A {
	private B b = new B(); 
} 

public class B { }

 

- 아래와 같이 클래스명이 B에서 NewB로 바뀌게 되면 컴파일 에러가 발생한다.

public class A {
	private B b = new B(); 
} 

public class NewB { }

 

- 의존성이 강하다

= 한 객체가 변경되면 이에 의존하는 다른 객체들도 함께 변경돼야 한다.

= 결합도가 높다

-> 유지보수성과 유연성이 저하될 수 있다.

ex) B가 NewB로 변경되면 해당 클래스를 필드로 가지고 있는 A도 변경되어야 한다. 

 

- 의존 관계를 느슨하게 하고 결합도를 낮추기 위해 아래와 같이 코드를 수정할 수 있다.

NewB라는 구체적인 구현체의 타입을 사용하는 대신 B라는 상위 인터페이스 타입으로 필드를 선언했고,

직접 객체를 생성하지 않고 생성자를 통해 전달 받는다.

이렇게 되면 실제로 사용하는 구현체가 NewB에서 다른 타입으로 변경돼도 A의 코드는 변경될 필요가 없다.

public class A {
	/* 상위 타입을 필드로 설정 */ 
	private B b; 
    
	/* 직접 객체를 생성하지 않고 생성자를 통해 전달 받음 */ 
	public A(B b) { 
		this.b = b; 
	} 
} 

/* 상위 타입으로 사용할 인터페이스 */ 
public interface B { 

} 

/* 인터페이스의 구현 클래스 */ 
public class NewB implements B { 

}

DI 방법 알아보기

1. 예제 디렉토리 구조 및 공통 클래스

* 아래는 예제에 공통적으로 사용할 클래스이다.

public interface Account {
    /* 잔액 조회 */
    String getBalance();
    /* 입금 */
    String deposit(int money);
    /* 출금 */
    String withDraw(int money);
}
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class MemberDTO {
    private int sequence; //회원번호
    private String name; //이름
    private String phone; //휴대폰번호
    private String email; //이메일
    private Account personalAccount; //개인계좌
}
@RequiredArgsConstructor
public class PersonalAccount implements Account {

    private final int bankCode; //은행코드
    private final String accNo; //계좌번호
    private int balance; //잔액
    @Override
    public String getBalance() {
        return this.accNo + " 계좌의 현재 잔액은 " + this.balance + "원 입니다.";
    }
    @Override
    public String deposit(int money) {
        String str = "";
        if(money >= 0) {
            this.balance += money;
            str = money + "원이 입금되었습니다.";
        } else {
            str = "금액을 잘못 입력하셨습니다.";
        }
        return str;
    }
    @Override
    public String withDraw(int money) {
        String str = "";
        if(this.balance >= money) {
            this.balance -= money;
            str = money + "원이 출금되었습니다.";
        } else {
            str = "잔액이 부족합니다. 잔액을 확인해주세요.";
        }
        return str;
    }
}

 

2. XML Configuration

(1) 생성자(Constructor) 주입

- MemberDTO는 Account 타입을 의존하고 있기 때문에

member 빈 등록 시 account 빈을 참조하도록 <constructor-arg> 태그의 ref 속성을 작성한다.

생성자를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 '생성자 주입' 이다.

- Spring에서 공식적으로 추천하는 의존성 주입 방법이다.

<!--main\resources\section01\xmlconfig\spring-context.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   
    <!--bean 태그의 클래스 속성은 인터페이스 타입이 아닌 구현 클래스 타입으로 작성해야 한다.
    따라서 Account 인터페이스가 아닌 PersonalAccount 클래스를 사용한다.-->
    <bean id="account" class="com.ohgiraffers.common.PersonalAccount">
        <constructor-arg index="0" value="20"/>
        <constructor-arg index="1" value="110-234-567890"/>
    </bean>
    
    <!-- 1. 생성자 주입 -->
    <bean id="member" class="org.example.common.MemberDTO">  
        <constructor-arg name="sequence" value="1"/>
        <constructor-arg name="name" value="홍길동"/>
        <constructor-arg name="phone" value="010-1234-5678"/>
        <constructor-arg name="email" value="hong@gmail.com"/>
        <constructor-arg name="personalAccount" ref="account"/>
    </bean>

</beans>
public class Application {
    public static void main(String[] args) {
    	/*XML 설정 파일을 기반으로 ApplicationContext 객체 생성*/
        ApplicationContext applicationContext
                = new GenericXmlApplicationContext("section01/xmlconfig/spring-context.xml");

	/*MemberDTO 타입의 빈 가져오기*/
        MemberDTO member = applicationContext.getBean("member", MemberDTO.class);
        System.out.println(member.getPersonalAccount().deposit(10000));
        System.out.println(member.getPersonalAccount().getBalance());
        System.out.println(member.getPersonalAccount().withDraw(5000));
        System.out.println(member.getPersonalAccount().getBalance());
    }
}

 

(2) Setter 주입

- <property> 태그는 setter 메소드를 통해 빈 객체의 값을 초기화하는 설정이다.

 name: 필드명

 value: 필드에 담을 값

 ref: 참조할 빈의 id

- MemberDTO는 Account 타입을 의존하고 있기 때문에

member 빈 등록 시 account 빈을 참조하도록 <property> 태그의 ref 속성을 작성한다.

Setter 메소드를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 '생성자 주입' 이다.

<!--main\resources\section01\xmlconfig\spring-context.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="account" class="com.ohgiraffers.common.PersonalAccount">
        <constructor-arg index="0" value="20"/>
        <constructor-arg index="1" value="110-234-567890"/>
    </bean>

    <!-- 생성 된 다른 bean을 의존성 주입할 경우 value 속성이 아닌 ref 속성을 사용하여 bean id를 전달 -->
    <!-- 2. setter 주입 -->
    <bean id="member" class="com.ohgiraffers.common.MemberDTO">
        <property name="sequence" value="1"/>
        <property name="name" value="홍길동"/>
        <property name="phone" value="010-1234-5678"/>
        <property name="email" value="hong@gmail.com"/>
        <property name="personalAccount" ref="account"/>
    </bean>

</beans>

 

3. Java Configuration

(1) 생성자(Constructor) 주입

- MemberDTO는 Account 타입을 의존하고 있기 때문에

memberGenerator 빈 등록 시 accountGenerator 빈을 참조하도록

MemberDTO 생성자의 인자로 accountGenerator 메소드 호출의 결과(PersonalAccount bean 객체)를 전달한다.

생성자를 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 '생성자 주입' 이라고 한다.

@Configuration
public class ContextConfiguration {

    @Bean
    public Account accountGenerator(){
        return new PersonalAccount(20, "110-234-567890");
    }

    /* bean 등록에 사용 된 메소드를 호출하여 의존성 주입을 처리할 수 있다. */
    @Bean
    public MemberDTO memberGenerator() {
        /* 1. 생성자 주입 
        MemberDTO 생성자를 통해 Account를 생성하는 메소드를 호출한 리턴 값을 전달하여 bean을 조립할 수 있다.*/
        return new MemberDTO(1, "홍길동", "010-1234-5678", "hong@gmail.com", accountGenerator());
    }
}
public class Application {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(ContextConfiguration.class);

        MemberDTO member = applicationContext.getBean("memberGenerator", MemberDTO.class);
        System.out.println(member.getPersonalAccount().deposit(10000));
        System.out.println(member.getPersonalAccount().getBalance());
        System.out.println(member.getPersonalAccount().withDraw(5000));
        System.out.println(member.getPersonalAccount().getBalance());
    }
}

 

(2) Setter 주입

- Application은 위의 '생성자 주입' 에서 이용한 코드와 같다.

- MemberDTO는 Account 타입을 의존하고 있기 때문에

memberGenerator 빈 등록 시 accountGenerator 빈을 참조하도록

setPersonalAccount 메소드의 인자로 accountGenerator 메소드의 호출 결과(PersonalAccount bean 객체)를 전달한다.

Setter을 통해 의존성 객체를 전달하여 의존성을 주입하고 있으므로 이를 'setter 주입'이라고 한다.

@Configuration
public class ContextConfiguration {

    @Bean
    public Account accountGenerator(){
        return new PersonalAccount(20, "110-234-567890");
    }

    @Bean
    public MemberDTO memberGenerator() {

        /* 2. setter 주입 */
        MemberDTO member = new MemberDTO();
        member.setSequence(1);
        member.setName("홍길동");
        member.setPhone("010-1234-5678");
        member.setEmail("hong@gmail.com");
        /* Setter을 통해 Account를 생성하는 메소드를 호출한 리턴 값을 전달하여 bean을 조립할 수 있다.*/
        member.setPersonalAccount(accountGenerator());
        return member;
    }
}

정리

- DI는 객체 간의 의존 관계를 빈 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해주는 것이다.

 이를 통해 객체 간의 결합도를 낮출 수 있으며, 이로 인해 유지보수성과 유연성이 증가한다.

- XML 빈 설정 시에는 <constructor-args> 또는 <property> 태그의 ref 속성에 의존성 주입할  bean의 이름을 설정한다.

- Java 빈 설정 시에는 생성자, setter 메소드의 인자 값으로 의존성 주입할 bean의 메소드 호출 반환 값을 전달한다.

'Spring Framework > Spring' 카테고리의 다른 글

[Spring] 5. AOP  (3) 2024.09.07
[Spring] 4. Bean  (3) 2024.09.05
[Spring] 3. DI Annotation  (2) 2024.09.05
[Spring] 1. IoC Container  (2) 2024.09.04