벤티의 개발 로그

[Spring] Spring Bean 1편 (생성과 추가) 본문

카테고리 없음

[Spring] Spring Bean 1편 (생성과 추가)

sonsh75 2025. 2. 16. 16:06
이 포스트는 글또 10기 내 독서 모임에 참여하여 읽기 시작한 '스프링 교과서'를 읽고 정리한 내용을 바탕으로 작성했습니다.

 

 

Bean?

Windows 11에는 움짤도 있구나...^^

 

지난 글에서, Bean에 대해 정말 잠깐 언급한 부분이 있다.

Spring Context는 특정 객체를 Spring에 전달해서 프레임워크가 구성한 방식으로 객체를 사용할 수 있게 한다. (이때, Spring Context에 추가되는 인스턴스를 Bean이라고 한다.)

 

2장은 이 한 문장으로부터 모든 것이 시작됐다. 우선 저 문장에서 눈에 띄는 단어는 'Spring Context'이다. 우선, Spring Context란 무엇일까?

 

Spring Context

Overview of Spring Framework (출처: https://docs.spring.io/spring-framework/docs/4.3.12.RELEASE/spring-framework-reference/html/overview.html)

 

Spring Context란, 'Spring Framework가 관리할 모든 인스턴스를 추가하는 Application의 메모리 공간'이다. Spring 공식 문서의 다이어그램에서 표현한 것처럼, Spring의 핵심 기술 중 하나이다.

 

2장과 3장을 읽고 Spring Context를 직접 그림으로 표현해 보기로 했는데, 이는 아래와 같다.

 

여기서 Spring Context는 뭘까?

 

우선 노란색 영역은 Spring 영역이고, 파란색 영역은 Application 영역이다. 각 동그라미와 세모는 객체 인스턴스들을 의미한다. 그렇다면 초록색 영역은 뭘까? 이 영역이 바로 Spring Context이다.

 

Spring(그림의 Spring 아이콘)은 Application에서 사용자가 정의한 어떠한 객체도 '직접적으로' 접근할 수는 없다. 그렇지만 일단 Spring을 이용하기 위해서 필요한 인스턴스는 모두 이용해야하기 때문에 '어딘가에는' 저 객체들을 옮겨야하는데, 이 공간이 Spring Context다.

 

따라서, Spring은 사용자가 정의한 인스턴스(Bean)를 Spring Context에 등록함으로써 제어할 수 있다. 다시 말해, 등록되지 않은 Bean은 Spring이 제어할 수 없다.

 

Bean을 추가하는 방법

 

크게 3가지 방법이 있다. 이 중 앞의 2가지는 프로젝트를 하면서 써봤었으나, 마지막 방법은 다소 생소하고 제대로 이해가 되지 않아 2가지 방법만 정리하기로 했다.

 

우선 Bean을 Spring이 제어할 수 있게 하려면 Bean을 등록하는 공간을 만드는 것이 먼저니까, Spring Context부터 호출해 보자. Spring Context 인스턴스를 만들기 위해서는 (Gradle 프로젝트 기준으로) 아래 의존성을 추가해야 한다.

implementation group: 'org.springframework', name: 'spring-context', version: '6.2.2'

 

무사히 의존성을 추가했으면, 코드에 다음과 같이 작성하면 Spring Context 인스턴스를 생성할 수 있다.

var context = new AnnotationConfigApplicationContext();

 

이제 Bean을 등록할 준비가 끝났다.

 

@Bean 사용

 

가장 직관적인 방법이다. Bean으로 쓸 클래스에 @Bean Annotation을 사용하는 것이다. 이는 아래와 같은 단계를 거친다.

 

1단계: @Configuration(구성) 클래스를 만든다.

2단계: 그 클래스의 내부에 Spring Context에 추가하려는 인스턴스를 반환하는 메서드를 정의하고, 그 메서드에 @Bean을 추가한다. (이때, @Bean의 name이나 value 속성을 이용해 Bean의 이름을 지정할 수 있다.)

3단계: AnnotationConfigApplicationContext()의 인자로 구성 클래스를 넣는다.

 

예를 들어, 보안과 관련된 Security라는 이름을 가지는 Bean을 등록하려고 한다면, Config 클래스에 security라는 이름을 인스턴스를 반환하는 메서드를 만든다. 그 Config의 이름을 SecurityConfig라고 한다면, Spring Context에 Security라는 Bean을 등록하기 위해서는 다음 한 줄만 입력하면 된다.

var context = new AnnotationConfigApplicationContext(SecurityConfig.class);

 

만약, 추가한 Bean 타입(이 예시에서는 Security)의 Bean 참조를 가져오고 싶다면 context.getBean()으로 가져올 수 있다.

 

그리고 이 방법을 사용하면, 동일한 Security 타입 인스턴스를 Spring Context에 여러 개 추가할 수 있다. 그러면 '어떻게 구분하지? 어떤 Bean을 기본값으로 설정하지?' 라는 문제가 생기는데, 아래처럼 Security 타입의 객체 인스턴스가 3개 만들어진다고 할 때, @Primary가 붙은 클래스로 생성되는 Bean이 기본값이 된다.

 

동그라미 => Security 타입의 Bean

 

3장을 읽고난 후, 또 다른 방법이 있다는 것을 알게 됐지만, 그 방법은 Bean을 생성한 후에 다른 시점에서 쓸 수 있기 때문에 이 포스트에서는 정리하지 않았다.

 

Stereotype Annotation 사용

 

@Component, @Controller, @Service, @Repository 등을 Sterotype Annotation이라고 하는데, Spring Container가 Spring 관리 Component로 식별하게 해주는 식별자이다. 난 이 방법이 더 친숙했다.

 

우선 앞의 예시를 그대로 가져오면, Security라는 클래스를 최초로 정의하는 곳@Component를 붙여 Spring에게 'Security 타입의 인스턴스를 Spring Context에 추가할게~~~'라고 알려주고, 구성 클래스@ComponentScan을 붙여 Security라는 클래스를 어디에서 찾을 수 있는지를 Spring에 알려준다.

 

그럼 어느 방법이 더 좋아요?

 

두 번째 방법은 Framework에 인스턴스를 생성한 후에만, 인스턴스를 제어할 수 있다. 따라서, 앞 방법은 @Bean이 붙은 메서드 안에 자유롭게 추가 설정이 가능하지만, 두 번째 방법은 그럴 수 없다.

 

또, 두 번째 방법으로 생성되고 등록된 인스턴스는 앞처럼 같은 타입의 인스턴스를 여러 개를 등록하지 않고, Singleton 패턴으로 관리할 수 있다.

 

그래서 두 번째 방법을 사용하는 것이 더 편리해 보일 수 있지만, 앞 방법은 인스턴스가 정의된 클래스가 Application 외부에서 정의된 경우에도 자유롭게 사용할 수 있기 때문에 외부 라이브러리를 사용할 때 필요한 방법이므로 2가지 방법 모두 알아야 한다! 가 결론이다.

 

후기

(Jelly) Bean

 

이 포스트의 부제인 '생성과 추가'는 좀 더 정확하게 표현하면 'Bean의 생성과, 생성한 Bean을 Spring Context에 추가'로 바꿀 수 있을 것 같다. 2장에서 공부했던 내용 중에 가장 유익했던 내용은 Spring Context의 역할과 정의였다. 앞으로는 이런 용어들을 혼동하지 않고 제대로 이해해서 사용할 수 있을 것 같다.