JAVA/Spring

[Spring] 의존성 주입 (DI, Dependency Injection)

4Legs 2021. 3. 10. 21:13

객체 의존성 (Object Dependency)

한 객체가 다른 객체를 참조하고 있을 때, 의존성을 가진다고 한다. 다음 예시를 보자.

class Controller{
    private MemoryRepository repository = new MemoryRepository();
}

Controller 클래스에서 new 키워드를 이용해 MemoryReopsitory 객체를 생성했다.

이 때, Controller 객체는 MemoryRepository 객체에 의존한다고 한다.

 

의존성 주입 (Dependency Injection)

위의 의존 관계에는 문제점이 하나 있다.

만약 MemoryRepository 클래스가 수정된다면, Controller 클래스 또한 수정해줘야 하기 때문이다. 즉, 두 클래스의 결합도(Coupling)이 높아지므로 좋지 않은 설계이다.

Controller 클래스의 입장에서, 자신이 직접 Repository 에 해당하는 객체를 지정하지 않고 외부에서 지정해주는 대로 Repository의 역할만 잘 수행한다면 위와 같은 경우는 발생하지 않을 것이다.

쉽게 말하자면 다음과 같다.

interface Repository{
    void save(String id);
    void read();
}

class MemoryRepository implements Repository{
    public void save(String id){
    	//...
    }
    
    public void read(){
    	//...
    }
}

class DataRepository implements Repository{
    public void save(String id){
    	//...
    }
    
    public void read(){
    	//...
    }
}


class Controller{
    private final Repository repository;
    
    public Controller(Repository repository){
    	this.repository = repository;
    }
    
    public void logic(String id){
    	repository.save(id);
    }
    
    //...
}

 

Controller 의 입장에서는, 자신이 참조하는 Repository 객체가 MemoryRepository인지, DataRepository인지는 알 필요가 없다.

그냥 Controller 객체를 사용하는 누군가가 보내어, 자신에게 들어오는 Repository 객체의 save, read 메소드만 실행해 사용하면 되기 때문이다.

이처럼, 자신이 참조하는 객체에 대한 의존성을 외부(Framework)에서 주입받도록 하는 설계 패턴을 의존성 주입이라 한다.

 

의존성 주입의 방법들

Spring 프레임워크에서는 다양한 방법을 통해 의존성을 주입할 수 있다.

생성자 주입

위에서 살펴본 의존성 주입 방법이다.

interface Repository{
    void save(String id);
    void read();
}

class MemoryRepository implements Repository{
    public void save(String id){
    	//...
    }
    
    public void read(){
    	//...
    }
}

class DataRepository implements Repository{
    public void save(String id){
    	//...
    }
    
    public void read(){
    	//...
    }
}


class Controller{
    private final Repository repository;
    
    @Autowired
    public Controller(Repository repository){
    	this.repository = repository;
    }
    
    public void logic(String id){
    	repository.save(id);
    }
    
    //...
}

생성자는 객체의 생성 시 단 한번만 호출되는 것이 보장되므로, 해당 객체가 필수적이거나 불변해야 할 경우에 주로 사용한다.

위의 예시 코드처럼 멤버 변수에 final이 붙기 때문에 생성자를 통해 반드시 값이 들어와야 하며, 이는 변경될 수 없기 때문이다.

Setter 주입

Setter 메소드를 통해 의존관계를 주입한다. 이는 내가 의존 관계를 선택할 수 있거나, 변경될 수 있을 경우에 사용한다.

예를 들어, 내가 MemoryRepository나 DataRepository 중 어떤 Repository에 내용을 저장할 지 직접 선택할 수 있는 경우에 Setter 메소드를 통해 의존관계를 주입할 수 있다.

class Controller{
	private Repository repository;
    
    @Autowired
    public void setRepository(Repository repository){
    	this.repository = repository;
    }
    
    //...
}

※ @Autowired(required = false) 로 지정하면 주입할 대상이 없어도 에러를 발생시키지 않는다.

필드 주입

필드에 바로 주입하는 방법으로, 코드가 간단하지만 외부에서 접근할 방법이 없어 테스트가 매우 힘들어져 권장되는 방법은 아니다.

class Controller{
	@Autowired
	private Repository repository;
    
    //...
}

 

메소드 주입

일반 메소드를 구현해 주입하는 방법으로, 한 번에 여러 필드를 주입받을 수 있다는 특징이 있다.

class Controller{
	private Repository repository;
    
    @Autowired
    public void initiate(Repository repository){
    	this.repository = repository;
    }
    
    //...
}

 

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

[Spring] 스프링 컨테이너 (Spring Container)  (0) 2021.04.01
[Spring] 스프링 빈 (Spring Bean)  (0) 2021.04.01