본문 바로가기
Spring

[Spring/스프링] 제어의 역행(IoP)과 의존성 주입(Dependency Injection, DI)

by LasBe 2022. 3. 31.
반응형

⚡️ 제어의 역행 (IoP)


제어의 역행은 간단히 객체 생성과 의존 관계를 직접 처리하는게 아니라

소스에 의존관계가 명시되지 않도록 컨테이너가 대신 처리한다고 설명할 수 있습니다.

 

이는 스프링의 핵심 개념이며 IoP를 두 가지 형태로 지원합니다.

  1. Dependency Lookup
  2. Dependency Injection

 

Dependency Lookup은 컨테이너가 애플리케이션 운용에 필요한 객체를 생성하고

클라이언트는 생성한 객체를 검색(Lookup)하여 사용하는 방식을 뜻합니다.

 

하지만 이는 실제로 잘 사용되지 않고 대부분 DI를 사용하여 개발하기 때문에 이 글에서는 생략하겠습니다.

 

 

 

⚡️ 의존성 주입 (Dependency Injection)


 

의존성 주입은 객체 사이의 의존관계를 스프링 설정 파일에 등록된 정보를 바탕으로

컨테이너가 자동으로 처리해주는 개념입니다.

 

따라서 의존관계에 있는 객체를 수정할 때 직접 코드를 수정하지 않고,

스프링 설정 파일 수정만으로 변경사항을 적용할 수 있어서 유지보수성이 향상됩니다.


📌 의존성 관계

의존성 관계란 객체와 객체의 결합 관계입니다.

 

즉 한 객체에서 다른 객체의 변수나 메소드를 이용해야 한다면

이용하려는 객체에 대한 객체 생성과 생성된 객체의 정보가 필요하게 되고

new 키워드를 사용해 객체를 직접 생성하는 코드는 강한 결합도를 유발합니다.

 

아래 코드는 여행갈 때 소니 카메라를 챙겨가려는 내용입니다.

public class SonyCamera implements Camera {
	@Override
	public void shooting() {
		System.out.println("SonyCamera로 사진을 찍었습니다.");
	}
}
public class Travel {
	private Camera camera;
	
	public void photography() {
		camera = new SonyCamera();
		camera.shooting();
	}
}

 

중간에 캐논 카메라로 바꿔 들고가려고 한다면 아래와 같이 코드 자체를 수정해주어야 하는 소요가 발생합니다.

public class Travel {
	private Camera camera;
	
	public void photography() {
		camera = new CannonCamera();
		camera.shooting();
	}
}

 

지금과 같이 간단한 코드라면 단어 몇개만 바꾸고 끝이겠지만

프로젝트의 볼륨이 크면 클수록 수정할 부분이 더 많아져 유지보수에 어려움이 생길겁니다.

 

이러한 문제는 스프링에서 객체 간 관계 설정을 의존성 주입을 통해

컨테이너를 거치게 함으로써 결합도를 느슨하게 만들 수 있습니다.


📌 스프링에서 의존성을 주입하는 방법

스프링에서는 의존성을 주입하는 3가지 방법이 존재합니다.

  1. 생성자 주입
  2. 필드 주입
  3. setter 주입

 

🔎 생성자 주입

@Component
public class Travel {
	private final Camera camera;
	
	@Autowired
	public Travel(Camera camera) { // 생성자 주입
		this.camera = camera;
	}
	
	public void photography() {
		camera.shooting();
	}
}

@Autowised 어노테이션을 통해 생성자에 객체를 주입시켜줍니다.

(스프링 4.3 버전부터는 단일 생성자인 경우 @Autowised를 명시하지 않아도 됩니다.)

 

생성자 주입 방식은 다른 방식과는 다르게 필드에 final 키워드를 사용할 수 있다는 메리트가 있습니다.

 

이는 실행중에 객체가 바뀌는 것을 컴파일 시점에 미리 방지할 수 있어서

다른 방식보다 더 안전한 사용이 가능해 스프링 팀에서도 의존성 주입 중 생성자 주입을 추천하고 있습니다.


스프링 컨테이너는 생성자나 메소드, 멤버변수위에 붙은 @Autowised를 확인하는 순간

해당 변수의 타입을 체크하고, 메모리에 그 타입의 객체의 존재를 확인한 후 주입해줍니다.

 

만약 컨테이너가 생성자 위에 붙은 어노테이션을 보고 Camera 타입의 객체를 찾았는데

메모리에 그 객체가 올라가있지 않다는 것을 확인하면

NoSuchBeanDefinitionException과 함께 에러를 발생시킵니다.

 

그렇다면 빈으로 등록하기 위해 카메라 타입의 클래스 객체를 어노테이션으로 다음과 같이 처리해주어야겠죠?

@Component
public class SonyCamera implements Camera {
	@Override
	public void shooting() {
		System.out.println("SonyCamera로 사진을 찍었습니다.");
	}
}

🔎 필드 주입

@Component
public class Travel {
	
	@Autowired
	private Camera camera;
}

필드 주입은 매우 간단하게 필드 위에 @Autowired 어노테이션을 추가해주면 됩니다.

 

매우 간단하게 의존성이 주입되지만 이는 스프링 팀에서 권장하는 방식이 아니기 때문에 사용을 지양하는 편이 좋습니다.

 

이에 대한 자세한 내용은 추후 다룰 예정입니다.


🔎 setter 주입

@Component
public class Travel {
	private Camera camera;
	
    	@Autowised
	public void setCamera(Camera camera) {
		this.camera = camera;
	}
}

필드 값을 변경하는 setter를 이용해 의존 관계를 주입할 수도 있습니다.

 

Setter 주입은 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용됩니다.

 

메소드의 이름은 꼭 setXXX()가 아니어도 되지만 일관성과 가독성을 위해 규칙을 지키는 걸 추천합니다.


📌 @Value, 값 주입

@Component
public class Travel {
	
    @Value("서울")
    private String location;
    
    @Value("${ex.str}")
    private String str;
}

변수에 값을 직접 주입해주어야 할 땐 @Value 어노테이션을 사용합니다.

 

첫번째 변수와 같이 직접적으로 넣어줄 수도 있고

EL문을 통해 공통적으로 관리할 별도의 값을 파일로 만들어놓고 불러와서 사용할 수도 있습니다.

 

표현식을 사용할 수 있기 때문에 다양하게 응용할 수 있습니다.


📌 @Qualifier, 의존성 주입 시 중복되는 타입 구분

 

[Spring/스프링] @Qualifier 구분자, 의존성 주입 시 위치

목차 " data-ke-type="html"> <>HTML 삽입 미리보기할 수 없는 소스 ⚡️ 시작하기 전 스프링을 공부하며 생성자 주입 방식으로 의존성을 주입하는 도중 같은 타입의 빈을 구별하기 위해 코드에 @Qualifier

lasbe.tistory.com

타입이 중복될 때 사용하는 @Qualifier 어노테이션은 위 글에서 소개했으니 참고바랍니다.

반응형

댓글


오픈 채팅