들어가며: "Container를 이해하면 Bean, DI, AOP, Transaction이 모두 하나의 그림으로 연결된다."
대부분의 Spring 개발자가 `@Component`, `@Bean`, `@Autowired`를 일상적으로 사용하지만, 정작 Spring Container가 무엇인지 명확하게 설명하는 데는 어려움을 겪는다. 이 장은 단순히 ApplicationContext라는 인터페이스를 소개하는 데 그치지 않고, Spring이라는 프레임워크의 심장(Heart)이자 핵심 운영 체제를 파헤치는 장이다.
4.1 Framework와 Library의 차이
Spring Container를 제대로 이해하려면, 먼저 Framework와 Library의 결정적인 차이부터 짚고 넘어가야 한다.
두 개념 모두 재사용 가능한 코드를 제공하지만, '프로그램의 실행 흐름을 누가 주도하는가'에서 완전히 갈라진다.
라이브러리 (Library): 내가 주도하는 코드
라이브러리는 개발자가 필요할 때 직접 호출하는 도구 모음이다.
// Java 컬렉션 프레임워크 예시
List<String> names = new ArrayList<>();
names.add("Spring");
names.add("Container");
ArrayList는 다양한 편의 기능을 제공하지만, 객체를 언제 생성하고 어느 시점에 메서드를 호출할지는 오롯이 개발자(애플리케이션)가 결정합니다. 주도권이 개발자에게 있다.
프레임워크 (Framework): 프레임워크가 주도하는 코드
반면 프레임워크는 애플리케이션의 거대한 실행 흐름 자체를 거머쥐고 있다. 개발자는 프레임워크가 정의한 틀 안에서 필요한 로직만 채워 넣으며, 때가 되면 프레임워크가 알아서 우리의 코드를 호출한다. 이 차이를 가장 잘 나타내는 유명한 문장이 있다.
할리우드 원칙 (Hollywood Principle)
"Don't call us, we'll call you." (우리를 호출하지 마세요. 우리가 당신을 호출하겠습니다.)

Spring은 전형적인 Framework입니다. 애플리케이션이 구동되면 Spring이 먼저 실행되어 필요한 객체를 스스로 만들고, 의존성을 연결한 뒤, 적절한 시점에 우리가 작성한 코드를 실행한다. 주도권이 애플리케이션에서 프레임워크로 완전히 넘어간 것이다.
4.2 Container란 무엇인가?
그렇다면 Spring은 왜 스스로를 Container(컨테이너)라고 부를까?
일반적으로 컨테이너는 물건을 담아두는 '상자'를 떠올리게 하지만, 소프트웨어 공학에서 컨테이너는 훨씬 역동적인 의미를 가진다.
💡소프트웨어에서의 Container란?
특정 대상의 생성, 의존성 연결, 생명주기(Lifecycle) 관리를 책임지며, 필요에 따라 이를 실행하고 제공하는 런타임 환경(Runtime Environment)
| Container 종류 | 주요 관리 대상 | 역할과 책임 |
| Servlet Container (ex. Tomcat) | Servlet 객체 | 서블릿 생명주기 관리, HTTP 요청/응답 매핑 |
| Docker Container | 애플리케이션 프로세스 | 격리된 환경에서 애플리케이션 실행 및 자원 관리 |
| Spring Container | Bean (Spring 객체) | Bean 생성, 의존성 주입(DI), 생명주기 및 프록시 관리 |
Spring Container는 단순한 '객체 보관함'이 아니다. 애플리케이션이 켜지는 순간부터 꺼질 때까지 객체의 모든 일생을 관장하는 운영자이다.
4.3 Bean이란 무엇인가?
Spring Container가 관리하는 대상을 Bean(빈)이라고 부른다.
많은 입문자가 Bean = Spring 객체라는 단순한 공식으로 외우지만, 이는 Bean의 본질을 절반만 설명한 것이다.
💡Bean의 정확한 정의
Bean은 Spring Container가 생성하고, 의존성을 주입하며, 생명주기를 관리하는 객체(Managed Object)이다.
모든 Bean은 Java 객체(Object)이지만, 모든 Java 객체가 Bean인 것은 아니다.

