| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- spring boot
- Spring
- 김영한
- 계산기
- 배치
- DB
- 스프링
- 스케줄러
- Effective Java
- java
- 디자인 패턴
- 빌더 패턴
- Spring Batch
- 이펙티브 자바
- 백엔드
- 로드밸런서
- 토스
- 트러블슈팅
- 추상클래스
- 자바
- 코드카타
- lv1
- redis
- 프로그래머스
- 프록시 패턴
- Til
- 스프링 배치
- GoF 23
- 템플릿 메서드 패턴
- 성능 개선
- Today
- Total
김코딩
Spring Container와 Bean이 헷갈리는 이들에게 본문
오늘은 Spring의 핵심원리인 Spring Container와 Bean에 대해서 글을 작성해보려고 합니다.
Spring Container란?
Spring Container는 한마디로 "객체를 관리해주는 공간" 입니다.
먼저 Spring Container에 들어가기 전에 간단한 자바 예제를 확인해보겠습니다.
기존 Java 방식의 객체 생성
우리가 이전에 Java를 사용해서 클래스를 활용할 때는 new 키워드를 사용해 직접 객체를 생성해야 했습니다.
public class DiscountService {
private int fixDiscount = 1000;
public void discountPrice(int amount) {
System.out.println(amount - fixDiscount + "원 입니다.");
}
}
public class DiscountMain {
public static void main(String[] args) {
DiscountService discountService = new DiscountService();
discountService.discountPrice(10000);
}
}
이 예제에서 보듯이, DiscountService 객체를 사용하려면 직접 new DiscountService()를 호출해야만 했습니다.
객체 관리의 문제점
하지만 이처럼 개발자가 모든 객체를 직접 생성하고 연결하는 방식은,
프로젝트가 커질수록 비효율적이고 유지보수가 어려워지는 단점이 있습니다.
- 클래스 간 의존 관계가 많아질수록 생성 코드가 복잡해지고
- 어떤 클래스가 어떤 객체를 사용하는지 일일이 하드코딩해야 하며
- 테스트나 확장에도 불리합니다.
이 문제를 해결하기 위해 등장한 것이 바로 Spring Container입니다.
Spring Container의 등장
기존에는 DiscountService를 직접 생성해서 사용하는 구조였다면,
이제는 Spring Container가 객체 생성과 의존성 주입까지 모두 대신해주는 방식으로 바뀌었습니다.
@Component
public class DiscountService {
private int fixDiscount = 1000;
public void discountPrice(int amount) {
System.out.println(amount - fixDiscount + "원 입니다.");
}
}
@Component
public class OrderService {
private final DiscountService discountService;
public OrderService(DiscountService discountService) {
this.discountService = discountService;
}
public void processOrder() {
discountService.discountPrice(10000);
}
}
@SpringBootApplication
public class DiscountApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DiscountApplication.class, args);
OrderService orderService = context.getBean(OrderService.class);
orderService.processOrder();
}
}
방금 Spring으로 리팩토링한 코드를 살펴보면,
저희는 어디에서도 new DiscountService()나 new OrderService()를 호출하지 않았습니다.
그럼에도 불구하고 orderService.processOrder() 메서드는 문제없이 실행되었죠.
도대체 어떻게 이게 가능한 걸까요?
바로 Spring이 내부적으로 필요한 객체들을 생성하고, 알아서 연결해주고, 필요한 곳에 주입해주는 덕분입니다.
이런 역할을 담당하는 것이 바로 Spring Container입니다.
그리고 여기서 중요한 개념이 하나 등장합니다.
Spring Container가 관리하는 객체들, 바로 Bean입니다.
Bean이란?
Spring Container가 생성하고 관리하는 객체를 우리는Bean이라고 부릅니다.
Bean은 어떻게 등록되나요?
Spring에서는 다음과 같은 방법으로 Bean을 등록할 수 있습니다:
1. 컴포넌트 스캔을 활용한 자동 등록
@Component, @Service, @Repository, @Controller 등의 어노테이션을 클래스 위에 붙이면
Spring이 자동으로 Bean으로 등록해줍니다.
@Component <- 요게 빈 자동 등록
public class DiscountService {
private int fixDiscount = 1000;
public void discountPrice(int amount) {
System.out.println(amount - fixDiscount + "원 입니다.");
}
}
이전에 작성했던 Spring 예제는 이 자동 등록 방식을 활용한 구조였습니다.
2. 자바 설정 파일을 통한 수동 등록
@Configuration 클래스에서 @Bean 어노테이션을 붙인 메서드를 통해 직접 등록할 수도 있습니다.
이 방식은 외부 라이브러리 객체를 Spring Bean으로 등록하거나,
객체 생성 방식이나 의존 주입 방식을 명확히 제어하고 싶을 때 유용합니다.
public class DiscountService {
private int fixDiscount = 1000;
public void discountPrice(int amount) {
System.out.println(amount - fixDiscount + "원 입니다.");
}
}
public class OrderService {
private final DiscountService discountService;
public OrderService(DiscountService discountService) {
this.discountService = discountService;
}
public void processOrder() {
discountService.discountPrice(10000);
}
}
@Configuration
public class Appconfig {
@Bean
public DiscountService discountService() {
return new DiscountService();
}
@Bean
public OrderService orderService() {
return new OrderService(discountService());
}
}
@SpringBootApplication
public class DiscountApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DiscountApplication.class, args);
OrderService orderService = context.getBean(OrderService.class);
orderService.processOrder();
}
}
그냥 객체를 생성하는 게 더 쉽고 빨라 보이는데, 왜 굳이 Spring Container를 써야 하나요?
처음에는 대부분 이렇게 생각합니다.
"그냥 new 키워드로 내가 직접 객체를 만들면 빠르고 간단한데,
Spring Container를 쓰면 오히려 설정도 많고 복잡해지는 것 아닌가요?"
사실, 작은 프로젝트나 단순한 예제에서는 직접 객체를 만드는 게 더 편할 수도 있습니다.
하지만 규모가 커지고, 클래스 간의 의존 관계가 많아질수록 이 방식은 금세 한계에 부딪힙니다.
직접 객체 생성 방식의 한계
- 변경에 취약합니다.
-> 예를 들어 DiscountService의 구현체를 바꾸려면 사용하는 모든 곳의 코드를 바꿔야 합니다. - 테스트하기 어렵습니다.
-> 직접 만든 객체는 테스트 시 가짜(Mock) 객체로 대체하기 힘듭니다. - 객체 생성 순서와 의존 관계가 복잡해질수록 코드가 꼬이고 유지보수가 어려워집니다.
Spring Container를 사용하면?
- 객체를 만들고, 의존성을 주입하는 과정을 자동화해줍니다.
- 필요할 때 어떤 구현체를 넣을지 유연하게 바꿀 수 있어 확장성이 뛰어납니다.
- 테스트 시에는 설정만 바꾸면 Mock 객체로 교체할 수도 있습니다.
- 관심사의 분리(Separation of Concerns) 를 통해 애플리케이션을 더 깔끔하게 구성할 수 있습니다.
직접 객체를 생성하는 건 마치 요리를 할 때 매번 시장에 가서 재료를 사고 손질해서 직접 조리하는 것과 비슷합니다.
반면 Spring Container는 이미 필요한 재료가 준비되어 있고, 자동으로 조리까지 해주는 요리 시스템이에요.
재료가 많고 메뉴가 복잡해질수록, 이 시스템이 얼마나 편리한지 실감하게 됩니다.
정리
Spring Container가 해주는 일
- 객체를 대신 생성해준다 (new 안 써도 됨)
- 의존성을 자동으로 주입해준다 (DI)
- Bean 생명주기를 관리해준다
- 싱글톤으로 객체를 공유해준다 (기본 Scope)
- 필요 시 Mock 객체로 대체하기 쉽다 (테스트 용이)
Bean 등록 방식 요약
| 등록 방식 | 설명 | 어노테이션 |
| 자동 등록 | 클래스 위에 어노테이션만 붙이면 됨 | @Component, @Service 등 |
| 수동 등록 | 설정 클래스에서 직접 Bean으로 등록 | @Configuration` + @Bean |
'스프링' 카테고리의 다른 글
| 반복되는 로그인 체크, 필터 하나로 끝내기(Filter 사용법) (0) | 2025.05.22 |
|---|---|
| 여긴 못지나간다.(@Valid) (0) | 2025.05.19 |
| 의존성 주입(Dependency Injection)이란? (0) | 2025.05.08 |
| 프로퍼티 바인딩이 뭔데 자꾸 헷갈려? (0) | 2025.05.01 |
| 스프링이란? (0) | 2024.03.28 |