▼ Why ?
MVC 패턴을 공부하면서 MVC 구조에서의 ' Model ' 역할을 하는 것이 Bean이라는 것을 알게 되었다. 그리고 이번에 진행하고 있는 "Donggram" 웹 개발 프로젝트에서 팀원이 작성한 코드에서 ' @Bean ' 애너테이션을 보게 되었고 이게 무슨 기능을 하는지 궁금하기도 하고 Bean에 대해 알아둬야 될 것 같아 이번 기회에 Bean에 대해 공부하게 되었다.
( 2023-11-04 )
GDSC - Web 커리큘럼 5주차에 배운 "Spring Security" 를 공부하면서 '@Configuration' 이라는 애너테이션에 대해 알게 되었다. 이 @Configuration 애너테이션은 스프링 시큐리티의 세부 설정을 위해 생성한 SecurityConfig 클래스가 스프링의 환경 설정 파일임을 알려줌과 동시에 빈(Bean)을 수동으로 등록하기 위한 애너테이션이다. 그런데 이를 이해하는 과정에서 빈에 대한 개념이 잘못 잡혀있는 것 같아 않아 빈(Bean)을 생성하고 다루기 위해 필요한 개념(Proxy Pattern, CGLIB, Singleton, AOP)들을 추가적으로 공부해봤고, "의존성 주입(DI)" 개념을 제대로 잡고 '@Autowired' 라는 애너테이션도 다시 공부해봤다.
▼ Bean
Bean ?
- Spring에서는 직접 ' new ' 를 이용하여 생성한 객체가 아니라, Spring에 의하여 관리당하는 Java 객체를 사용한다 !
➜ 이 Java 객체가 "Bean" 이며, Singleton으로 관리된다 !
( Sinleton Pattern : 하나의 class에 오직 하나의 객체만이 생성된다, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 Return )
➜ 즉, Spring이 개발자 대신 객체를 제어하기 위해선 해당 객체가 Bean으로 등록되어 있어야 한다 !

- Spring Container에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트
➜ Spring Container가 관리하는 Java 객체
➜ 해당 Container에 공급하는 "설정 메타 데이터 (XML)"에 의해 생성된다

