About Dependency Injection

koonlx·2024년 11월 5일

Spring

목록 보기
2/7

Intro

의존성 주입은 소프트웨어 디자인 패턴 중 하나로, 객체 간의 의존 관계를 코드 내부에서 직접 생성하지 않고 외부에서 주입하는 방식이다. 이를 통해 객체는 다른 객체와의 결합도가 낮아지며, 유지보수성과 테스트의 용이성이 향상된다.

의존성 주입의 주요 목적

  • 결합도 낮추기: 객체 간의 강한 결합을 줄여 코드의 유연성과 재사용서을 높인다.
  • 테스트 가능성 향상: 외부에서 의존성을 주입받기 때문에, 유닛 테스트 시 모의 객체(mock object) 등을 사용하여 독립적으로 테스트 가능.
  • 코드의 명확성 증가: 객체 생성과 로직의책임이 분리되기 때문에 코드를 더 직관적으로 이해할 수 있다.

의존성 주입은 생정자 주입, 세터 주입, 필드 주입의 세 가지 방식으로 구현할 수 있다.

Java Example Code

생성자 주입

  • 인터페이스 정의
public interface MessageService {
	void sendMessage(String message);
}
  • 구현 클래스
public class EmailService implements MessageService {
	@Override
    public void sendMessage(String message) {
    	System.out.println("Email sent: " + message);
	}
}
  • 의존성 주입을 사용하는 클래스
public class NotificationSender {
	private MessageService messageService;
    
    // 생성자 주입
    public NotificationSender(MessageService messageService) {
    	this.messageService = messageService;
	}
	
    public void notify(String message) {
	    messageService.sendMessage(message);
    }
}
  • Main 클래스에서 의존성 주입
public class Main {
	public static void main(String[] args) {
    // 의존성을 주입하여 객체 생성
    MessageService service = new EmailService();
    NotificationSender sender = new NotificationSender(service);
    
	// 메서드 실행
    sender.notify("Hello, Dependency Injection!");
}

특징

  • 의존성을 생성자에게 주입받기 때문에, 객체가 생성될 때 필수적으로 의존성이 주입된다.
  • 의존성이 없으면 객체가 생성되지 않기 때문에, 의존성의 필수성을 보장한다.
  • 불변성을 유지할 수 있다. 생성자에서 주입된 의존성은 이후에 변경할 수 없으므로 객체가 안정적인 상태를 유지한다.
  • 테스트와 유지보수에 유리하며, 의존성이 변경되지 않는 경우가 많아 코드가 더 안전하고 명확해진다.

장단점

  • 장점: 필수 의존성을 보장하고 불변성을 유지한다. 테스트와 유지보수에 유리하다.
  • 단점: 의존성이 많은 경우 생성자가 길어져 코드가 복잡해질 수 있다.

세터 주입

  • 인터페이스 정의
public interface MessageService {
	void sendMessage(String message);
}
  • 구현 클래스
public class EmailService implements MessageService {
	@Override
    public void sendMessage(String message) {
    	System.out.println("Email sent: " + message);
	}
}
  • 세터 주입을 사용하는 클래스
public class NotifiactionSender {
	private MessageService messageService;
    
    // 세터 메서드를 통한 의존성 주입
    public void setMessageService(MessageService messageService) {
    	this.messageService = messageService;
	}
    
    public void notify(String message) {
	    if (messageService != null) {
		    messageService.sendMessage(message);
	    } else {
    		System.out.println("MessageService is not set");
		}
	}
}	
  • Main 클래스에서의 사용
public class Main {
	public static void main(String[] args) {
    	// 객체 생성
        MessageService service = new EmailService();
        NotificationSender sender = new NotificationSender();
        
        // 세터 메서드로 의존성 주입
        sender.setMessageService(service);
        
        // 메서드 실행
        sender.notify("Hello with Setter Injection!");
    }
}

특징

