Spring/이론

Spring Bean 생명주기 - 실무에서 알아야 할 핵심만

JuNo_12 2025. 6. 5. 10:01

초기화와 소멸 콜백 방법

Bean의 초기화와 소멸 시점에 특정 작업을 수행하는 3가지 방법을 알아보겠습니다.


1. @PostConstruct / @PreDestroy (권장)

@Service
public class FileService {
    
    private FileWriter fileWriter;
    
    @PostConstruct
    public void init() {
        try {
            fileWriter = new FileWriter("application.log");
            System.out.println("파일 연결 완료!");
        } catch (IOException e) {
            throw new RuntimeException("파일 초기화 실패", e);
        }
    }
    
    @PreDestroy
    public void cleanup() {
        try {
            if (fileWriter != null) {
                fileWriter.close();
                System.out.println("파일 연결 종료!");
            }
        } catch (IOException e) {
            System.err.println("파일 정리 실패: " + e.getMessage());
        }
    }
}

 

2. @Bean의 initMethod/destroyMethod

외부 라이브러리나 @PostConstruct를 쓸 수 없는 경우에 사용합니다.

public class DatabaseConnection {
    public void connect() {
        System.out.println("데이터베이스 연결 시작");
    }
    
    public void disconnect() {
        System.out.println("데이터베이스 연결 종료");
    }
}

@Configuration
public class AppConfig {
    @Bean(initMethod = "connect", destroyMethod = "disconnect")
    public DatabaseConnection databaseConnection() {
        return new DatabaseConnection();
    }
}

 

3. InitializingBean / DisposableBean 인터페이스

잘 사용하지 않지만 알아두면 좋습니다.

@Service
public class CacheService implements InitializingBean, DisposableBean {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        // 초기화 로직
    }
    
    @Override
    public void destroy() throws Exception {
        // 정리 로직
    }
}

 


실행 순서

여러 방법을 함께 사용할 경우의 실행 순서입니다.

초기화 순서:

  1. 생성자 → 의존성 주입 → @PostConstruct → InitializingBean → initMethod

소멸 순서:

  1. @PreDestroy → DisposableBean → destroyMethod

실무 활용 사례

캐시 워밍업

@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;
    private Map<Long, Product> productCache = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void warmUpCache() {
        List<Product> popularProducts = productRepository.findPopularProducts();
        popularProducts.forEach(product -> 
            productCache.put(product.getId(), product)
        );
        System.out.println("캐시 워밍업 완료: " + productCache.size() + "개");
    }
}

 

외부 API 연결 관리

@Service
public class PaymentService {
    
    private PaymentApiClient apiClient;
    
    @PostConstruct
    public void initializeApiClient() {
        apiClient = PaymentApiClient.builder()
            .apiKey("your-api-key")
            .timeout(Duration.ofSeconds(30))
            .build();
        
        if (!apiClient.isHealthy()) {
            throw new RuntimeException("결제 API 연결 실패");
        }
    }
    
    @PreDestroy
    public void closeApiClient() {
        if (apiClient != null) {
            apiClient.close();
        }
    }
}

 


주의사항

Prototype Bean의 함정

@Service
@Scope("prototype")
public class FileProcessor implements DisposableBean {
    
    @Override
    public void destroy() throws Exception {
        // ⚠️ Prototype Bean에서는 자동으로 호출되지 않음!
        // Spring 컨테이너가 소멸을 관리하지 않음
    }
}

 

초기화 실패 처리

@Service
public class ExternalService {
    
    @PostConstruct
    public void init() {
        try {
            connectToExternalAPI();
        } catch (Exception e) {
            throw new BeanInitializationException("외부 서비스 연결 실패", e);
        }
    }
}

실무 팁

  1. @PostConstruct/@PreDestroy 우선 사용 - 가장 직관적이고 사용하기 쉬움
  2. 외부 라이브러리는 initMethod/destroyMethod - 소스 수정 불가능한 경우
  3. Prototype Bean 주의 - 소멸 콜백이 자동으로 호출되지 않음
  4. 예외 처리 필수 - 초기화 실패 시 명확한 에러 메시지 제공

Bean 생명주기는 리소스 관리와 성능 최적화의 핵심입니다. 특히 파일, 네트워크 연결, 캐시 등을 다룰 때 반드시 활용해야 합니다.