Spring DI와 IoC

7810 단어 SpringSpring

스프링의 중요한 특징인 의존성 주입(DI)와 제어권 역전(IoC)에 대해 정리해보았다.

1. DI(Dependency Injection) 의존성 주입

하나의 객체가 다른 객체의 의존성을 제공하는 테크닉.
의도는 객체의 생성과 사용의 관심을 분리하는 것이다. 이는 가독성과 코드 재사용을 높혀준다.

  • 의존성이 뭐야...?
class MemberService {
	
    private final MemberRepository memberRepository = 
    					new MemoryMemberRepository();
}

위의 코드를 보면 MemberService 클래스 내부에서 MemberRepository 객체를 생성하고 있다. MemberServicef클래스가 MemberRepository클래스를 의존하고 있다고 말할 수 있다.

객체를 필요로하는 클래스 내에서 직접 객체를 생성하는 것은 클래스를 특정 객체에 커밋하는 것이고 이후에 클래스로부터 독립적으로 인스턴스의 생성을 변경하는 것이 불가능하기 때문에 유연하지 못하다.

즉, 의존한다는 것은 MemberRepository 클래스가 변하면 MemberService클래스에도 영향을 미친다는 의미이다.
MemberService 클래스에 MemberRepository 이외에 추가하고 싶거나 변경하고 싶다면 MemberService 클래스에 생성자를 추가로 만들거나 수정해야한다.
이는 유연하지 못하다.

class MemberService {
	
    private final MemberRepository memberRepository;
    
    public MemberService(MemberRepository memberRepository) {
    	
        this.memberRepository = memberRepository; //생성자주입
    
    }
}

Interface MemberRepository {
	
    Member save(Member member) {...}
}
  • 생성자 주입 : 필요한 의존성을 모두 포함하는 클래스의 생성자를 만들고 그 생성자를 통해 의존성을 주입한다.

이러한 문제를 해결하기 위해 의존관계를 Interface를 이용해 추상화시켜야한다.
의존관계를 Interface로 추상화하게 되면, 더 다양한 의존 관계를 맺을 수가 있고, 실제 구현 클래스와의 관계가 느슨해지고, 결합도가 낮아진다.

여기서 의존관계를 끊어주기 위해 내부에서 객체를 생성해서 사용하는게 아니라 외부에서 객체를 생성해 넣어준다.

이것을 DI라고 한다.

👍 DI의 장점

  • 재사용성이 높아진다.
  • 유닛테스트에 용이하다.
  • 왜 사용하는지 파악하기 수월하므로 가독성이 높아진다.
  • 의존 관계 설정이 컴파일시가 아닌 실행시에 이루어져 모듈들간의 결합도(coupling)를 낮출 수 있다.

여기서 MemberService에 MemberRepository 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체(Bean)를 생성해야한다.

Container에 Bean 을 등록하면 객체 생성 시점에 객체 의존 관계를 바탕으로 Container에서 해당 Bean을 찾아서 주입한다.

Bean이란?

Spring이 제공하는 Container(DI container == IoC Container)를 통해서 관리되는 인스턴스이다.

Spring은 Container에 Bean 등록할 때 기본으로 싱글톤으로 등록한다.
즉, Bean으로 지정된 클래스는 Container에 서 1개의 인스턴스로만 존재할 수 있다. 유일하게 하나만 등록해서 공유한다는 뜻으로 이해하면 된다.

🥔Bean 등록

Bean 등록 방법에는 2가지가 있다.

  1. 컴포넌트 스캔과 자동 의존관계 설정
  2. 자바 코드로 직접 스프링 빈 등록하기

1) 컴포넌트 스캔과 자동 의존관계 설정

@Component를 포함하는 다음 애노테이션이 있으면 Bean으로 자동 등록된다.
-@Controller
-@Service
-@Repository

생성자에 @Autowired가 있으면 spring이 연관된 객체를 container에서 찾아서 넣어준다.(DI)
생성자가 1개만 있으면 @Autowired 생략 가능하다.(비지니즈로직임을 쉽게 알 수 있도록 써주는 것이 더 좋을 수도 있겠다...)

@Service
class MemberService {
	
    private final MemberRepository memberRepository;
    
    @Autowired
    public MemberService(MemberRepository memberRepository) {
    	
        this.memberRepository = memberRepository; //생성자주입
    
    }
}

2) 직접 Bean 등록하기

@Configuration
public class SpringConfig {
	
   @Bean
   public MemberRepository memberRepository() {
   	return new MemberRepository();
   }
}

❓ 그럼 어떤 방식을 써야할까??

실무에서는 주로 1번 컴포넌트 스캔 방식을 사용한다.
정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면
2번 방식을 사용한다.

2. IoC(Inversion of Control) 제어권 역전

의존성은 어떤 객체가 사용해야 할 객체라고 할 수 있고, 이것을 직접 new 등을 써서 만들어 쓰면 의존성을 자기가 직접 만들어 쓴다고 할 수 있다. 일반적으로 의존성은 직접 만들어 쓰는 것을 의미한다.

class MemberService {

    private final MemberRepository memberRepository = 
    					new MemoryMemberRepository();
}

위의 코드처럼 직접적으로 의존성을 만들지 않고, 외부에서 의존성을 가져오는 경우를 제어권 역전이 일어났다고 말한다.
DI는 IoC의 일종이라고 생각하면 된다.


📚참고자료

https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85
김영한- 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

좋은 웹페이지 즐겨찾기