[Spring] 의존성 주입(DI) 란

leehyunjon·2022년 6월 8일
0

Spring

목록 보기
2/3

의존성 주입(DI)

DI란 의존 관계 주입(Dependency Injection)의 줄임말로써, 어떤 객체가 사용하는 의존객체를 내부에서 생성하는 것이 아닌 외부에서 주입해서 사용하는 것을 말한다.

@Service
public class UserService{
	private UserRepository userRepository;
    
    public UserService(){
    	this.userRepository = new UserRepository();
    }
}

public class UserRepository{
	//...
}

위의 예시에서는 UserService클래스를 사용하기 위해선 UserRepository가 필요하다. 이때 UserService는 UserRepository의 의존성을 가진다라고 한다. UserService가 UserRepository의 의존성을 가지기 위해 UserService의 생성자에서 UserRepository클래스를 직접 생성하여 의존 관계를 맺는다.
즉, 객체 내부에서 의존객체를 생성한다.

하지만 이렇게 내부에서 의존객체를 생성하게 되면 코드의 재활용성이 떨어지게 되고 모듈간의 결합도가 높아지고 유연성이 떨어지게 된다.

@Service
public class UserService{
	@Autowired
	private final UserRepository userRepository;
    
    //...
}

위의 코드와 같이 구현하게 되면 UserService에서 의존 객체를 생성하지 않고 userService에 UserRepository클래스가 주입되어있다.
즉, 외부에서 등록되어있는 UserRepository객체를 UserService가 생성되었을때 자동으로 UserRepository를 의존되게 한다.

의존성 주입을 사용하는 이유

  • 코드를 단순화 시켜준다.
  • 모듈간의 결합도는 낮추고 유연성을 향상시킬수 있다.
  • 재사용성을 높여준다.
  • 테스트에 용이하다.

이렇게 외부에서 객체를 주입할 수 있게 하기 위해서는 조건이 있다.
바로 주입 시키려는 객체가 빈(Bean)이라는 객체로 등록이 되어있어야 하는것이다.
이 빈 객체는 스프링 IoC 컨테이너라는 곳에서 관리되는 객체인데 컨테이너에 저장되어 있는 빈 객체만이 의존성 주입될 수 있다.
즉, 외부에서 객체를 주입할 수 있게 하는 것은 스프링 IoC 컨테이너의 역할이다.


IoC

IoC란 Inversion Of Control의 약자로써 '제어 반전'이라는 뜻이다.

객체의 생성부터 파괴까지의 생명주기 관리와 의존성 관리를 개발자가 아닌 컨테이너에게 넘김으로써 모든 객체에 대한 제어권이 바뀌었다는 것을 말한다.

    		출저 : https://devlog-wjdrbs96.tistory.com/165?category=882236

IoC 컨테이너는 외부에서 생성된 객체들을 등록하고 다른 객체에서 등록된 객체와 의존 관계를 필요로 할때 컨테이너에 등록된 객체를 외부에서 주입해주는 역할을 수행한다.

IoC에서 관리하는 객체(Bean)을 외부에서 주입하므로써 모듈간의 결합도는 낮아지고 런타임시 의존관계가 결정되기 때문에 유연성이 높아진다.

IoC 종류

BeanFactory

  • Bean객체를 생성하고 관리하는 최상위 인터페이스로써 단순히 컨테이너에서 빈을 생성하고 DI를 처리하는 기능만 한다.
  • 팩토리 디자인 패턴을 구현한것.
  • 빈을 조회할수 있는 getBean() 메소드가 정의되어있다.
  • 보통 BeanFactory를 직접적으로 사용하지 않고 ApplicationContext를 사용한다.

ApplicationContext

  • Bean객체를 생성하고 관리하는 것은 BeanFactory와 동일하지만 스프링의 부가 기능을 제공한다.
  • BeanFactory를 상속받은 인터페이스이며 구동되는 시점에 Bean객체들을 스캔하여 객체화 한다.

Bean

Bean이란 스프링 IoC 컨테이너에서 관리하는 객체이다.

Bean 특징

  • IoC컨테이너가 관리하여 의존성 관리가 편리하다.
  • 싱글톤 형태

Bean 등록방법

Component-Sacanning

Component-Sacnning 방식은 어느지점에 설정되어있는 컴포넌트를 찾아 Bean으로 등록하는 방식이다.
@ComponentScanning과 @Component 어노테이션으로 등록할수 있다.

  • @ComponentScanning : 특정 지점에 있는 @Component를 탐색해서 모든 인스턴스를 생성해 Bean으로 등록하게 하는 역할
  • @Component : 실제 빈으로 등록될 클래스

@SpringBootApplication 어노테이션을 확인해보면 이렇게 @CompoentScan이 설정되어있는데, @ComponentScan이 설정된 메인 함수에서부터 @Component가 설정되어있는 모든 클래스를 찾아서 Bean으로 등록시키게 된다.

@Service
public class UserService{

	//...
	public User findUser(){
    	//...
    }
}
@RequiredArgsConstructor
@RestController
public class UserController{

	private final UserService;
    
    public User findUser(...){
    	return userService.findUser();
    }
}

예를 들어 이렇게 UserService클래스에 @Service어노테이션을 이용하여 findUser메소드를 구현하면 애플리케이션 구동시 @SpringBootApplication에 설정되어있는 @ComponentScanning을 통해 @Component가 설정된 UserService를 찾아 IoC에 빈으로 등록하고 UserController에서 IoC에 등록된 UserService를 DI하고 findUser를 사용할수 있게 되는 것이다.