➜ Spring Container는 BeanDefinition만 알면 Java 코드인지 XML인지 알 필요 없이 Bean 정보 생성 가능
( BeanDefinition : 빈 설정 메타 정보)
- 인스턴스화(Instantiate)된 객체를 의미
➜ Spring Container에 등록된 객체를 " Spring Bean " 이라고 한다 - 애너테이션(Annotation)을 통해 객체를 Spring Container에 간단하게 등록 가능 !
( 과거에는 XML로 지정해주는 등의 과정이 필요했다 )
▼ Bean 자동 등록 vs 수동 등록 / 사용 방법
Bean 등록 - @Component vs @Bean
- @Component 이용
- 'jwt' package의 JwtTokenProvider' class
- class 레벨에서 선언함으로써 Spring이 Runtime시에 Component 스캔을 하여 자동으로 Bean을 찾고(detect) 등록하는 애너테이션
( Component 스캔 기본 대상 : @Component, @Controller, @Service, @Repository, @Configuration )
- class 레벨에서 선언함으로써 Spring이 Runtime시에 Component 스캔을 하여 자동으로 Bean을 찾고(detect) 등록하는 애너테이션
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {
...
}
- @Bean 이용 (꼭 @Configuration과 함께 사용)
[ config 패키지의 SecurityConfig 클래스 ]
- 메서드 레벨에서 선언하며, 반환되는 객체(instance)를 수동으로 빈(Bean)을 등록하는 애너테이션
- '@Bean' 은 '@Configuration' 을 사용해 "빈" 으로 등록된 클래스 내에서만 사용하도록 하자 !
Why?
➙ '@Configuration' 안에도 '@Component' 애너테이션이 붙어 있어 @Configuration 애너테이션이 붙은 클래스도 스프링 빈으로 등록된다.
이때 스프링은 'CGLIB'를 이용해 @Configuration 애너테이션이 붙은 클래스 내에 있는 빈(@Bean), 즉 객체들에 "프록시 패턴(Proxy Pattern)"을 적용한다 !
➙ 그렇게 되면, @Bean 애너테이션이 달린 메서드를 여러 번 호출해도 공통된 하나의 객체만 반환하기 때문에 "싱글톤(Singleton)" 을 보장할 수 있게 된다 ! - 수동으로 빈을 등록해줬지만 의도적으로 매번 다른 객체가 생성되게 하고 싶다면, 아래와 같이 @Configuration 애너테이션이 갖고 있는 'proxyBeanMethods'를 'false'로 설정해주면 된다.
- 메서드 레벨에서 선언하며, 반환되는 객체(instance)를 수동으로 빈(Bean)을 등록하는 애너테이션
@Configuration(proxyBeanMethods = false)
public class MyBeanConfiguration { ... }
...
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
...
@Configuration // 해당 class가 하나 이상의 @Bean 메서드를 제공하고
// Spring Container가 Bean Definition을 생성하고
// Runtime시 그 Bean들이 요청들을 처리할 것을 선언하는 애너테이션
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {
...
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
...
}
🔻 ' passwordEncoder() ' 메서드에서 반환한 객체가 Spring Container에 Bean(객체)으로 등록된다
프록시 패턴 (참고 자료) - [Software Design Pattern] 프록시 패턴 (Proxy Pattern) — Uykm_Note (tistory.com)
[Software Design Pattern] 프록시 패턴 (Proxy Pattern)
▼ Why ? What ? GDSC - Web 커리큘럼에서 빈(Bean)을 등록하는 과정에서 빈(Bean)을 수동으로 등록하는 것과 자동으로 등록하는 것의 차이를 잘 모르는 것 같아 다시 공부를 했다. 빈을 수동으로 등록할
ukym-tistory.tistory.com
자동 등록 vs 수동 등록
- 스프링 빈(Bean)을 하나 등록하고 싶을때 @Component 애너테이션만 달아주면 끝나기 때문에 기본적으로 "자동 빈 등록" 을 선호한다.
➙ 무엇보다 자동으로 등록해주어도 객체지향 5원칙을 지킬 수 있다는 점이 크다 !
(+) 스프링 컨테이너는 싱글톤을 보장해준다는 특징이 있기 때문에, 빈(Bean)을 자동으로 등록하는 경우엔 별도의 코드 없이도 "싱글톤"이 보장된다는 장점도 있다.
( 수동으로 빈을 등록해주려면 설정 정보 클래스(@Configuration) 내에서 @Bean 애너테이션을 달아주며, 객체를 생성하고 주입할 대상도 일일히 적어줘야 하는 번거로운 작업이 요구된다. ) - 빈을 수동으로 등록하여 사용하는 경우
(+) 개발자가 직접 제어 불가능한 라이브러리를 사용할 때
(+) 애플리케이션 전범위적으로 사용되는 클래스를 등록할 때
(+) 다형성을 활용하여 여러 구현체를 등록해줘야 할 때
➙ 여러 구현체를 등록해줄 때 '@Bean' 을 이용해 수동으로 등록해준다면 설정 정보(@Configuration을 붙인 클래스) 내에서 어떤 구현체들이 빈(Bean)으로 등록됐는지 한 눈에 확인할 수 있기 때문에 유지보수에 유리하다 !
(-) 빈으로 등록하는 클래스가 많아질수록 빈으로 등록하는 작업이 많아져 번거로움이 커지기 때문에, 앞서 말한 경우를 제외하고는 스프링의 "컴포넌트 스캔(Component Scan)" 기능을 활용하여 자동으로 빈을 등록해주는 것이 좋다 !
"업무 로직 빈" 에는 자동 빈 등록 기능을, "기술 지원 빈" 엔 수동 빈 등록 기능을 사용 !
- 업무 로직 빈은 흔히 알고 있는 Controller(웹 지원), Service(핵심 비즈니스 로직), Repository(데이터 계층 로직)에 주로 사용된다.
➙ 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉬운 경우 ! - 기술 지원 빈은 기술적인 문제나 AOP(Aspect-Oriented Programming)을 처리할 때 주로 사용된다. 쉽게 말해서, 데이터베이스 연결, 공통 로그 처리와 같은 로직을 지원하기 위한 기술이다.
➙ 애플리케이션에 광범위하게 영향을 미치는 경우이기 때문에, 유지 보수에 유리할 수 있도록 수동으로 빈을 등록하여 설정 정보에 명확하게 나타나게 하자 ! - AOP(Aspect-Oriented Programming) ?
➙ 트랜잭션(Transaction) 관리처럼 OOP(Object-Oriented Programming)에서 독립적으로 관리하기 힘든 부가 기능을 모듈화시킨 것을 "Aspect" 라 하며, 이러한 Aspect를 핵심 비즈니스 로직과 분리하는 등 OOP를 보완해주는 작업을 수행하는 것을 "AOP" 라고 한다 !
등록한 Bean 사용 (Donggram 프로젝트 소스 코드 참고)
- member 클래스에 정의되어 있는 endcodePassword 메서드 (참고)
public void encodePassword(PasswordEncoder passwordEncoder){
this.password = passwordEncoder.encode(password);
}
- Service 패키지에 있는 MemberService 클래스
➙ PasswordEncoder 클래스 타입의 의존 객체 passwordEncoder를 생성
@Service
@RequiredArgsConstructor
public class MemberService {
...
private final PasswordEncoder passwordEncoder;
...
public ResponseDto join(SignUpDto signUpDto) throws Exception{
...
Member member = Member.builder()
...
member.encodePassword(passwordEncoder); // PasswordEncoder class 타입의 Bean 호출
...
}
...
}
🔻 ' @RequiredArgsConstructor ' 이 선언되었기 때문에 ' @Autowired ' 와 생성자 없이 등록된 Bean을 사용 가능
➙ 자동으로 MemberService 클래스가 의존(강하게 결합 X)하는 'PasswordEncoder' 클래스 타입의 빈(Bean), 즉 'passwordEncoder' 의존 객체를 찾아 주입해준다.
➙ 즉, "스프링 컨테이너" 에 등록된 빈(Bean)들 중에 PasswordEncoder 클래스 타입에 해당하는 Bean을 찾아 사용하는 것이다 !
@Autowired (참고 자료) - [Spring] DI & @Autowired — Uykm_Note (tistory.com)
[Spring] DI & @Autowired
▼ Why ? What ? ▼ DI (Dependency Injection) 의존성 종속; class 간의 의존관계를 Spring Container가 자동으로 연결해주는 것 ➜ class A가 class B, C와 상호작용한다면, 객체 A는 객체 B, C와 의존관계이다 의존관
ukym-tistory.tistory.com
▼ CGLIB
CGLIB ?
- 스프링 컨테이너에 빈(Bean)을 등록하면 기본적으로 싱글톤 패턴으로 빈을 관리하게 된다.
( 등록된 빈을 요청하게 되면 항상 동일한 빈을 반환해준다. )
➙ 이때 싱글톤 패턴을 보장해줄 수 있게 해주는 바이트 코드 조작 라이브러리이다.
CGLIB이 싱글톤(Singleton)을 보장해주는 내부 원리
스프링 프레임워크는 @Configuration 애너테이션이 달린 클래스에 속한 빈(@Bean)들을 스프링 컨테이너에 등록할 때 해당 클래스의 빈들에게 싱글톤 패턴을 적용하기 위해 아래처럼 임의의 다른 클래스를 만들어서 해당 빈들을 스프링 빈으로 등록한다.

