Study/SpringBoot

Spring에서 Bean 생명주기 개념 정리

kanado 2025. 5. 17. 14:55

스프링 컨테이너와 ApplicationContext란?

Spring에서 객체를 관리하는 핵심은 바로 스프링 컨테이너입니다. 이 컨테이너는 객체(Bean)를 생성하고, 의존성을 주입하며, 생명주기를 관리합니다.

  • ApplicationContext는 스프링 컨테이너의 구체적인 구현체이며, BeanFactory를 확장합니다.
  • BeanFactory가 최소 기능만 제공하는 반면, ApplicationContext는 다음을 포함한 다양한 기능을 제공합니다
  • 대부분의 스프링 애플리케이션에서는 ApplicationContext를 사용합니다.

 

Bean 생명주기 전체 흐름

Spring에서 Bean의 생명주기는 다음과 같은 순서로 진행됩니다.

  1. Bean 인스턴스 생성 (생성자 호출)
  2. 의존성 주입 (필드, 생성자, setter 등)
  3. BeanNameAware, BeanClassLoaderAware, ApplicationContextAware 등 콜백 method 호출
  4. @PostConstruct 또는 InitializingBean.afterPropertiesSet() 또는 init-method 호출
  5. 컨테이너 종료 시 @PreDestroy 또는 DisposableBean.destroy() 또는 destroy-method 호출

API 서버에서의 Bean 생명주기 실제 흐름

구조 예시

Client → [UserController] → [UserService] → [UserRepository]

모든 컴포넌트는 @RestController, @Service, @Repository 등으로 등록되어 있고, Spring이 자동으로 Bean으로 관리합니다.

 

1️⃣ Bean 인스턴스 생성 (UserService 기준으로 설명)

@Service
public class UserService {
    public UserService() {
        System.out.println("1. 생성자 호출");
    }
}

서버 실행 시, 스프링이 @Service로 등록된 클래스를 탐색하고 new UserService() 호출합니다.

 

2️⃣ 의존성 주입

@RequiredArgsConstructor
@Service
public class UserService {
    private final UserRepository userRepository; // 자동 주입
}
  • Spring은 UserRepository도 Bean으로 등록되어 있으므로 이를 UserService에 자동 주입합니다.
  • 이 작업은 생성자 또는 필드, 세터 방식으로 수행됩니다.

 

3️⃣ Aware 인터페이스 (필요 시)

@Component
public class MyLogger implements ApplicationContextAware {
    public void setApplicationContext(ApplicationContext applicationContext) {
        System.out.println("3. ApplicationContext 주입");
    }
}
  • 특별한 처리가 필요한 경우, 스프링 컨텍스트 등 내부 객체에 접근하기 위해 Aware 인터페이스를 사용할 수 있습니다.
  • 일반 API 개발에서는 자주 쓰이지는 않지만, 로그 추적기, 메타정보 접근 등에 사용됩니다.

 

4️⃣ 초기화 콜백

  • Bean 생성 → 의존성 주입이 완료된 직후 실행됩니다.
  • 즉, 해당 Bean이 완전한 상태가 되었을 때 Spring이 자동으로 호출해 줍니다.
  • 이 시점에서는 다음과 같은 "초기 설정 작업"을 주로 수행합니다.
목적 예시
외부 서비스 연결 DB 연결 확인, Redis 클라이언트 초기화, AWS 인증
인증 토큰 획득 외부 API 사용을 위한 OAuth 토큰 요청
초기 데이터 로딩 캐시 미리 로딩, 기본 설정 값 불러오기
상태 점검 구성 요소가 정상 동작하는지 체크
Scheduler 등록 일정 주기 작업 예약

 

내부 동작 (Spring은 어떻게 처리하나?)

  1. Spring이 Bean을 new로 생성
  2. 필드/생성자 등으로 DI 수행
  3. @PostConstruct가 붙은 메서드를 리플렉션으로 찾아서 한 번 실행

👉 실무 예시: Redis 캐시 키 미리 로딩, 외부 API 토큰 발급

@Component
@RequiredArgsConstructor
public class CacheWarmUpService {

    private final RedisService redisService;

    @PostConstruct
    public void init() {
        System.out.println("캐시 초기화 시작");
        redisService.loadAllCategoriesToCache();
    }
}

@Component
public class ExternalApiClient {

    private String accessToken;

    @PostConstruct
    public void init() {
        this.accessToken = requestTokenFromApi();
        System.out.println("API 토큰 발급 완료: " + accessToken);
    }

    private String requestTokenFromApi() {
        // HTTP 요청으로 OAuth 토큰 받기
        return "access_token_xyz";
    }
}

 

5️⃣ Bean 사용 (실제 API 요청 처리)

서버 기동이 완료되면, 사용자가 API 요청을 보낼 때 컨트롤러가 호출되고 Bean을 사용합니다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }
}

이때 userService는 이미 DI + 초기화가 완료된 상태입니다.

 

6️⃣ 소멸 콜백 (서버 종료 시점)

서버가 종료될 때 등록된 Bean들의 소멸 메서드가 순차적으로 호출됩니다.

@PreDestroy
public void destroy() {
    System.out.println("5. @PreDestroy 호출");
    // 예: 커넥션 종료, 임시 파일 삭제
}

이건 보통 서버 종료, docker stop , ./gradlew bootRun 중지할 때 트리거됩니다.

 

끝.