거북이-https://velog.io/@violet_evgadn 이전완료

스프링 빈 활용 본문

웹 개발/Spring(활용)

스프링 빈 활용

VioletEvgadn 2022. 8. 3. 22:58

XML 활용한 Spring Bean

XML 방식으로도 Spring Bean을 등록할 수 있다. 하지만, 최근에는 사용하지 않는다고 봐도 무방할 정도로 활용도가 떨어진다.

만약 XML 방식을 알고 싶다면 찾아보는 것을 추천하지만 정말 활용도가 1도 없다고 생각하기 때문에 이 글에서는 스킵하겠다.


Java Config를 활용한 방식

Java Config는 @Bean 어노테이션을 활용해서 객체를 Bean으로 등록하는 방식이다.

코드를 통해 알아보자

class MemberService{
	...
}

class VIPService{
	...
}

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService(){
        return new MemberService();
    }

    @Bean
    public VIPService vipService(){
        return new VIPService();
    }
    
}

설명하기 앞서, 실제 프로젝트에선 MemberService와 VIPService 클래스는 새로운 디렉터리에 들어가서 하나의 기능을 하고 있을 것이다. 하지만 어디까지나 Spring Bean을 관찰하기 위한 과정이므로 새로운 디렉터리에 넣었다고 가정하고 Spring Bean 등록 과정만 보도록 하자.

 

@Bean을 활용하기 위한 방법은 간단하다.

1. Spring Bean들을 모아둘 Class를 생성한 이후 @Configuration 어노테이션을 붙여준다.

2. Spring Bean으로 등록할 객체에 @Bean 어노테이션을 붙여주고 return 명령을 통해 객체를 반환시킨다.

 

자. 이게 끝이다. 쉽지 않은가?
그렇다면 실제로 Spring Context에 Bean으로 등록되어 있는지 확인해보자

public class ReviewApplication {

	public static void main(String[] args) {
		ApplicationContext ap = new AnnotationConfigApplicationContext(AppConfig.class);

		String[] beanNames = ap.getBeanDefinitionNames();

		for(String name:beanNames) System.out.println(name);
	}
}

우리는 앞서 일반적으로 Spring Container는 ApplicationContext를 의미함을 배웠다.

따라서 ApplicationContext를 불러오고 Spring Bean에 등록한 AppConfig Class를 불러오자.

 

이후 ap.getBeanDefinitionNames()를 통해 등록된 Bean 이름들을 모두 출력해보면 아래와 같은 결과가 나온다

와! 위에서 등록했던 memberService와 vipService가 Spring Bean으로 등록되었음을 알 수 있다.

또한 appConfig라는 클래스도 Spring Bean으로 등록됨을 알 수 있다. 이는 `public class AppConfig`를 @Configuration 어노테이션으로 등록해주었기 때문에 이 AppConfig 클래스 또한 Spring Bean으로 올라간 것이다.

어찌 되었든 AppConfig도 클래스이기 때문이다.

참고로 따로 이름을 선언해주지 않으면 맨 앞글자(AppConfig에서 A)가 소문자로 바뀌어 Bean으로 등록됨을 알 수 있다.

 

◎ @Configuration 방식의 장점 및 한계

MemberService를 보면 return new MemberService()로써 객체를 생성함을 알 수 있다.

그런데 나중에 회사 차원에서 멤버 서비스를 추가하거나 변경하고 싶다고 가정하자. 그래서 이렇게 변경된 멤버 서비스 내용을 MemberService2 클래스로 만들었다.

원래 코드라면 new MemberService() 부분을 코드 상에서 모두 찾아 new MemberService2()로 수정해야 할 것이다.

하지만 Bean에 등록하였다면 이런 복잡한 과정을 거치지 않아도 된다.

단순히 memberService() 클래스를 `return new MemberService2()`로 바꿔주기만 하면 바로 Spring Bean에 올라가는 클래스가 바뀌기 때문에 변경사항이 바로 적용되는 것이다.

 