// CGLIB 내부 코드
@Bean
public MyBean myBean() {
if (myBean이 이미 스프링 컨테이너에 등록되어 있으면?) {
return "스프링 컨테이너에서 찾아서 반환";
} else { // 스프링 컨테이너에 없으면
// 기존 로직을 호출해서 MyBean를 생성하고 스프링 컨테이너에 등록
return "반환";
}
}
▼ Why ?
MVC 패턴을 공부하면서 MVC 구조에서의 ' Model ' 역할을 하는 것이 Bean이라는 것을 알게 되었다. 그리고 이번에 진행하고 있는 "Donggram" 웹 개발 프로젝트에서 팀원이 작성한 코드에서 ' @Bean ' 애너테이션을 보게 되었고 이게 무슨 기능을 하는지 궁금하기도 하고 Bean에 대해 알아둬야 될 것 같아 이번 기회에 Bean에 대해 공부하게 되었다.
( 2023-11-04 )
GDSC - Web 커리큘럼 5주차에 배운 "Spring Security" 를 공부하면서 '@Configuration' 이라는 애너테이션에 대해 알게 되었다. 이 @Configuration 애너테이션은 스프링 시큐리티의 세부 설정을 위해 생성한 SecurityConfig 클래스가 스프링의 환경 설정 파일임을 알려줌과 동시에 빈(Bean)을 수동으로 등록하기 위한 애너테이션이다. 그런데 이를 이해하는 과정에서 빈에 대한 개념이 잘못 잡혀있는 것 같아 않아 빈(Bean)을 생성하고 다루기 위해 필요한 개념(Proxy Pattern, CGLIB, Singleton, AOP)들을 추가적으로 공부해봤고, "의존성 주입(DI)" 개념을 제대로 잡고 '@Autowired' 라는 애너테이션도 다시 공부해봤다.
▼ Bean
Bean ?
- Spring에서는 직접 ' new ' 를 이용하여 생성한 객체가 아니라, Spring에 의하여 관리당하는 Java 객체를 사용한다 !
➜ 이 Java 객체가 "Bean" 이며, Singleton으로 관리된다 !
( Sinleton Pattern : 하나의 class에 오직 하나의 객체만이 생성된다, 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 Return )
➜ 즉, Spring이 개발자 대신 객체를 제어하기 위해선 해당 객체가 Bean으로 등록되어 있어야 한다 !

