* 이 포스트는 인프런에 있는 김영한 님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술' 강의를 들으면서 내용을 정리한 글입니다. *

 

[메인 컨텐츠]

[무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 인프런 | 강의 (inflearn.com)

 

# 2022-03-04 #

 

#목차#

15. 컴포넌트 스캔과 자동 의존관계 설정

16. 자바 코드로 직접 스프링 빈 등록하기


15. 컴포넌트 스캔(Component scan)과 자동 의존관계 설정

 

컴포넌트 스캔이란?

컴포넌트(@Component)는 스프링에서 관리하는 클래스 앞에 붙는 annotation(@)이다. @Component가 붙은 클래스는 자동으로 스프링이 관리한다. 앞서 컨트롤러 클래스에는 @Controller라는 annotation을 붙였는데, 이것도 컴포넌트에 해당한다. (@Controller annotation 클래스를 보면 해당 클래스에도 @Component annotation이 붙어있기 때문이다.)

 

그런데 스프링이 클래스들을 관리하는 방법은 컴포넌트 스캔뿐만이 아니다. 자바 코드를 사용해서 직접 스프링 빈(@Bean)을 등록할 수도 있다. 강의에서는 각각의 장단점이 있고 섞어서 사용할 수도 있으니 어쨌든 두 방법을 모두 알긴 알아야 한다고 하셨다. 

 

우선 컴포넌트 스캔 방법부터 다뤄보자. 앞서 컨트롤러, 서비스, 리포지토리 클래스를 만들었다. 이 클래스들 역시 각자의 역할이 있고(컨트롤러-서비스-리포지토리 방식 구성), 따라서 컴포넌트 스캔을 할 수 있다. 정확히 말하면, 이 [컨트롤러-서비스-리포지토리] 구성이 정말 자주 사용되는 구성이다 보니 스프링에서는 각각의 구조를 위한 annotation이 따로 있는 것이다. 

 

그래서 서비스 클래스에는 @Service, 리포지토리 클래스에는 @Repository annotation을 붙여주면 스프링에서 이 클래스들을 관리하고 필요할 때 알아서 배치할 수 있다. 

 

컴포넌트 스캔이 필요한 이유

그런데 앞서 작성한 코드로도 충분히 컨트롤러-서비스-리포지토리의 구조를 갖춘 것 같은데 왜 컴포넌트 스캔이 필요할까? '자동 의존관계 설정(Dependency Injection. DI)' 때문이다. 

 

해당 구조에서는 컨트롤러는 서비스에게, 서비스는 리포지토리에게 '의존'한다. 즉 컨트롤러의 메소드는 서비스가 없으면 작동할 수 없고, 서비스의 메소드도 리포지토리가 없으면 실행될 수 없다. 이러한 관계를 의존관계(Dependency)라고 한다. 그래서 컨트롤러나 서비스의 생성자에 매개변수로 서비스나 리포지토리 타입의 매개변수를 넣는 식으로 이 의존관계를 해결했던 것이다. 

 

그런데 스프링에서 컴포넌트 스캔을 하면 이 과정을 스프링이 알아서 해 준다. 직접 할 필요가 없다는 것이다. 

 

컴포넌트 스캔하는 방법/과정

(1) 각 클래스에게 역할에 맞는 annotation을 붙인다. 

(2) 클래스 사이에 의존관계가 있는 경우가 있다. 

A 클래스가 B 클래스에게 의존한다고 해 보자. 이때 A 클래스는 자신의 생성자를 만들 때 B 클래스 타입의 매개변수를 받아 와서 new() 방식으로 B 클래스에게 의존한다. (뒤에서 다루겠지만 생성자를 만드는 것 말고 다른 의존 방법도 있다. )

위와 같은 기존 방식과 달리, 컴포넌트 스캔에서는 new()를 쓰지 않는다. 왜냐하면 객체의 생성을 스프링에서 관리하기 때문이다. 그냥 A 클래스의 생성자로 B 클래스 타입의 매개변수를 받는 것은 동일한데, 다른 점은 new()를 쓰지 않고 싱글턴 패턴으로 객체를 관리해 주며, 해당 생성자 위에 @Autowired이라는 annotation을 붙인다는 것이다. 

 

싱글턴 패턴을 쓰는 이유는 굳이 Service 등의 클래스를 여러 개 만들 필요가 없기 때문이다. 또한 여러 객체가 만들어지는 경우 그 안의 필드, 리소스들이 서로 동일하지 않게 되어 문제가 발생할 가능성도 커진다. 

 

[1] 컴포넌트 스캔 미사용

// service.java
public class Service{
	private Repository repository;
    
    public Service(Repository repository){
    	this.repository = repository;
    }
    
    // methods
    
    // example method
    public void example(){
    	service = new Service(new Repository());
    }
    
}

// repository.java
public class Repository{

	// methods

}

[2] 컴포넌트 스캔 사용

// service.java
@Service
public class Service{
	private Repository repository;
    
    @Autowired
    public Service(Repository repository){
    	this.repository = repository;
    }
    
    // methods...
}

// repository.java
@Repository
public class Repository{
	// methods...
}

 

 

의존관계 주입하는 여러 방법

의존관계 주입에는 크게 3가지 방법이 있다. (1) 생성자 주입, (2) 메소드(setter) 주입, (3) 필드 주입이다. 

그러나 생성자 주입이 편리하고 단점도 적어서(없는건지 적은건지 모르지만 셋 중에는 가장 좋다.) 주로 생성자 위에 @Autowired를 붙인다. 

 

메소드 주입을 잘 하지 않는 이유

메소드 주입은 setter 메소드의 매개변수로 의존하는 클래스 타입의 변수를 받아서 할당하는 방식이다. 그러나 보통 스프링 서버를 한번 로딩하고 나면 굳이 변수를 여러 번 할당하거나 바꿔 낄 일이 없다. (예를 들어서, 서비스 객체에 리포지토리를 여러 번 바꿔 끼울 일이 없다. ) 그래서 굳이 만들 필요가 없다. 또한 이런 메소드를 만들 때는 public으로 선언해야 하는데, 만일 다른 곳에서 setter 메소드를 잘못 사용하면 중간에 다른 리포지토리가 할당되는 등의 오류가 발생할 수 있기 때문에 권장하지 않는다. 

 

필드 주입을 잘 하지 않는 이유

중간에 바꿔 낄 수 없고, 인텔리제이 등의 IDE에서도 권장하지 않는다는 방식으로 밑줄이 그어진다. 


16. 자바 코드로 직접 스프링 빈 등록하기

 

그런데 컴포넌트 스캔을 사용하지 않고 직접 스프링 빈을 등록하는 방법이 있다. 

 

직접 등록하는 방법의 이점

-리포지토리 등 클래스를 '바꿔 낄 일'이 생길 때 필요하다. (ex. DB를 여러 개 사용하는 경우, 다른 DB로 변경하는 경우 등등)

 

컴포넌트 스캔과 비교했을 때의 단점

-컴포넌트 스캔 코드가 훨씬 간결하다. 

-또한 컴포넌트 스캔을 아예 안 쓸 수는 없다. @Service, @Repository annotation은 스프링 빈으로도 등록할 수 있지만, @Controller annotation은 사용해야만 스프링에서 컨트롤러로 인식할 수 있다. 

 

스프링 빈 등록하는 방법

(1) Config 클래스를 SpringBootApplication 클래스와 같은 디렉토리에 만든다. 

(2) 해당 클래스 위에 @Configuration annotation을 붙인다. -> 스프링에게 @Bean들을 찾을 때 여기서 찾으라고 표시해 주는 역할인 것 같다.

(3) 해당 클래스 안에다가는 @Bean을 사용해서 생성자들을 등록한다. 

 

예시 코드

@Configuration
public class Config{
	
    @Bean
    public Service service(){
    	return new Service(repository());
    }
    
    @Bean
    public Repository repository(){
    	return new Repository();
    }
    
}

 

'server-side > spring' 카테고리의 다른 글

spring 개발일지 21-22강  (0) 2022.03.08
spring 개발일지 18-19강  (0) 2022.03.08
spring 개발일지 10-12강 + 개발 고민주제 틈틈이 정리  (0) 2022.03.04
spring 개발일지 5-6강  (0) 2022.03.03
spring 개발일지 4강  (0) 2022.03.03

+ Recent posts