💡 오해 바로잡기: Bean은 '특별한 클래스'가 아니다
Bean이라는 새로운 Java Class Type이 별도로 존재하는 것이 아닙니다.
@Service
public class MemberService {
// 평범한 Java 클래스 (POJO)
}
MemberService는 상속이나 특수한 인터페이스 구현이 없는 지극히 평범한 Java 클래스이다.
- new MemberService()로 개발자가 직접 생성하면 일반 객체
- Spring Container가 스캔하여 생성 및 관리하면 Bean
즉, Bean은 객체의 종류(Type)를 뜻하는 말이 아니라, Spring Container의 관리하에 있는가? 라는 객체의 상태(Status)를 의미한다.
4.4 Spring Container의 5가지 핵심 책임
Spring Container의 책임은 객체를 만드는 수준에 그치지 않는다.
전체 시스템의 실행을 조율하기 위해 다음과 같은 거대한 책임을 수행한다.

- Bean 생성 (Instantiation): 설정 정보나 어노테이션을 읽고 객체를 메모리에 올린다.
- 의존성 연결 (Dependency Injection): 생성된 Bean들 간의 관계를 파악하여 `@Autowired` 등을 통해 연결한다.
- 생명주기 관리 (Lifecycle Management): 객체의 생성 직후 초기화(`@PostConstruct`) 메서드를 호출하는 등 상태 변경을 다룬다.
- 프록시 객체 생성 (Proxy Mechanism): AOP, `@Transactional` 등이 적용된 경우, 원본 객체를 감싸는 프록시(Proxy) 객체를 만들어 기능을 확장한다.
- 안전한 정리 및 소멸 (Destruction): 애플리케이션 종료 시점에 자원을 반납하고 소멸 콜백(`@PreDestroy`)을 안전하게 실행한다.
4.5 Spring은 Bean을 어떻게 등록할까?
Spring Container가 Bean을 관리한다는 점을 알았습니다. 그렇다면 Spring은 수많은 자바 클래스 중 어떤 것을 Bean으로 만들어야 하는지 어떻게 판단할까? 개발자가 "이 객체를 관리해 주세요"라고 알리는 과정을 Bean 등록(Bean Registration)이라고 부르며, 대표적으로 2가지 경로가 있다.
1. 컴포넌트 스캔 (Component Scan): 자동 등록
가장 보편적이고 생산성이 높은 방식이다. 클래스 상단에 어노테이션을 부착한다.
@Service
public class MemberService {
// 비즈니스 로직
}
애플리케이션 구동 시 Spring은 지정된 패키지 하위를 탐색(Scan)한다. 이때 `@Component`를 비롯해 이를 내포한 `@Service`, `@Repository`, `@Controller`, `@RestController` 어노테이션이 붙은 클래스를 발견하면 자동으로 Bean 등록 대상에 올린다.
⚠️ 핵심 오해 포인트
어노테이션 자체가 스스로 객체를 만드는 것이 아니다! 어노테이션은 단순히 "이 클래스를 Bean으로 등록해 주세요"라는 표시(Marker)일 뿐이며, 실제 클래스를 읽어 객체로 만드는 주체는 Spring Container이다.
2. 자바 설정 (Java Configuration): 수동 등록
설정 클래스를 작성하여 명시적으로 객체를 반환하는 방식이다.
@Configuration
public class AppConfig {
@Bean
public PaymentService paymentService() {
return new KakaoPaymentService();
}
}
클래스 패스를 스캔하는 대신, 개발자가 코드로 직접 객체를 생성하여 반환하면 Spring이 그 결과물을 가져가 Container에 등록한다.
오해 바로잡기: @Component vs @Bean
입문자들은 종종 `@Component`와 `@Bean`을 기능적으로 대립시켜 이해하곤 합니다. 하지만 올바른 비교 구도는 다음과 같다.
- `@Component` (자동 등록): 내가 직접 작성하고 수정할 수 있는 소스 코드에 사용한다.
- `@Bean` (수동 등록): 외부 라이브러리(수정 불가능한 코드)를 Bean으로 올리거나, 객체 생성 로직이 매우 복잡할 때 사용한다.
Container 입장에서는 두 방식 모두 관리 대상이 되는 동일한 Bean일 뿐이다.
4.6 다양한 등록 정보를 하나로 처리하는 비결
여기서 한 단계 더 깊은 질문이 생기기 마련이다. "어떤 개발자는 `@Component`를 쓰고, 어떤 개발자는 `@Bean`을 쓰며, 레거시 시스템은 XML을 사용한다. 심지어 미래에는 새로운 설정 방식이 등장할 수도 있다. Spring은 어떻게 이 제각각인 등록 정보를 단 하나의 Container 메커니즘으로 처리할까?"

