ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot 애플리케이션 종료 시 안전하게 파일 백업 처리하기
    Tech/SpringBoot 2025. 6. 8. 21:55
    반응형

    Spring Boot 애플리케이션을 개발할 때, 서비스 종료 시 파일 백업이나 리소스 정리 같은 작업이 필요한 경우가 많습니다. 이 글에서는 애플리케이션 종료 시 안전하게 파일을 백업하는 방법을 두 가지 방식으로 소개합니다.

    • @PreDestroy를 이용한 Spring Bean 종료 훅
    • JVM 레벨에서 작동하는 Shutdown Hook

    ✅ 환경 설정 (Gradle)

    // build.gradle.kts
    plugins {
        id("org.springframework.boot") version "3.2.5"
        id("io.spring.dependency-management") version "1.1.4"
        kotlin("jvm") version "1.9.23"
        kotlin("plugin.spring") version "1.9.23"
    }
    
    dependencies {
        implementation("org.springframework.boot:spring-boot-starter")
    }
    

    ✅ 방식 1. @PreDestroy로 백업 처리하기

    import jakarta.annotation.PreDestroy
    import org.springframework.stereotype.Service
    import java.io.File
    import java.nio.file.Files
    import java.nio.file.StandardCopyOption
    
    @Service
    class FileBackupService {
    
        private val sourceFile = File("data/main.csv")
        private val backupFile = File("backup/main_backup.csv")
    
        fun doSomethingWithFile() {
            println("파일 작업 수행 중...")
        }
    
        @PreDestroy
        fun backupOnShutdown() {
            println("애플리케이션 종료 중: 파일 백업 시작")
            try {
                if (sourceFile.exists()) {
                    backupFile.parentFile.mkdirs()
                    Files.copy(sourceFile.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
                    println("파일 백업 완료: ${backupFile.absolutePath}")
                } else {
                    println("백업할 원본 파일이 없습니다.")
                }
            } catch (e: Exception) {
                println("백업 실패: ${e.message}")
            }
        }
    }
    

    ✔ 실행 확인용 Runner

    import org.springframework.boot.CommandLineRunner
    import org.springframework.stereotype.Component
    
    @Component
    class FileRunner(private val fileBackupService: FileBackupService) : CommandLineRunner {
        override fun run(vararg args: String?) {
            fileBackupService.doSomethingWithFile()
            println("애플리케이션이 곧 종료됩니다. Ctrl+C 또는 종료 버튼을 누르세요.")
        }
    }
    

    ✅ 방식 2. JVM Shutdown Hook으로 백업 처리하기

    @PreDestroy보다 더 강력하고, Spring 컨텍스트 밖에서도 동작 가능한 방식입니다.

    import org.springframework.stereotype.Component
    import java.io.File
    import java.nio.file.Files
    import java.nio.file.StandardCopyOption
    import javax.annotation.PostConstruct
    
    @Component
    class JvmShutdownFileBackup {
    
        private val sourceFile = File("data/main.csv")
        private val backupFile = File("backup/main_backup.csv")
    
        @PostConstruct
        fun registerShutdownHook() {
            Runtime.getRuntime().addShutdownHook(Thread {
                println("[ShutdownHook] JVM 종료 감지됨. 파일 백업 시작...")
    
                try {
                    if (sourceFile.exists()) {
                        backupFile.parentFile.mkdirs()
                        Files.copy(sourceFile.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
                        println("[ShutdownHook] 파일 백업 성공: ${backupFile.absolutePath}")
                    } else {
                        println("[ShutdownHook] 원본 파일 없음.")
                    }
                } catch (e: Exception) {
                    println("[ShutdownHook] 백업 중 오류 발생: ${e.message}")
                }
            })
    
            println("Shutdown Hook 등록 완료")
        }
    }
    

    ✅ 요약 비교

    방식 설명

    @PreDestroy Spring 관리 Bean이 정상 종료될 때만 동작
    Shutdown Hook JVM 종료 감지하여 Spring 컨텍스트 이전에도 동작 가능
    kill -9 (SIGKILL) 어떤 방식도 감지 불가능 (OS에서 강제 종료되므로 예외 없음)

    ✅ 마무리

    • 일반적인 Spring 앱에서는 @PreDestroy만으로 충분한 경우가 많습니다.
    • 하지만 보다 확실한 종료 처리가 필요하다면 JVM Shutdown Hook을 함께 사용하세요.
    • 두 방식을 함께 쓰면 더 안전한 종료 처리를 보장할 수 있습니다.

    백업 외에도 로그 정리, 외부 연결 해제 등 다양한 종료 처리를 이 방식에 확장해보세요!

     

    반응형
Designed by Tistory.