이 파일의 학습 목적
10a — IoC 컨테이너 · Bean · 생명주기
Spring이 @Service @Repository가 붙은 클래스를 어떻게 인식하고, 왜 직접 new를 안 해도 되는지 — 이것이 IoC(제어의 역전)와 Bean이다. 10a는 Spring 컨테이너의 핵심 동작 원리를 정리한다.
이 파일에서 배울 것
- ① IoC 컨테이너 — 객체 생성/관리 주체가 누구인가
- ② Bean — Spring이 관리하는 객체의 정의
- ③ Bean 생명주기 — 생성→의존성 주입→사용→소멸
- ④ @PostConstruct / @PreDestroy
- ⑤ 싱글톤 스코프 — Bean이 하나만 존재하는 이유
프로젝트 코드 연결
- ·
@Service @Repository @Component → 자동 Bean 등록
- ·
AppConfig.java @Bean — 1차 수동 Bean 등록
- ·
@RequiredArgsConstructor → 생성자 주입 자동화
- · Bean 스코프 — 싱글톤이 기본인 이유
시리즈 순서:
09 JPA 완전 이해 →
10a ← 지금 여기
→ 10b 어노테이션 완전정복
01 — IoC 컨테이너
객체 생성·관리를 Spring이 맡는다 — 개발자가 new 하지 않아도 되는 이유
🏭
IoC — Inversion of Control (제어의 역전)
IoC
🏢 비유 — 회사 인사팀
내가 직접 직원 채용·관리·해고 = 직접 new 하고 관리
인사팀(Spring IoC 컨테이너)이 채용·배치·관리·해고 담당
→ 나는 "UserService 직원 필요해요" 라고만 하면 됨
→ Spring이 알아서 만들어서 줌 (의존성 주입)
❌ IoC 없이
public class UserController {
private UserService service =
new UserService();
}
→
✅ IoC 사용
@RequiredArgsConstructor
public class UserController {
private final UserService service;
}
💡 ApplicationContext = IoC 컨테이너 구현체
Spring Boot 시작 시 ApplicationContext 생성
→ @Component, @Service, @Repository, @Controller 붙은 클래스 스캔
→ Bean 객체 생성 및 등록 → 의존관계 주입 → 애플리케이션 실행
02 — Bean이란?
Spring IoC 컨테이너가 관리하는 객체 — 싱글턴이 기본
☕
Bean = Spring이 관리하는 객체
Bean
🏪 비유 — 편의점 재고
편의점(IoC 컨테이너) = 물건(Bean)을 미리 준비해서 보관
손님이 "콜라 주세요" → 창고에서 꺼내서 줌 (같은 콜라, 싱글턴)
→ Bean은 기본적으로 싱글턴 — 애플리케이션 전체에서 인스턴스 1개만 존재
Java — Bean 등록 방법 3가지
@Component
@Service
@Repository
@Controller
public class UserService { ... }
@Configuration
public class AppConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
@SpringBootApplication
public class BoardApplication {
public static void main(String[] args) {
SpringApplication.run(BoardApplication.class, args);
}
}
💡 싱글턴 Bean — 주의할 점
Bean은 인스턴스 1개 → 여러 요청이 동시에 같은 Bean 사용
→ Bean 내부에 상태(인스턴스 변수)를 저장하면 안 됨 → 다른 요청과 데이터 섞임
→ Service, Repository는 상태 없이 메서드만 → 안전
03 — Bean 생명주기
생성 → 의존성 주입 → 초기화 → 사용 → 소멸 — 각 단계에서 할 수 있는 것
🔄
Spring Bean 생명주기 전체 흐름
Lifecycle
1
스프링 컨테이너 생성
ApplicationContext 초기화 — Bean 정의 정보 로딩
2
Bean 객체 생성 (new)
생성자 호출 — @Component 붙은 클래스 인스턴스 생성
3
의존성 주입 (DI)
생성자 주입 / @Autowired 필드 주입 완료 — 다른 Bean 연결
4
초기화 콜백 — @PostConstruct ⭐
의존성 주입 완료 직후 호출 — DB 연결 확인, 캐시 초기화, 설정 검증 등
5
Bean 사용
애플리케이션 실행 중 — 요청 처리, 비즈니스 로직 수행
6
소멸 전 콜백 — @PreDestroy
컨테이너 종료 직전 호출 — DB 연결 닫기, 리소스 정리
7
Bean 소멸
스프링 컨테이너 종료 — 모든 Bean GC 대상
04 — @PostConstruct · @PreDestroy
Bean 초기화·소멸 시점에 코드 실행 — 가장 많이 쓰는 생명주기 훅
🔔
생명주기 콜백 어노테이션
Callback
Java — @PostConstruct / @PreDestroy 실제 사용
@Component
public class DataInitializer {
private final UserRepository userRepository;
@PostConstruct
public void init() {
if (!userRepository.existsByUsername("admin")) {
User admin = new User("admin", "ROLE_ADMIN");
userRepository.save(admin);
}
}
@PreDestroy
public void destroy() {
}
}
| 어노테이션 | 호출 시점 | 주요 사용 목적 |
| @PostConstruct | Bean 생성 + DI 완료 직후 | 초기 데이터 설정, 연결 확인, 캐시 워밍업 |
| @PreDestroy | 컨테이너 종료 직전 | DB 연결 닫기, 파일 저장, 리소스 반납 |
⚠️ 생성자에서 하면 안 되는 초기화 작업
생성자 실행 시점 = 의존성 주입 전 → 주입된 Bean이 아직 null
→ @PostConstruct = DI 완료 후 → 주입된 Bean 모두 사용 가능
→ DB 조회, Repository 사용 등은 반드시 @PostConstruct에서
📌 우리 프로젝트 @PostConstruct 활용
앱 시작 시 관리자 계정 자동 생성 여부 확인
또는 JWT 비밀키 검증, CORS 설정 확인 등에 활용 가능
05 — Bean 스코프
싱글턴이 기본 — 필요 시 프로토타입·요청 스코프 사용
📦
@Scope — Bean 인스턴스 범위
Scope
| 스코프 | 인스턴스 생성 시점 | 사용 사례 |
| singleton | 컨테이너 시작 시 1개 (기본값) | Service, Repository, Controller 대부분 |
| prototype | 요청할 때마다 새 인스턴스 | 상태를 가져야 하는 객체 |
| request | HTTP 요청당 1개 | 웹 환경 — 요청별 상태 관리 |
| session | HTTP 세션당 1개 | 웹 환경 — 세션별 상태 관리 |
Java — 스코프 설정
@Component
@Scope("singleton")
public class UserService { ... }
@Component
@Scope("prototype")
public class TempProcessor { ... }
💡 우리 프로젝트는 거의 전부 싱글턴
UserService, BoardService, UserRepository, BoardController 등
→ 모두 상태(인스턴스 변수) 없이 메서드만 → 싱글턴 안전
→ 로컬 변수(메서드 내부)는 요청마다 새로 생성 → 공유 안 됨