Spring Bean이란?
Spring Bean은 Spring IoC(Inversion of Control) 컨테이너가 관리하는 객체입니다. 일반적인 Java 객체와 달리 개발자가 직접 생성(new 키워드 사용)하는 것이 아니라, Spring 컨테이너가 생성, 초기화, 의존성 주입, 소멸까지 모든 생명주기를 담당합니다.
Bean으로 관리되는 객체는 기본적으로 싱글톤 패턴으로 동작하며, 애플리케이션 전역에서 하나의 인스턴스만 존재합니다. Spring은 이런 Bean들을 ApplicationContext나 BeanFactory 같은 컨테이너를 통해 관리하고, 필요한 곳에 자동으로 주입해줍니다.
핵심 개념
1. 의존성 주입 자동화
Spring 컨테이너는 Bean들 간의 의존성을 자동으로 분석하고 주입합니다. 개발자가 직접 객체를 생성하고 의존성을 연결할 필요가 없어지므로 코드가 간결해지고 유지보수성이 향상됩니다.
@Service
public class OrderService {
private final ProductRepository productRepository;
private final PaymentGateway paymentGateway;
// 생성자 주입 - Spring이 자동으로 의존성을 주입
public OrderService(ProductRepository productRepository,
PaymentGateway paymentGateway) {
this.productRepository = productRepository;
this.paymentGateway = paymentGateway;
}
public void processOrder(Order order) {
Product product = productRepository.findById(order.getProductId());
paymentGateway.processPayment(order.getPayment());
}
}
Spring 컨테이너는 또한 빌드 시점에 순환 의존성을 감지하여 설계 오류를 조기에 발견할 수 있습니다. 이는 런타임 에러를 사전에 방지하는 중요한 안전장치 역할을 합니다.
2. 생명주기와 스코프 관리
Spring은 Bean의 생명주기를 체계적으로 관리합니다. @PostConstruct와 @PreDestroy 어노테이션을 통해 초기화와 소멸 시점에 특정 로직을 실행할 수 있으며, 다양한 스코프를 지원하여 객체의 생존 범위를 제어할 수 있습니다.
@Component
@Scope("singleton") // 기본값, 애플리케이션 당 하나의 인스턴스
public class DatabaseConnectionManager {
private Connection connection;
@PostConstruct
public void initialize() {
// Bean 초기화 후 실행
connection = DriverManager.getConnection("jdbc:h2:mem:testdb");
System.out.println("Database connection initialized");
}
@PreDestroy
public void cleanup() {
// Bean 소멸 전 실행
if (connection != null) {
try {
connection.close();
System.out.println("Database connection closed");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Spring은 singleton 외에도 prototype, request, session 등 다양한 스코프를 제공하여 필요에 따라 적절한 생명주기를 선택할 수 있습니다.
3. AOP와 프록시 지원
Bean으로 관리되는 객체들은 Spring AOP(Aspect-Oriented Programming)의 혜택을 받을 수 있습니다. 트랜잭션 관리, 로깅, 보안, 캐싱 등의 공통 관심사를 비즈니스 로직과 분리하여 처리할 수 있습니다.
@Service
@Transactional
public class BankService {
private final AccountRepository accountRepository;
public BankService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
@Transactional(rollbackFor = Exception.class)
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// Spring이 프록시를 통해 트랜잭션을 자동 관리
Account from = accountRepository.findByAccountNumber(fromAccount);
Account to = accountRepository.findByAccountNumber(toAccount);
from.withdraw(amount);
to.deposit(amount);
accountRepository.save(from);
accountRepository.save(to);
// 메서드 완료 시 자동 커밋, 예외 시 자동 롤백
}
}
4. 테스트 지원과 Mock 객체
Bean으로 관리되는 컴포넌트는 테스트 시 쉽게 Mock 객체로 대체할 수 있습니다. Spring Test 프레임워크는 @MockBean, @SpyBean 등의 어노테이션을 제공하여 테스트 환경에서 의존성을 쉽게 모킹할 수 있습니다.
@SpringBootTest
class OrderServiceTest {
@MockBean
private ProductRepository productRepository;
@MockBean
private PaymentGateway paymentGateway;
@Autowired
private OrderService orderService; // 실제 Bean 주입
@Test
void shouldProcessOrderSuccessfully() {
// given
Product mockProduct = new Product(1L, "Test Product", BigDecimal.valueOf(100));
when(productRepository.findById(1L)).thenReturn(mockProduct);
when(paymentGateway.processPayment(any())).thenReturn(true);
Order order = new Order(1L, new Payment(BigDecimal.valueOf(100)));
// when & then
assertDoesNotThrow(() -> orderService.processOrder(order));
verify(productRepository).findById(1L);
verify(paymentGateway).processPayment(any());
}
}
정리
Spring Bean으로 객체를 관리하면 다음과 같은 핵심 이점을 얻을 수 있습니다:
| 측면 | 이점 | 핵심 기능 |
|---|---|---|
| 의존성 관리 | 자동 주입, 순환 의존성 감지 | Constructor/Field/Setter Injection |
| 메모리 효율성 | 싱글톤 패턴으로 인스턴스 공유 | Singleton Scope (기본값) |
| 생명주기 제어 | 초기화/소멸 로직 자동 실행 | @PostConstruct, @PreDestroy |
| 횡단 관심사 | 트랜잭션, 로깅, 보안 등 자동 처리 | AOP, Proxy Pattern |
| 테스트 용이성 | Mock 객체 쉬운 대체 | @MockBean, @SpyBean |
| 설정 중앙화 | 일관된 구성 관리 | @Configuration, @Bean |
Spring의 Bean 관리 체계는 단순한 객체 생성을 넘어서 엔터프라이즈 애플리케이션 개발에 필요한 다양한 기능들을 통합적으로 제공합니다. 이를 통해 개발자는 비즈니스 로직에 집중할 수 있으며, 확장 가능하고 유지보수하기 쉬운 애플리케이션을 구축할 수 있습니다.