하지만 한계 또한 존재한다.

결국에 서비스가 넓어질수록 Spring Bean에 올려야 할 클래스는 다양해질 것이고, 그만큼 AppConfig에 올려야 할 Spring Bean 생성 함수도 많아지게 될 것이다.

예를 들어 A, B Class만 활용할 경우 해당 인스턴스가 Spring Bean으로 올라가 있는지 쉽게 확인할 수 있다.

하지만, A1, A2,..., A10000 Class가 존재한다고 가정하자. 그렇다면 AppConfig에 A1, ..., A10000 Class가 Spring Bean으로 올라갔는지 하나하나 확인해야 하는데 이 경우 너무나도 많은 작업이 요구되고 Human Error가 발생할 확률도 커질 것이다

 

따라서 스프링에서는 "컴포넌트 스캔" 방식을 활용해 @Bean 어노테이션을 사용하지 않더라도 Spring Bean을 등록하고 DI를 진행할 수 있게 만들었다.


컴포넌트 스캔

A1, A2, ..., A10000 클래스를 Spring Bean에 올려야 한다고 가정하자.

원래라면 `public A1 a1() { return new A1() }` 코드를 10000개 만들어서 Spring Bean에 등록시켜야 한다.

이야... 생각만 해도 너무나도 재밌는 작업이 될 것이다.

 

개발자는 이런 재미있는 작업을 하고 싶지 않았기 때문에 컴포넌트 스캔 방식이라는 것을 활용한다.

개발자들은 생각했다.

"어차피 A1 클래스를 Spring Bean으로 올릴 생각하고 만드는 건데, A1 클래스를 만들 때부터 이 클래스를 Bean에 올릴 것임을 미리 명시하면 좋지 않을까? 그렇다면 굳이 @Bean을 통해 추가 코딩을 하지 않아도 될 것 같은데!"

 

이런 생각에서 나온 개념이 컴포넌트 스캔이다.

 

컴포넌트 스캔이란 Spring이 Bean으로 등록될 준비가 된 클래스들(Bean으로 올릴 것이라고 명시한 클래스들)을 Scan 하여 자동으로 Bean으로 등록해주는 과정을 말한다.

즉 Class에 어노테이션으로 "이 클래스는 Bean에 올릴 것입니다!"를 알리면 컴포넌트 스캔을 통해 해당 클래스에 대한 객체(Bean)를 생성하여 Spring Bean 저장소에 올리는 것이다.

 

Spring Bean에 올릴 Class에는 @Component 어노테이션을 활용해 Spring Bean에 올릴 Class임을 알리고, @ComponentScan 어노테이션을 통해 컴포넌트 스캔을 수행하여 Spring Container를 활용한 IoC가 가능해진다.

즉, @Component로 등록한 Class의 Bean을 @ComponentScan 어노테이션을 가진 클래스가 찾아서 활용하는 것이다.

 

컴포넌트 스캔을 수행할 때는 @Component 어노테이션을 가지고 있는 모든 객체를 컨테이너에 올려놓는데, 나중에 배울 @Controller, @Service, @Repository 어노테이션 또한 @Component 어노테이션을 상속받고 있어 컴포넌트 스캔 시에 자동으로 빈으로 등록되는 것이다. 또한 Main Class에 있을 @SpringBootApplication 어노테이션이 @ComponentScan 어노테이션을 상속받고 있기 때문에 컴포넌트 스캔을 그렇게 신경 쓰지 않고서도 코딩이 가능해지는 것이다.

 

@Component
public class AppConfig{
	...
    public int sum(int a, int b){
        return a+b;
    }
   	...
}

위와 같이 설정만 하면 컴포넌트 스캔을 수행할 때 자동으로 해당 클래스를 Bean으로 만들어 컨테이너에 저장시키는 것이다.

 

@ComponentScan("com.example.review")
public class Main{
	...
}