여기서 @Component가 아닌 @Service를 사용했는데 어떻게 되는거냐고 하면

@Service어노테이션에는 @Component가 등록되어있다.
마찬가지로 @Controller, @Service, @Repository에도 내부적으로 @Component가 등록되어 있다.

Configuration

@Configuration 어노테이션을 Bean으로 설정할 클래스에 선언후 특정 타입으로 리턴하는 메서드에 @Bean을 붙여주게 되면 자동으로 Bean 등록이 된다. (기본적으로 메서드 이름으로 빈 등록이 된다.)

@Configuration
public class Food {

	@Bean
	public Apple getApple() {
		return new Apple();
	}
}
public class Apple{
	public void eat(){
		System.out.println("사과 냠냠");
	}
}
@Service
public class TestService {
	@Autowired
	private Food food;
    
	public void configTest(){
		Apple apple = food.getApple();
		apple.eat();
	}
}

TestService에서 Apple클래스를 불러와 eat이라는 메소드를 사용하려고한다면 TestService에서 Apple클래스를 new로 생성해서 의존관계를 맺은 후 사용할수 있다.

하지만 IoC에 등록되어있는 Food클래스의 getApple을 통해 Apple을 불러와 사용할 수 있다.

예시를 위해 억지로 끼워넣긴 했지만 @Configuration으로 설정된 Food클래스의 getApple메서드를 통해 Apple을 빈으로 등록해 사용했다고 볼 수 있다.


이렇게 @Configuration를 통해 의존성 주입이 가능한 이유는 내부적으로 @Component가 설정되어있어 @ComponentScan의 탐색 대상이 되기 때문이다.

@Component VS @Bean

공부하다가 문득 둘다 ComponentScanning으로 탐색해서 빈으로 등록하는 거라면 이 둘의 차이가 뭘까 라고 생각이 들었다.

직접적인 차이는
@Component는 클래스 레벨에서 선언함으로써 스프링 런타임시 ComponentScanning을 통해 자동으로 빈을 등록하는 어노테이션이다.
@Bean은 메소드 레벨에서 선언함으로써 개발자가 객체를 수동으로 빈 등록을 하는 어노테이션이다.

사용적인 차이는
@Component는 개발자가 직접 컨트롤 가능한 클래스인 경우 사용한다.
@Bean은 개발자가 컨트롤 불가능한 외부 라이브러리를 빈으로 등록할때 사용한다.

위에 예시로 들었던 Apple클래스가 외부 라이브러리로 생각한다면 Food클래스로 컨트롤 불가능한 Apple클래스를 가져왔다고 보면 될듯하다.

Bean Scope

빈 스코프란 빈의 사용 범위를 말하는 것으로 singleton, prototype, request 등이 있다.

  • singleton : 빈 스코프의 기본값으로 앱 구동 동안 단 한번만 생성된다.
  • prototype : IoC컨테이너에 빈을 요청할때마다 새로운 인스턴스를 생성해준다.
  • request : http요청마다 빈이 생성된다.

의존성 주입을 알기 위해서 IoC와 Bean을 알아봐야 했다.

그럼 마지막으로 의존성 주입 방법에 대해서 알아보자.

의존성 주입 방법

1. 생성자 주입

말 그대로 생성자를 통해서 의존 관계를 맺는것이다.

스프링에서 권장하는 방법이다.

그 이유는 필수적으로 사용해야하는 의존 객체 없이는 인스턴스를 사용할수 없도록 강제성을 두어 NullPointException과 같은 이슈를 예방할수 있기 때문이다.

@Service
public class UserService{
	private UserRepository userRepository
    
    public UserService(UserRepository userRepository){
    	this.userRepository = userRepository;
    }
}

2. setter 주입

setter메서드를 통해 의존 관계를 주입해준다.
@Autowired 어노테이션을 붙여주면 인자로 필요한 해당 객체를 Bean으로 인지하고 해당 객체를 필드에 set 해준다.
클라이언트에게 노출되어 의존 관계가 변경이 가능하다

@Service
public class UserService{
	private UserRepository userRepository
    
    @Autowired
    public void setUserRepository(UserRepository userRepository){
    	this.userRepository = userRepository
    }
}

3. 필드 주입

필드 이름이 바로 주입하는 방법.
@Autowired 어노테이션 하나로 주입할수 있어 편리한 방법이지만, 변경이 불가능하므로 내부 코드에서 직접 수정해야하는 단점이 있다.
애플리케이션과 관련 없는 테스트 코드, 설정을 목적으로 하는 @Configuration 이외의 곳에서는 지양하는 것이 좋다.

스프링에서 권장하고 있지 않는 방법이다.

@Service
public class UserService{
	@Autowired
	private UserRepository userRepository
}

Reference

https://devlog-wjdrbs96.tistory.com/165?category=882236

https://velog.io/@dsunni/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%9B%90%EB%A6%AC-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC-%EC%9E%90%EB%8F%99-%EC%84%A4%EC%A0%95

https://velog.io/@ayoung0073/springboot-IoC%EC%A0%9C%EC%96%B4%EC%9D%98-%EC%97%AD%EC%A0%84%EC%99%80-DI%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85

https://gmlwjd9405.github.io/2018/11/10/spring-beans.html

https://jojoldu.tistory.com/27

https://jongmin92.github.io/2018/02/11/Spring/spring-ioc-di/

https://cbw1030.tistory.com/54

https://devlog-wjdrbs96.tistory.com/426?category=882236

profile
내 꿈은 좋은 개발자

0개의 댓글