  • 세터 메서드를 통해 선택적으로 의존성을 주입할 수 있어, 의존성이 곡 필요하지 않은 경우에 유연하게 사용할 수 있다.
  • 의존성을 나중에 변경할 수 있기 때문에 가변성을 가질 수 있다.
  • 기본 생성자로 객체를 먼저 생성한 뒤, 필요할 때 세터 메서드로 의존성을 설정할 수 있다.
  • 주로 의존성이 선택적인 경우나, 객체가 생성된 후에 의존성을 동적으로 주입해야 할 때 적합하다.

장단점

  • 장점: 의존성을 선택적으로 주입할 수 있으며, 필요한 경우 의존성을 변경할 수 있다.
  • 단점: 객체 생성 시 의존성을 필수적으로 보장할 수 없다. 잘못된 상태의 객체가 생성될 가능성이 생긴다.

필드 주입

  • 필드 주입을 사용하는 클래스
public class NotificationSender {
	// 필드 직접 주입(권장되지 않는 방식이다.)
	public MessageService messageService;
    
    public void notify(String message) {
	    if (messageService != null) {
			messageService.sendMessage(message);
	    } else {
    		System.out.println("MessageService is not set");
	    }
	}
}
  • Main 클래스에서의 사용
public class Main {
	public static void main(String[] args) {
    	MessageService service = new EmailService();
        NotificationSender sender = new NotificationSender();
        
        sender.messageService = service;
        
        sender.notify("Hello with Field Injection!);
    }
}

특징

  • 클래스의 필드에 직접 의존성을 주입하는 방식으로, 주입 방식 중 가장 간단하고 코드가 간결해 보인다.
  • 직접 필드에 접근해 의존성을 설정하기 때문에 캡슐화 원칙을 위배할 가능성이 있으며, 외부에서 필드에 접근할 수 있는 경우 코드의 안정성이 낮아질 수 있다.
  • 테스트와 유지보수가 어려워질 수 있다. 예를 들어, 테스트 시 모의 객체(mock object)를 주입하기 어렵다.
  • Spring에서는 주로 @Autowired 애너테이션을 필드에 붙여 의존성을 자동으로 주입한다.

장단점

  • 장점: 코드가 간결하고 가독성이 높아 보인다.
  • 단점: 테스트가 어려워지고 캡슐화를 위배할 가능성이 있다. 외부에서 의존성을 변경하기 어렵고, 객체의 상태가 불안정해질 가능성이 있다.

차이점 정리

  • 의존성 주입의 시점과 방법: 생성자 주입은 객체 생성 시에 주입이 이루어지며, 필수적인 의존성을 보장한다. 반면, 세터 주입은 객체 생성 후에 설정될 수 있어 유연성이 높다. 필드 주입은 코드가 간단해 보일 수 있지만, 외부에서 직접 접근할 수 없기 때문에 유지보수와 테스트에 불리하다.
  • 객체의 가변성: 생성자 주입을 사용하면 객체가 생성된 이후에 의존성을 변경할 수 없어 불변성을 유지할 수 있다. 반면, 세터 주입과 필드 주입은 객체가 생성된 후에도 의존성을 수정할 수 있으므로 가변성이 있다.
  • 테스트 용이성: 생성자 주입과 세터 주입은 테스트 시 외부에서 의존성을 주입하기가 쉽다. 반면 필드 주입은 직접적으로 접근하기 어려워 테스트에서 mock 객체를 주입하는 것이 까다로울 수 있다.
  • 캡슐화: 필드 주입은 클래스 내부에서 필드를 직접 변경하는 방식이기 때문에, 캡슐화 원칙을 위배할 가능성이 있다. 반면 생성자 주입과 세터 주입은 외부에서 제어 가능하여 더 안전한 코드 구조를 만들 수 있다.

따라서, 객체의 불변성을 유지하고 의존성을 강제해야 한다면 생성자 주입을, 유연하게 선택적 의존성을 추가해야 한다면 세터 주입을, 코드의 간결성만 중요하고 테스트 및 유지보수에 대한 요구가 적다면 필드 주입을 사용하는 것이 좋다.

profile
Server Developer

0개의 댓글