Java/Spring

[Spring] IoC/DI

태감새 2023. 5. 1. 19:38

IoC (Inversion of Control)

제어의 역전. 기존의 코드는 어플리케이션 코드에서 객체를 생성한다. 하지만 스프링 컨테이너는 객체의 생명주기를 컨테이너가 관리하고 주입시킨다. 어플리케이션 코드가 아닌 스프링 컨테이너가 객체를 관리하므로 제어의 권한이 역전되었다. 이를 IoC라고 한다.

 

IoC를 하는 이유?

객체의 생명주기 관리를 프레임워크에게 위임

  • 비즈니스 로직만 신경 쓸 수 있음

 

Bean 생명주기?

스프링 컨테이너 생성 -> Bean 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 종료

  • 초기화 콜백 : 빈 의존관계 주입 후 호출
    • @PostConstruct로 메서드 위에 지정 가능. 의존관계 주입 후 실행
  • 소멸전 콜백 : 빈이 소멸되기 직전 호출
    • @PreDestroy로 메서드 위에 지정 가능. 빈 소멸 직전에 실행

 

Bean을 등록하는 방법?

  • configuration bean
  • component scan

 

DI (Direct Injection)

객체를 직접 생성하는 것이 아니라 외부에서 생성한 후 주입 시켜주는 방식. 스프링 프레임워크에서는 스프링 컨테이너에 생성된 빈을 주입하는 방식을 사용한다.

 

왜 직접 생성하지 않고 DI를 사용하지?

의존관계를 약화시킬 수 있다.

의존관계

B가 변할 시 A에게도 영향을 끼치면 둘은 의존관계를 가진다고 할 수 있다.

의존관계 줄이기

  • 받는 타입을 인터페이스로 추상화하면 의존관계가 느슨해짐.
    • 다형성을 이용하여 더 다양한 의존관계를 맺을 수 있기 때문
  • DI를 사용하면 클래스 내부에 직접 객체를 생성할 필요가 없어진다.
    • 객체들간의 관계가 보다 유연해진다.
    • 다양한 객체를 넣을 수 있으므로 객체의 재사용성이 높아진다.

 

DI의 방법은 어떤 것이 있지?

  • 필드 주입
    • 불변성을 보장할 수 없다.
    • 순환 참조가 발생할 수 있다.
  • setter 주입
    • 불변성 보장 불가 (메서드를 이용해서 변경이 가능하기 때문)
  • 생성자 주입
    • 불변성 보장 가능 (final 키워드 사용 가능)
    • 순환 참조 컴파일 시 찾을 수 있음

왜 불변성을 보장해야 하는가?

의존 관계의 변경이 필요한 상황은 거의 없다. setter를 열어두면 의도치 않은 수정의 가능성이 열려있기 때문에 유지보수성이 떨어진다.

순환참조 방지 원리

class Member {
    private final User user;

    // 생성 시 User 객체를 필요로 함
    public Member(User user) {
        this.user = user;
    }
}
class User {
    private final Member member;

    // 생성 시 Member 객체를 필요로 함
    public User(Member member) {
        this.member = member;
    }
}

생성자 주입은 생성자를 사용하므로 생성과 동시에 주입이 일어난다. 그러므로 객체 생성 자체가 안되므로 구동 시점에 에러가 발생한다.

class Member {
    @Autowired
    private User user;
    // 생성에 아무런 영향 없음
}
class User {
    @Autowired
    private Member member;
    // 생성에 아무런 영향 없음
}

이와 달리 다른 방법(필드, setter)은 객체를 생성하고 주입을 하기때문에 메서드 호출이 일어나기 전에는 순환참조가 발생하지 않는다.

스프링 부트 2.6부터는 순환 참조가 기본적으로 허용되지 않도록 변경됨. 그래서 모든 경우에 순환 참조가 발생하는 경우 컴파일 에러 발생

 

정리

IoC/DI를 사용하는 이유

  • IoC : 객체의 생성을 어플리케이션 코드가 아닌 외부에서 관리하는 것.
    • 스프링에서는 스프링 컨테이너가 관리.
  • DI : 외부에서 생성된 객체를 주입하는 것을 의미

 

  1. 클래스간의 의존성이 줄어든다.
    • 주입받는 대상이 변하더라도 클래스의 변경이 없다.
  2. 객체 관리를 외부에서 해주므로 비즈니스 로직에 집중할 수 있다.

DI의 사용 방법

  • field 주입
  • setter 주입
  • 생성자 주입

생성자 방식을 권고하는 이유

의존관계 설정 이후 변경이 필요한 상황은 거의 없다. field와 setter 주입 방식은 변경의 여지가 있다. 하지만 생성자 방식은 필드에 final 키워드를 사용할 수 있으므로 불변성이 보장된다. 그렇기에 생성자 주입 방식을 권고하고 있다.

참고

https://steady-coding.tistory.com/600
https://mangkyu.tistory.com/125

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

동시성 제어  (0) 2023.05.04
[Spring] AOP  (0) 2023.05.02
SpringMVC (3) - SpringMVC  (0) 2023.04.27
JPA 간단정리  (0) 2023.04.25
[Spring] QueryString 객체로 받기  (0) 2023.04.23