메이쁘

[Spring] Spring Annotation 으로 쓰이는 @Autowired 를 사용하는 이유 쉽게 풀어보자! 본문

Technology/Web - Spring

[Spring] Spring Annotation 으로 쓰이는 @Autowired 를 사용하는 이유 쉽게 풀어보자!

메이쁘 2020. 7. 22. 23:15

안녕하세요.

 

Spring Framework 를 사용하다보면

 

MVC 중 컨트롤러에서 자주 사용하는 이 @Autowired 에 대해 알아보려 합니다.

 

 

예를 들어

 

 

@Autowired

private TempBc tempBc;

 

 

이렇게 사용하곤 하는데요.

 

왜 저기서 @Autowired 가 사용되었을까? 궁금해서 찾아보며 공부해봤습니다.

 

 

그리고 제가 이해한 내용을 작성하면서 저와 같이 궁금한 사람들에게 정보를 나눠드리고자 포스팅하게 되었습니다.

 

 

 

 

@Autowired 정의


  -  알아서 의존 객체(Bean) 를 찾아서 주입한다.

 

  -  의존 객체(Bean) 가 2개 이상이면 오류를 발생시킨다.

 

  -  보통, 인터페이스 선언 시 사용된다.

 

 

 

잉??

 

주로 다른 포스팅 글에서는 이렇게 정의내리고 끝나는데요.

 

의존 객체가 무엇인지, 어떻게 알아서 주입되는지...

 

차근차근 짚어나가보겠습니다.

 

 

 

먼저, 자바 또는 Spring 에서 말하는 의존성 이란

 

하나의 객체 내에서 필요에 의해 다른 객체를 사용해야 할 경우, 사용하는 것

 

을 뜻합니다.

Class Genesis {
  Car car = new Car();
    
  public Genesis() {
  	car.run();
  }
}

  -  Genesis (제네시스) 라는 클래스는 항상 Car 객체를 생성합니다.

이 때, 제네시스는 항상 Car 객체의 함수를 사용하므로 "제네시스는 Car에 의존한다." 라고 할 수 있습니다.

 

  -  이렇게 되면, 나중에 Car 객체의 run 함수를 수정할 경우 부득이하게 Genesis 생성자 결과 또한 변경되므로, 결합력이 높아 유지보수가 어려워집니다.

 

  -  또 다른 예시로, 다른 객체를 사용하고 싶다면 또 다시 다른 객체를 생성하고, 그 객체의 함수 명까지 추가 / 수정 해야하는 번거로움이 생깁니다.

 

  -  의존성이 높을 수록(= 결합력이 높을 수록) 위와 같은 상황이 잦아질 수 밖에 없습니다.

 

 

 

 

하지만, Spring의 특성 중 하나인 DI(Dependency Injection = 의존성 주입)

 

하나의 객체 내에서 필요에 의해 다른 객체를 생성자(Constructor) 또는 Setter 를 통해 주입시키는 것

 

을 뜻합니다.

 

class Genesis {
	Car car;
    
    public Genesis(Car car) {
    	this.car = car;
        car.run();
    }
}
Car car = new Car();
Genesis genesis = new Genesis(car);

  - Genesis 클래스 내에서 Car 객체의 함수를 사용하기 위해 이전 방법과는 다르게 Car 변수를 인자로 받아 내부에 선언한 변수에 옮겨담습니다. (주입)

 

  -  이렇게 되면, 후에 함수 명은 run으로 같지만 이전과는 다른 기능을 동작시키고 싶을 경우 Car 클래스를 상속받은 다른 클래스 객체를 생성자를 통해 집어넣기만 하면 됩니다. (조금 극단적이지만 실제 프로젝트에서는 자주 인터페이스나 다른 클래스를 상속(또는 구현)받아 재정의합니다.)

 

Spring에서는 개발자들이 수시로 객체 생성이나 의존성 높은 설계 대신에 변수 사용과 개발에만 집중할 수 있도록 이런 DI(의존성 주입) 기능을 제공합니다.

 

 

 

DI 기능 중 대표적으로 xml 사용, @Autowired 가 있습니다.

*** 점차 시간이 지나면서, @Autowired를 사용하게 되었습니다.

 

 

 

Spring Framework 에서는 인터페이스 와 인터페이스를 상속받은 클래스 중 인터페이스를 가지고 Controller와 Service를 동작시킵니다.

 

그 이유는 Effective Java 규칙 52번째 - "객체를 참조할 때는 그 인터페이스를 사용하라." 라는 말이 있듯이,

 

인터페이스를 사용할 경우 결합도가 느슨해져 프로그램은 더욱 유연해집니다.

*** 단, 인터페이스만 있으면 안되고 반드시 하나의 클래스 이상에서 인터페이스를 구현(재정의) 해야 합니다!

 

 

결국, @Autowired annotation 을 통해

 

"인터페이스를 선언한 변수에 자동적으로 그 인터페이스를 재정의한 객체를 주입시킨다."

 

라는 기능을 수행합니다.

 

 

 

이해하셨나요?

 

다시 맨 처음으로 돌아가서,

 

 

@Autowired

private TempBc tempBc;

 

 

의 의미는

 

tempBc 라는 TempBc 인터페이스 변수에 TempBc 인터페이스를 재정의한 클래스 객체 TempBcImpl(예로 적었습니다.) 를 주입시킨다.

 

라는 뜻이 되는거죠.

 

 


private TempBc tempBc = new TempBcImpl();

 (이와 비슷하게, List<Int> list = new ArrayList<Int>(); 가 있죠.)

 

이제 @Autowired 에 대한 의미를 이해하셨나요?

 

대신, 전제조건은 반드시 하나의 클래스만 해당 인터페이스를 재정의해야 합니다.

 

 

그래서 두 개 이상의 클래스로 하나의 인터페이스를 구현(재정의)할 시,

@Qualifier 또는 @Resource 로 여러 개의 재정의한 클래스 중 특정 Bean 이름(추후 포스팅하겠습니다!) 에 맞는 클래스 객체를 가져오겠다. 라고 표시해야 합니다.

 

 

 

이상입니다.

 

감사합니다!

Comments