위와 같이 설정하면 Main Class가 수행될 때 컴포넌트 스캔을 수행하여 위에서 @Component로 지정했던 AppConfig Bean을 활용할 수 있게 되는 것이다.

 

@ComponentScan에 입력값이 들어가 있음을 알 수 있는데, 이는 패키지 경로를 의미한다.

@ComponentScan에 패키지 경로를 입력하면 해당 패키지를 포함한 모든 하위 패키지의 @Component에 대하여 컴포넌트 스캔을 실행하는 것이다.

 

즉, @ComponentScan에 입력한 패키지 경로보다 상위에 있는 @Component 클래스들은 컴포넌트 스캔을 통해서 (해당 클래스에서) 활용할 수 없게 되는 것이다.

 

조금 더 깊게 들어가면 includePackages, excludePackages Parameter를 활용해 추가할 수 있으나, 사실 그렇게까지 복잡하게 코드를 짤 필요는 없으므로 설명하지 않고 넘어가겠다.

 

◎ @Component와 @Bean

결국 @Component와 @Bean은 Spring Bean을 올리기 위한 어노테이션인 것이다. 그렇다면 정확히 어떤 차이가 존재할까?

 

먼저 범위의 차이가 존재한다.

@Component는 Class Level에 어노테이션으로 @Bean은 Method Level에 어노테이션으로 붙임을 알 수 있다.

즉, @Component를 활용할 경우 클래스 1개당 Bean 1개와 매칭 시킬 수 있으나 @Bean을 활용할 경우 클래스 1개당 Bean N개와 매칭시킬 수 있게 되는 것이다.

 

또한 @Bean은 사용자가 컨트롤하지 못하는 Class나 유연하게 객체를 생성하고 넘기고 싶을 때 활용한다.

예를 들어 외부 라이브러리를 Bean으로 활용하고 싶을 경우 외부 라이브러리에 대한 코드를 복사 + 붙여 넣기 하여 @Component 어노테이션을 붙이는 것은 매우 비효율적일 것이다.

이럴 때 메서드 하나를 생성하여 외부 라이브러리를 객체 형태로 반환하게 한 다음 해당 메서드에 @Bean을 붙이면 외부 라이브러리 또한 Spring Bean으로 활용할 수 있을 것이다.

반대로 @Component는 Class 자체를 빈으로 등록하고 싶을 때, 이 Class가 개발자가 Control 할 수 있거나 직접 개발한 클래스일 경우 사용하면 좋다.


요약

Spring Container에 Bean을 등록하는 방법은 총 3가지가 존재한다.

 

XML로 등록하는 방법, @Bean을 활용하는 방법, @Component를 활용하는 방법

 

먼저 최근 XML로 등록하는 방법은 거의 쓰이지 않으므로 설명하지 않았다.

 

@Bean을 활용하기 위해선 먼저 Spring Bean들을 저장할 클래스 하나를 생성해야 한다.

이후 클래스에 @Configuration 어노테이션을 활용해 Bean들을 생성하는 클래스임을 명시하고 @Bean을 메서드에 붙임으로써 Spring Bean을 등록한다.

 

@Component를 활용하는 방법도 존재한다.

이를 컴포넌트 스캔 방식이라고 하는데, Spring Bean으로 만들 클래스에 @Component 어노테이션을 활용하고 Bean을 활용할 클래스에 @ComponentScan 어노테이션을 붙임으로써 활용할 수 있게 된다.

 

@Component는 클래스에 적용되며 개발자가 수정 가능한 클래스에 활용하는 반면에 @Bean은 메서드에 적용되며 주로 외부 라이브러리(개발자가 수정 불가능한 클래스)를 Bean으로 등록할 경우에 많이 활용한다.

'웹 개발 > Spring(활용)' 카테고리의 다른 글

build.gradle 사용법  (0) 2022.11.27
Spring Web 메인 클래스  (0) 2022.08.05
AOP 실습  (0) 2022.08.04
DI 주입 실습  (0) 2022.08.04
Comments