- Spring Container에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트
➜ Spring Container가 관리하는 Java 객체
➜ 해당 Container에 공급하는 "설정 메타 데이터 (XML)"에 의해 생성된다

➜ Spring Container는 BeanDefinition만 알면 Java 코드인지 XML인지 알 필요 없이 Bean 정보 생성 가능
( BeanDefinition : 빈 설정 메타 정보)
- 인스턴스화(Instantiate)된 객체를 의미
➜ Spring Container에 등록된 객체를 " Spring Bean " 이라고 한다 - 애너테이션(Annotation)을 통해 객체를 Spring Container에 간단하게 등록 가능 !
( 과거에는 XML로 지정해주는 등의 과정이 필요했다 )
▼ Bean 자동 등록 vs 수동 등록 / 사용 방법
Bean 등록 - @Component vs @Bean
- @Component 이용
- 'jwt' package의 JwtTokenProvider' class
- class 레벨에서 선언함으로써 Spring이 Runtime시에 Component 스캔을 하여 자동으로 Bean을 찾고(detect) 등록하는 애너테이션
( Component 스캔 기본 대상 : @Component, @Controller, @Service, @Repository, @Configuration )
- class 레벨에서 선언함으로써 Spring이 Runtime시에 Component 스캔을 하여 자동으로 Bean을 찾고(detect) 등록하는 애너테이션
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {
...
}
- @Bean 이용 (꼭 @Configuration과 함께 사용)
[ config 패키지의 SecurityConfig 클래스 ]
- 메서드 레벨에서 선언하며, 반환되는 객체(instance)를 수동으로 빈(Bean)을 등록하는 애너테이션
- '@Bean' 은 '@Configuration' 을 사용해 "빈" 으로 등록된 클래스 내에서만 사용하도록 하자 !
Why?
➙ '@Configuration' 안에도 '@Component' 애너테이션이 붙어 있어 @Configuration 애너테이션이 붙은 클래스도 스프링 빈으로 등록된다.
이때 스프링은 'CGLIB'를 이용해 @Configuration 애너테이션이 붙은 클래스 내에 있는 빈(@Bean), 즉 객체들에 "프록시 패턴(Proxy Pattern)"을 적용한다 !
➙ 그렇게 되면, @Bean 애너테이션이 달린 메서드를 여러 번 호출해도 공통된 하나의 객체만 반환하기 때문에 "싱글톤(Singleton)" 을 보장할 수 있게 된다 ! - 수동으로 빈을 등록해줬지만 의도적으로 매번 다른 객체가 생성되게 하고 싶다면, 아래와 같이 @Configuration 애너테이션이 갖고 있는 'proxyBeanMethods'를 'false'로 설정해주면 된다.
- 메서드 레벨에서 선언하며, 반환되는 객체(instance)를 수동으로 빈(Bean)을 등록하는 애너테이션
@Configuration(proxyBeanMethods = false)
public class MyBeanConfiguration { ... }
...
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
...
@Configuration // 해당 class가 하나 이상의 @Bean 메서드를 제공하고
// Spring Container가 Bean Definition을 생성하고
// Runtime시 그 Bean들이 요청들을 처리할 것을 선언하는 애너테이션
@RequiredArgsConstructor
public class SecurityConfig implements WebMvcConfigurer {
...
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
...
}
🔻 ' passwordEncoder() ' 메서드에서 반환한 객체가 Spring Container에 Bean(객체)으로 등록된다
프록시 패턴 (참고 자료) - [Software Design Pattern] 프록시 패턴 (Proxy Pattern) — Uykm_Note (tistory.com)
[Software Design Pattern] 프록시 패턴 (Proxy Pattern)
▼ Why ? What ? GDSC - Web 커리큘럼에서 빈(Bean)을 등록하는 과정에서 빈(Bean)을 수동으로 등록하는 것과 자동으로 등록하는 것의 차이를 잘 모르는 것 같아 다시 공부를 했다. 빈을 수동으로 등록할
ukym-tistory.tistory.com
자동 등록 vs 수동 등록
- 스프링 빈(Bean)을 하나 등록하고 싶을때 @Component 애너테이션만 달아주면 끝나기 때문에 기본적으로 "자동 빈 등록" 을 선호한다.
➙ 무엇보다 자동으로 등록해주어도 객체지향 5원칙을 지킬 수 있다는 점이 크다 !
(+) 스프링 컨테이너는 싱글톤을 보장해준다는 특징이 있기 때문에, 빈(Bean)을 자동으로 등록하는 경우엔 별도의 코드 없이도 "싱글톤"이 보장된다는 장점도 있다.
( 수동으로 빈을 등록해주려면 설정 정보 클래스(@Configuration) 내에서 @Bean 애너테이션을 달아주며, 객체를 생성하고 주입할 대상도 일일히 적어줘야 하는 번거로운 작업이 요구된다. ) - 빈을 수동으로 등록하여 사용하는 경우
(+) 개발자가 직접 제어 불가능한 라이브러리를 사용할 때
(+) 애플리케이션 전범위적으로 사용되는 클래스를 등록할 때
(+) 다형성을 활용하여 여러 구현체를 등록해줘야 할 때
➙ 여러 구현체를 등록해줄 때 '@Bean' 을 이용해 수동으로 등록해준다면 설정 정보(@Configuration을 붙인 클래스) 내에서 어떤 구현체들이 빈(Bean)으로 등록됐는지 한 눈에 확인할 수 있기 때문에 유지보수에 유리하다 !
(-) 빈으로 등록하는 클래스가 많아질수록 빈으로 등록하는 작업이 많아져 번거로움이 커지기 때문에, 앞서 말한 경우를 제외하고는 스프링의 "컴포넌트 스캔(Component Scan)" 기능을 활용하여 자동으로 빈을 등록해주는 것이 좋다 !
"업무 로직 빈" 에는 자동 빈 등록 기능을, "기술 지원 빈" 엔 수동 빈 등록 기능을 사용 !
- 업무 로직 빈은 흔히 알고 있는 Controller(웹 지원), Service(핵심 비즈니스 로직), Repository(데이터 계층 로직)에 주로 사용된다.
➙ 어떤 곳에서 문제가 발생했는지 명확하게 파악하기 쉬운 경우 ! - 기술 지원 빈은 기술적인 문제나 AOP(Aspect-Oriented Programming)을 처리할 때 주로 사용된다. 쉽게 말해서, 데이터베이스 연결, 공통 로그 처리와 같은 로직을 지원하기 위한 기술이다.
➙ 애플리케이션에 광범위하게 영향을 미치는 경우이기 때문에, 유지 보수에 유리할 수 있도록 수동으로 빈을 등록하여 설정 정보에 명확하게 나타나게 하자 ! - AOP(Aspect-Oriented Programming) ?
➙ 트랜잭션(Transaction) 관리처럼 OOP(Object-Oriented Programming)에서 독립적으로 관리하기 힘든 부가 기능을 모듈화시킨 것을 "Aspect" 라 하며, 이러한 Aspect를 핵심 비즈니스 로직과 분리하는 등 OOP를 보완해주는 작업을 수행하는 것을 "AOP" 라고 한다 !
등록한 Bean 사용 (Donggram 프로젝트 소스 코드 참고)
- member 클래스에 정의되어 있는 endcodePassword 메서드 (참고)
public void encodePassword(PasswordEncoder passwordEncoder){
this.password = passwordEncoder.encode(password);
}
- Service 패키지에 있는 MemberService 클래스
➙ PasswordEncoder 클래스 타입의 의존 객체 passwordEncoder를 생성
@Service
@RequiredArgsConstructor
public class MemberService {
...
private final PasswordEncoder passwordEncoder;
...
public ResponseDto join(SignUpDto signUpDto) throws Exception{
...
Member member = Member.builder()
...
member.encodePassword(passwordEncoder); // PasswordEncoder class 타입의 Bean 호출
...
}
...
}
🔻 ' @RequiredArgsConstructor ' 이 선언되었기 때문에 ' @Autowired ' 와 생성자 없이 등록된 Bean을 사용 가능
➙ 자동으로 MemberService 클래스가 의존(강하게 결합 X)하는 'PasswordEncoder' 클래스 타입의 빈(Bean), 즉 'passwordEncoder' 의존 객체를 찾아 주입해준다.
➙ 즉, "스프링 컨테이너" 에 등록된 빈(Bean)들 중에 PasswordEncoder 클래스 타입에 해당하는 Bean을 찾아 사용하는 것이다 !
@Autowired (참고 자료) - [Spring] DI & @Autowired — Uykm_Note (tistory.com)
[Spring] DI & @Autowired
▼ Why ? What ? ▼ DI (Dependency Injection) 의존성 종속; class 간의 의존관계를 Spring Container가 자동으로 연결해주는 것 ➜ class A가 class B, C와 상호작용한다면, 객체 A는 객체 B, C와 의존관계이다 의존관
ukym-tistory.tistory.com
▼ CGLIB
CGLIB ?
- 스프링 컨테이너에 빈(Bean)을 등록하면 기본적으로 싱글톤 패턴으로 빈을 관리하게 된다.
( 등록된 빈을 요청하게 되면 항상 동일한 빈을 반환해준다. )
➙ 이때 싱글톤 패턴을 보장해줄 수 있게 해주는 바이트 코드 조작 라이브러리이다.
CGLIB이 싱글톤(Singleton)을 보장해주는 내부 원리
스프링 프레임워크는 @Configuration 애너테이션이 달린 클래스에 속한 빈(@Bean)들을 스프링 컨테이너에 등록할 때 해당 클래스의 빈들에게 싱글톤 패턴을 적용하기 위해 아래처럼 임의의 다른 클래스를 만들어서 해당 빈들을 스프링 빈으로 등록한다.

// CGLIB 내부 코드
@Bean
public MyBean myBean() {
if (myBean이 이미 스프링 컨테이너에 등록되어 있으면?) {
return "스프링 컨테이너에서 찾아서 반환";
} else { // 스프링 컨테이너에 없으면
// 기존 로직을 호출해서 MyBean를 생성하고 스프링 컨테이너에 등록
return "반환";
}
}