일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- dto projection
- 동적 SQL
- 테오의 스프린트
- 모의면접
- open contribution jam
- 구슬
- 후기
- 보따리
- redis
- 글또
- 부꾸러미
- bean
- 체험
- jooq
- DI
- 코드트리
- 북극곰
- 프로그래머스
- jscode
- 오블완
- 트러블슈팅
- Database
- Spring
- 티스토리챌린지
- spring context
- 눈송이
- 사이드 프로젝트
- SQL
- 글또 #다짐
- 부꾸
- Today
- Total
벤티의 개발 로그
[Spring] Spring Bean 1편 (생성과 추가) 본문
이 포스트는 글또 10기 내 독서 모임에 참여하여 읽기 시작한 '스프링 교과서'를 읽고 정리한 내용을 바탕으로 작성했습니다.
Bean?
지난 글에서, Bean에 대해 정말 잠깐 언급한 부분이 있다.
Spring Context는 특정 객체를 Spring에 전달해서 프레임워크가 구성한 방식으로 객체를 사용할 수 있게 한다. (이때, Spring Context에 추가되는 인스턴스를 Bean이라고 한다.)
2장은 이 한 문장으로부터 모든 것이 시작됐다. 우선 저 문장에서 눈에 띄는 단어는 'Spring Context'이다. 우선, Spring Context란 무엇일까?
Spring Context
Spring Context란, 'Spring Framework가 관리할 모든 인스턴스를 추가하는 Application의 메모리 공간'이다. Spring 공식 문서의 다이어그램에서 표현한 것처럼, Spring의 핵심 기술 중 하나이다.
2장과 3장을 읽고 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이 기본값이 된다.
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가지 방법 모두 알아야 한다! 가 결론이다.
후기
이 포스트의 부제인 '생성과 추가'는 좀 더 정확하게 표현하면 'Bean의 생성과, 생성한 Bean을 Spring Context에 추가'로 바꿀 수 있을 것 같다. 2장에서 공부했던 내용 중에 가장 유익했던 내용은 Spring Context의 역할과 정의였다. 앞으로는 이런 용어들을 혼동하지 않고 제대로 이해해서 사용할 수 있을 것 같다.