비결은 추상화에 있습니다. Spring Container는 자바 코드나 어노테이션, XML 형식을 직접 읽지 않는다. 대신 설정 정보를 통합 읽기 도구(BeanDefinitionReader)를 통해 BeanDefinition이라는 하나의 공통 메타데이터 규격으로 변환하여 다룬다.
4.7 IoC의 진짜 원동력: BeanDefinition (맛보기)
많은 개발자가 'Bean'이 Spring의 핵심이라고 언급된다.하지만 프레임워크 내부 메커니즘 관점에서의 진짜 핵심은 BeanDefinition이다. Spring은 객체(Bean)를 메모리에 올리기 전에, 객체를 어떻게 만들지에 대한 '설계도(Metadata)'를 먼저 수집하고 관리하기 때문이다.

Spring Container는 수집된 BeanDefinition 설계도들을 바탕으로 정해진 순서에 따라 안정적으로 Bean을 찍어낸다.
4.8 BeanFactory: 가장 순수한 형태의 IoC 공장
이 BeanDefinition 설계도를 바탕으로 실제 Bean을 만들어내는 최상위 엔진이 바로 BeanFactory입니다. BeanFactory는 Spring의 가장 뿌리가 되는 최상위 Container 인터페이스로, 단 하나의 명확한 역할을 수행한다.
- BeanDefinition 메타데이터를 읽는다.
- 요청 시점에 객체를 인스턴스화하고 의존성을 주입(DI)한다.
- 완성된 Bean을 반환한다.
이름 그대로 'Bean을 찍어내는 순수한 공장(Factory)' 역할에 집중된 기본 실행체이다.
4.9 ApplicationContext: 거대한 엔터프라이즈 실행 환경
그러나 실제 Spring 프로젝트에서는 BeanFactory를 직접 사용하는 일이 거의 없다. 대신 이를 확장한 ApplicationContext를 사용한다. 실제 실무 애플리케이션을 운영할 때는 단순히 객체를 생성하고 주입하는 것 외에도 수많은 부가 기능이 필수적이기 때문이다.

BeanFactory vs ApplicationContext
- BeanFactory: 객체를 생성하고 의존성을 연결하는 '기본 엔진' (Lazy Loading 지원)
- ApplicationContext: 기본 엔진 위에 국제화, 이벤트, 프로파일, AOP, 프록시 생성 등 엔터프라이즈 부가 기능을 완비한 '완성형 런타임 환경' (Pre-Loading 지원)
정리하자면, 우리가 흔히 말하는 "Spring Container"는 단순한 BeanFactory가 아니라, 그 모든 확장 기능을 품고 있는 ApplicationContext를 의미한다.
Chapter 4 핵심 요약
- 주도권의 역전 (IoC): 프레임워크는 프로그램의 실행 흐름을 주도하며, Spring에서는 그 주체가 Spring Container다.
- Container와 Bean의 본질: Container는 단순 저장소가 아니라 객체의 생명주기를 관장하는 런타임 실행 환경이며, Bean은 그 안에서 관리받는 객체의 상태(Status)를 뜻한다.
- 등록 방식의 추상화 (BeanDefinition): 자동 등록(`@Component`)과 수동 등록(`@Bean`) 모두 BeanDefinitionReader를 거쳐 BeanDefinition이라는 단일 설계도로 통합된다.
- 컨테이너의 이원화 (BeanFactory vs ApplicationContext): BeanFactory가 순수한 객체 생성 및 DI 엔진이라면, ApplicationContext는 AOP, 이벤트, 국제화 등을 지원하는 실무형 통합 컨테이너다.
'🍃SpringBoot' 카테고리의 다른 글
| Spring IoC & DI 완전 정복: Chapter 6. Spring은 의존성을 어떻게 해결할까? (0) | 2026.07.22 |
|---|---|
| Spring IoC & DI 완전 정복: Chapter 5. BeanDefinition과 Bean 생성 과정 (0) | 2026.07.22 |
| Spring IoC & DI 완전 정복: Chapter 3: "제어를 프레임워크에게 넘긴다는 것은 무슨 의미일까?" (0) | 2026.07.22 |
| Spring IoC & DI 완전 정복: Chapter 2: "객체는 왜 스스로 필요한 객체를 만들면 안 될까?" (1) | 2026.07.22 |
| Spring IoC & DI 완전 정복: Chapter 1: "객체지향은 왜 등장했을까?" (0) | 2026.07.22 |
