๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring

Spring Boot์˜ Graceful Shutdown ์–ด๋””๊นŒ์ง€ ์•Œ๊ณ ์žˆ๋‚˜์š”? (SmartLifeCycle, Phased ์™€ ํ•จ๊ป˜ timeout-per-shutdown-phase ์˜คํ•ด ๋ฐ”๋กœ์žก๊ธฐ)

์ด ๊ธ€์—์„œ๋Š” Graceful Shutdown์„ "์šฐ์•„ํ•œ ์ข…๋ฃŒ"๋กœ ์–ธ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

 

1. ์šฐ์•„ํ•œ ์ข…๋ฃŒ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ์„ค์ •

์šฐ์•„ํ•œ ์ข…๋ฃŒ ํ…Œ์ŠคํŠธ๋Š” IDE์—์„œ ์‹คํ–‰ ์‹œ ์›ํ™œํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์„ค๋ช…๋˜์–ด ์žˆ์–ด, Linux ์„œ๋ฒ„์—์„œ jar๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ข…๋ฃŒํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

Using graceful shutdown with your IDE may not work properly if it does not send a proper SIGTERM signal. See the documentation of your IDE for more details.

 

1.1 application.yml ์„ค์ •

application.yml์— ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง ๋ถ€ํŠธ 3.4๋ถ€ํ„ฐ๋Š” ์šฐ์•„ํ•œ ์ข…๋ฃŒ๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •๋˜๋ฉฐ, timeout-per-shutdown-phase๋Š” 30์ดˆ์ž…๋‹ˆ๋‹ค.

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 10s

 

1.2 ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •

๊ทธ๋ฆฌ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…(80์ดˆ)์„ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ํ•˜๊ธฐ ์œ„ํ•ด ์“ฐ๋ ˆ๋“œ ํ’€์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

@Configuration
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(1);

        taskExecutor.initialize();

        return taskExecutor;
    }
}

 

! ์ฐธ๊ณ 

์œ„์ฒ˜๋Ÿผ ํ ์‚ฌ์ด์ฆˆ๋ฅผ ์ œํ•œํ•˜์ง€ ์•Š์œผ๋ฉด, ThreadPoolTaskExecutor์˜ initializeExecutor, createQueue๋ฅผ ํ†ตํ•ด SynchronousQueue๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

SynchronousQueue ๋Š” ์ž‘์—…์ด ํ์— ์ ์žฌ๋˜์ง€ ์•Š๊ณ , ์ž‘์—…์„ ์ œ์ถœํ•œ ์“ฐ๋ ˆ๋“œ๋Š” ์ž‘์—…์„ ์†Œ๋น„ํ•  ์“ฐ๋ ˆ๋“œ๊ฐ€ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

 

2. ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ: ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ ค์ค„๊นŒ?

2.1 ์‹คํ—˜ ์ฝ”๋“œ

์ด ์ƒํ™ฉ์—์„œ ์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์“ฐ๋ ˆ๋“œ ํ’€์— 80์ดˆ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ œ์ถœํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•ด๋ด…๋‹ˆ๋‹ค. (jar๋กœ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค)

public class TestController {

    private final TaskExecutor taskExecutor;

    @GetMapping("/tasks")
    public String test() throws InterruptedException {
        log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ œ์ถœ (80์ดˆ ์†Œ์š” ์˜ˆ์ƒ)...");
        taskExecutor.execute(() -> {
            try {
                for (int i = 1; i <= 80; i++) {
                    Thread.sleep(1000);
                    log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: {}์ดˆ ๊ฒฝ๊ณผ", i);
                }
                log.info("๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ!");
            } catch (InterruptedException e) {
                log.warn("๋น„๋™๊ธฐ ์ž‘์—…์ด ๊ฐ•์ œ ์ข…๋ฃŒ(Interrupted) ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
            }
        });

        return "์ž‘์—…์ด ์ œ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒ(Ctrl+C)ํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.";
    }
}

 

2.2 ๊ฒฐ๊ณผ๋Š” ๋ฌด์—‡์ผ๊นŒ?

๊ฒฐ๊ณผ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”?

  1. ํ†ฐ์บฃ ์“ฐ๋ ˆ๋“œ ํ’€์— ์šฐ์•„ํ•œ ์ข…๋ฃŒ๊ฐ€ ์ ์šฉ๋˜๋”๋ผ๋„ ๋ชจ๋“  ๋น„๋™๊ธฐ ์ž‘์—…์ด ์ œ์ถœ๋˜์–ด `log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: {}์ดˆ ๊ฒฝ๊ณผ", i);`์˜ ๋กœ๊ทธ๊ฐ€ 80๋ฒˆ ๋‹ค ์ฐํžŒ ํ›„์— ์ข…๋ฃŒ๋œ๋‹ค.
  2. ์•„๋‹ˆ๋‹ค. ์šฐ์•„ํ•œ ์ข…๋ฃŒ๊ฐ€ ๋น„๋™๊ธฐ ์ž‘์—…์„ 10์ดˆ๋งŒ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋œ๋‹ค. `log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: {}์ดˆ ๊ฒฝ๊ณผ", i);`์˜ ๋กœ๊ทธ๊ฐ€ 1๋ฒˆ๋ณด๋‹ค ๋ฌด์กฐ๊ฑด ๋œ ์ฐํžŒ๋‹ค.

์ •๋‹ต์€ 2๋ฒˆ์ž…๋‹ˆ๋‹ค.

 

2.3 ๋กœ๊ทธ ๋ถ„์„

์ „์ฒด ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.

2026-01-23T09:50:51.460  INFO [nio-8080-exec-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ œ์ถœ (80์ดˆ ์†Œ์š” ์˜ˆ์ƒ)...
2026-01-23T09:50:52.461  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 1์ดˆ ๊ฒฝ๊ณผ
2026-01-23T09:50:57.239 DEBUG [ionShutdownHook] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed from ACCEPTING_TRAFFIC to REFUSING_TRAFFIC
2026-01-23T11:46:13.097 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147482623
2026-01-23T11:46:13.099  INFO [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2026-01-23T11:46:13.108  INFO [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2026-01-23T11:46:13.109 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147481599
2026-01-23T11:46:13.109 DEBUG [tomcat-shutdown] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerGracefulShutdown' completed its stop procedure
2026-01-23T11:46:13.111 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerStartStop' completed its stop procedure
2026-01-23T11:46:13.112 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 1073741823
2026-01-23T11:46:13.646  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 2์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:14.646  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 3์ดˆ ๊ฒฝ๊ณผ
...
2026-01-23T11:46:21.650  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 10์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:22.650  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 11์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:23.118  INFO [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Shutdown phase 1073741823 ends with 1 bean still running after timeout of 10000ms: [taskExecutor1]
2026-01-23T11:46:23.119 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase -2147483647
2026-01-23T11:46:23.119 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'springBootLoggingLifecycle' completed its stop procedure
2026-01-23T11:46:23.120 DEBUG [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'taskExecutor1'
2026-01-23T11:46:23.121  WARN [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—…์ด ๊ฐ•์ œ ์ข…๋ฃŒ(Interrupted) ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!
2026-01-23T11:46:23.121 DEBUG [       Async1-1] o.s.c.support.DefaultLifecycleProcessor  : Bean 'taskExecutor1' completed its stop procedure

 

๋กœ๊ทธ๋ฅผ ์ž˜ ๋ณด๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ACCEPTING_TRAFFIC ์ƒํƒœ์—์„œ REFUSING_TRAFFIC ์ƒํƒœ๋กœ ๋ณ€๊ฒฝํ•œ ํ›„์— 2147482623์ด๋ผ๋Š” phase์— ์žˆ๋Š” ๋นˆ์„ Stoppingํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

2147482623 ๊ฐ’์€ Integer.MAX_VALUE์ธ๋ฐ, ๋‚˜์ค‘์— Phased ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๋ช…ํ•  ๋•Œ ๋‹ค์‹œ ๋‚˜์˜ค๋‹ˆ ๊ธฐ์–ตํ•ด๋‘์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

2026-01-23T09:50:57.239 DEBUG [ionShutdownHook] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed from ACCEPTING_TRAFFIC to REFUSING_TRAFFIC
2026-01-23T11:46:13.097 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147482623
2026-01-23T11:46:13.099  INFO [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2026-01-23T11:46:13.108  INFO [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete

 

๊ทธ ํ›„์— 'Stopping beans in phase {Integer ๊ฐ’}' ์ˆœ์œผ๋กœ ๋กœ๊ทธ๊ฐ€ ์ฐํžˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ, phase ๋ž€ ๋กœ๊ทธ๋Š” timeout-per-shutdown-phase ์˜ phase ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

2026-01-23T11:46:13.109 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147481599
2026-01-23T11:46:13.109 DEBUG [tomcat-shutdown] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerGracefulShutdown' completed its stop procedure
2026-01-23T11:46:13.111 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerStartStop' completed its stop procedure
2026-01-23T11:46:13.112 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 1073741823
2026-01-23T11:46:23.118  INFO [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Shutdown phase 1073741823 ends with 1 bean still running after timeout of 10000ms: [taskExecutor1]
2026-01-23T11:46:23.119 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase -2147483647
2026-01-23T11:46:23.119 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'springBootLoggingLifecycle' completed its stop procedure

 

2.4 timeout-per-shutdown-phase์˜ ์ง„์งœ ์˜๋ฏธ

๊ทธ๋ž˜์„œ, ์•„๋ž˜ ์„ค์ • ๊ฐ’์€ ์‚ฌ์‹ค phase ์ˆซ์ž ๊ฐ’์ด ๋‹ค๋ฅธ ๋นˆ๋“ค์„ ์ข…๋ฃŒํ•  ๋•Œ๋งˆ๋‹ค ์ตœ๋Œ€ 10์ดˆ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ด์ง€, ์ด ์‹œ๊ฐ„ ์•ˆ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ข…๋ฃŒ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 10s

 

๋‹จ์ง€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ phase๊ฐ€ ๋†’์€ ์ˆœ์—์„œ ๋‚ฎ์€ ์ˆœ์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌํ•œ ํ›„, ๊ฐ phase ๊ทธ๋ฃน์„ ์ข…๋ฃŒํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๋‚ด์žฅ๋œ ์›น์„œ๋ฒ„(ํ†ฐ์บฃ, ๋„คํ‹ฐ) ๊ด€๋ จ ๋นˆ๋“ค์˜ phase ๊ฐ’์ด ๊ฐ€์žฅ ๋†’๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ๋จผ์ € ์ข…๋ฃŒ๋˜์–ด ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ๋ฐ›์•„๋“ค์ด์ง€ ์•Š๊ฒŒ ๋˜๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค.

 

2.5 Phase ๋ณ„ ์ข…๋ฃŒ ๋ฉ”์ปค๋‹ˆ์ฆ˜

์˜ˆ๋ฅผ ๋“ค์–ด, ๋นˆ A์™€ B๊ฐ€ phase 1000์ด๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์ง€๊ณ , ๋นˆ C, D, E๊ฐ€ phase 999๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์งˆ ๋•Œ (๋นˆ์€ ๊ฐ™์€ phase ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค), DefaultLifecycleProcessor(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋นˆ๋“ค์„ phase ๋‹จ์œ„๋กœ ์ข…๋ฃŒํ•˜๋Š” ๊ฐ์ฒด)๋Š” phase 1000์„ ๊ฐ€์ง„ ๋นˆ๋“ค์„ ์ •๋ฆฌํ•  ๋•Œ ๊ฐ ๋นˆ๋“ค์ด ์‚ฌ์šฉํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ๋นˆ๋“ค์„ ์žฌ๊ท€์ ์œผ๋กœ ์ฐพ์œผ๋ฉฐ ๊ด€๋ จ ๋นˆ๋“ค์„ ๋ชจ๋‘ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. (์ด๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ doStop()์„ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค). ์ด ๊ณผ์ •์„ phase ๊ฐ’ ๋ณ„๋กœ ์ตœ๋Œ€ 10์ดˆ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์ตœ๋Œ€ 10์ดˆ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ ํ›„์—๋Š” ๋‹ค์Œ phase ๊ฐ’์„ ๊ฐ€์ง„ 999 phase ๋ฅผ ์ข…๋ฃŒํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ 1073741823 ๋นˆ์ธ ์“ฐ๋ ˆ๋“œ ํ’€์„ ์ข…๋ฃŒํ•  ๋•Œ, ์ž‘์—…์ด 10์ดˆ ์ด์ƒ ๊ฑธ๋ฆฌ๊ฒŒ ๋˜๋‹ˆ timeout-per-shutdown-phase 10์ดˆ๋ฅผ ๋„˜๊ธฐ๊ฒŒ ๋˜์–ด ํ•ด๋‹น phase๋Š” ํƒ€์ž„์•„์›ƒ ๋˜์—ˆ๋‹ค๋Š” ๋กœ๊ทธ์™€ ํ•จ๊ป˜ ๋‹ค์Œ phase๋กœ ๋„˜์–ด๊ฐ€๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2026-01-23T11:46:13.112 DEBUG [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 1073741823
2026-01-23T11:46:13.646  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 2์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:14.646  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 3์ดˆ ๊ฒฝ๊ณผ
...
2026-01-23T11:46:21.650  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 10์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:22.650  INFO [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 11์ดˆ ๊ฒฝ๊ณผ
2026-01-23T11:46:23.118  INFO [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Shutdown phase 1073741823 ends with 1 bean still running after timeout of 10000ms: [taskExecutor1]

 

3. ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ: ์“ฐ๋ ˆ๋“œ ํ’€์— ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด?

3.1 ์“ฐ๋ ˆ๋“œ ํ’€ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์„ค์ •

๋‹ค์Œ 2๋ฒˆ์งธ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€์— ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•ด๋ด…์‹œ๋‹ค.

@Configuration
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(1);

        // ์ถ”๊ฐ€!
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(30);

        taskExecutor.initialize();

        return taskExecutor;
    }
}

 

3.2 ๊ฒฐ๊ณผ๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

๊ฒฐ๊ณผ๋Š” ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

  1. ํ†ฐ์บฃ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์‹œ๊ฐ„์„ 10์ดˆ๋กœ ์„ค์ •ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€์— ์ ์šฉํ•œ 30์ดˆ๋Š” ์ ์šฉ๋˜์ง€ ์•Š๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ 10์ดˆ๋งŒ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์ข…๋ฃŒ๋œ๋‹ค!
  2. ํ†ฐ์บฃ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์‹œ๊ฐ„ 10์ดˆ์™€ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€์— ์ ์šฉํ•œ 30์ดˆ๊ฐ€ ๋”ํ•ด์ ธ ์ด 40์ดˆ ๋™์•ˆ ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์ข…๋ฃŒ๋œ๋‹ค!
  3. ํ†ฐ์บฃ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์‹œ๊ฐ„ ๋”ฐ๋กœ, ์“ฐ๋ ˆ๋“œ ํ’€ ์šฐ์•„ํ•œ ์ข…๋ฃŒ๊ฐ€ ๋”ฐ๋กœ ์ ์šฉ๋˜์–ด ๋น„๋™๊ธฐ ์ž‘์—…์„ 30์ดˆ ๋™์•ˆ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์ข…๋ฃŒ๋œ๋‹ค!

์ •๋‹ต์€ 3๋ฒˆ ์ž…๋‹ˆ๋‹ค.

 

3.3 ์™œ ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ• ๊นŒ?

์•„๋ž˜๋Š” ์ „์ฒด ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.

2026-01-22T21:02:43 INFO --- [nio-8080-exec-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ œ์ถœ (80์ดˆ ์†Œ์š” ์˜ˆ์ƒ)...
2026-01-22T21:02:44 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 1์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:02:44 INFO --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2026-01-22T21:02:45 INFO --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete
2026-01-22T21:02:45 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 2์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:02:46 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 3์ดˆ ๊ฒฝ๊ณผ
...
2026-01-22T21:03:12 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 29์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:03:13 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 30์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:03:14 INFO --- [ taskExecutor-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 31์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:03:15 WARN --- [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Timed out while waiting for executor 'taskExecutor' to terminate

 

์™œ ์ด๋Ÿฐ์ง€ timeout-per-shutdown-phase์˜ ์˜๋ฏธ๋ฅผ ์ž˜ ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค.

 

timeout-per-shutdown-phase๋Š” phase ๊ฐ„์˜ ํƒ€์ž„์•„์›ƒ์„ ์ง€์ •ํ•  ๋ฟ์ด์ง€, ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๊ฐ„์„ญํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ฆ‰, ๋น„๋™๊ธฐ๋ฅผ ์œ„ํ•œ ์“ฐ๋ ˆ๋“œ ํ’€์— ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ์„ค์ •ํ•˜๊ฒŒ ๋˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ข…๋ฃŒํ•  ๋•Œ "๊ทธ๋ž˜, ๋„ˆ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์„ค์ •๋˜์–ด ์žˆ์œผ๋‹ˆ ๋„ˆ ์‹œ๊ฐ„๋งŒํผ ๊ธฐ๋‹ค๋ฆฌ๋„๋ก ํ•ด"๋ผ๊ณ  ํ•˜๊ณ  ๋‹ค์Œ phase๋ฅผ ๋ฐ”๋กœ ์ข…๋ฃŒํ•˜๋Ÿฌ ๊ฐ€๊ณ , ๋น„๋™๊ธฐ๋ฅผ ์œ„ํ•œ ์“ฐ๋ ˆ๋“œ ํ’€์€ ๊ทธ ํ’€ ์ž์ฒด๋กœ ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋กœ ์„ค์ •ํ•œ ํƒ€์ž„์•„์›ƒ ์‹œ๊ฐ„๋งŒํผ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ๊ทธ ์‹œ๊ฐ„๊นŒ์ง€ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€์ด ์ž‘์—…์„ ์ข…๋ฃŒํ•˜์ง€ ๋ชปํ•˜๋ฉด ๋งˆ์ง€๋ง‰ ๋กœ๊ทธ(Timed out while waiting for...)์ฒ˜๋Ÿผ "ํ•ด๋‹น ์“ฐ๋ ˆ๋“œ ํ’€์„ ์ข…๋ฃŒํ•˜๋Š”๋ฐ ํƒ€์ž„์•„์›ƒ์ด ๋–ด์–ด์š”. ์ž‘์—…์ด ์™„๋ฃŒ๋  ์ˆ˜๋„ ์žˆ๊ณ , ์•ˆ๋  ์ˆ˜๋„ ์žˆ์–ด์š”."๋ผ๊ณ  WARN ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ๋‚จ๊ธฐ๋ฉฐ ์ข…๋ฃŒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

4. ์„ธ ๋ฒˆ์งธ ๋ฌธ์ œ: ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ

4.1 ๋ณต์ˆ˜ ์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •

๋‹ค์Œ ๋งˆ์ง€๋ง‰ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์—ฐ๋™ ์„œ๋น„์Šค A์™€ B ๊ฐ„์˜ ์žฅ์• ๋ฅผ ๊ฒฉ๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์“ฐ๋ ˆ๋“œ ํ’€ 2๊ฐœ๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ ์—ฐ๋™ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.(์ผ์ข…์˜ ๋ฒŒํฌํ—ค๋” ํŒจํ„ด์ž…๋‹ˆ๋‹ค).

 

์“ฐ๋ ˆ๋“œ ํ’€ 1๋ฒˆ์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์‹œ๊ฐ„์€ 30์ดˆ, ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฒˆ์€ 15์ดˆ์ž…๋‹ˆ๋‹ค.

@Configuration
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor1() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(1);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(30);

        taskExecutor.setThreadNamePrefix("Async1-");

        taskExecutor.initialize();

        return taskExecutor;
    }

    @Bean
    public TaskExecutor taskExecutor2() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(1);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        taskExecutor.setAwaitTerminationSeconds(15);

        taskExecutor.setThreadNamePrefix("Async2-");
        
        taskExecutor.initialize();

        return taskExecutor;
    }
}

 

API ์—”๋“œํฌ์ธํŠธ๋„ 2๊ฐœ๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

@RestController
@RequiredArgsConstructor
public class TestController {

    private final TaskExecutor taskExecutor1;
    private final TaskExecutor taskExecutor2;


    @GetMapping("/tasks-1")
    public String test() throws InterruptedException {
        ThreadPoolExecutor taskExecutor = (ThreadPoolExecutor) taskExecutor1;
        log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ œ์ถœ (80์ดˆ ์†Œ์š” ์˜ˆ์ƒ)...");
        taskExecutor.submit(() -> {
            try {
                for (int i = 1; i <= 80; i++) {
                    Thread.sleep(1000); // 1์ดˆ์”ฉ 30๋ฒˆ ๋Œ€๊ธฐ
                    log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: {}์ดˆ ๊ฒฝ๊ณผ", i);
                }
                log.info("๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ!");
            } catch (InterruptedException e) {
                log.warn("๋น„๋™๊ธฐ ์ž‘์—…์ด ๊ฐ•์ œ ์ข…๋ฃŒ(Interrupted) ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
            }
        });

        return "์ž‘์—…์ด ์ œ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒ(Ctrl+C)ํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.";
    }

    @GetMapping("/tasks-2")
    public String test2() throws InterruptedException {
        ThreadPoolExecutor taskExecutor = (ThreadPoolExecutor) taskExecutor2;
        log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ œ์ถœ (30์ดˆ ์†Œ์š” ์˜ˆ์ƒ)...");
        taskExecutor.submit(() -> {
            try {
                for (int i = 1; i <= 30; i++) {
                    Thread.sleep(1000); // 1์ดˆ์”ฉ 30๋ฒˆ ๋Œ€๊ธฐ
                    log.info("๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: {}์ดˆ ๊ฒฝ๊ณผ", i);
                }
                log.info("๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ!");
            } catch (InterruptedException e) {
                log.warn("๋น„๋™๊ธฐ ์ž‘์—…์ด ๊ฐ•์ œ ์ข…๋ฃŒ(Interrupted) ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!");
            }
        });

        return "์ž‘์—…์ด ์ œ์ถœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์„œ๋ฒ„๋ฅผ ์ข…๋ฃŒ(Ctrl+C)ํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.";
    }
}

 

์š”์•ฝํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

- ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์—ญ ์„ค์ •: timeout-per-shutdown-phase 10์ดˆ

- ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ 1๋ฒˆ: ์ž‘์—… ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ๊นŒ์ง€ 30์ดˆ, ์‹ค์ œ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ 80์ดˆ

- ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฒˆ: ์ž‘์—… ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ๊นŒ์ง€ 15์ดˆ, ์‹ค์ œ ์ž‘์—… ์†Œ์š” ์‹œ๊ฐ„ 30์ดˆ

 

4.2 ๊ฒฐ๊ณผ๋Š”?

๊ฒฐ๊ณผ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

 

๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฒˆ์€ WARN ๋ ˆ๋ฒจ์˜ ๊ฒฝ๊ณ  ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๋กœ๊ทธ์™€ ํ•จ๊ป˜ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋Š” ๋กœ๊ทธ๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ 1๋ฒˆ์€ 30์ดˆ ์ดํ›„์— WARN ๋ ˆ๋ฒจ์˜ ๊ฒฝ๊ณ  ํƒ€์ž„์•„์›ƒ์ด ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๋กœ๊ทธ๋งŒ ๋‚˜์˜ค๊ณ , ์ž‘์—…์ด ์™„๋ฃŒ๋˜์ง€ ์•Š๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

์•„๋ž˜๋Š” ์ „์ฒด ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.

026-01-22T21:26:57.619+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 1์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:26:57.724+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 2์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:26:57.750+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.b.a.ApplicationAvailabilityBean      : Application availability state ReadinessState changed from ACCEPTING_TRAFFIC to REFUSING_TRAFFIC
2026-01-22T21:26:57.751+09:00 DEBUG 60684 --- [ionShutdownHook] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@62452cc9, started on Thu Jan 22 21:26:53 KST 2026
2026-01-22T21:26:57.752+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147482623
2026-01-22T21:26:57.753+09:00  INFO 60684 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2026-01-22T21:26:58.418+09:00  INFO 60684 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete

2026-01-22T21:26:58.418+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147481599
2026-01-22T21:26:58.418+09:00 DEBUG 60684 --- [tomcat-shutdown] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerGracefulShutdown' completed its stop procedure
2026-01-22T21:26:58.419+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'webServerStartStop' completed its stop procedure

2026-01-22T21:26:58.419+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 1073741823
2026-01-22T21:26:58.419+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'taskExecutor1' completed its stop procedure
2026-01-22T21:26:58.419+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'taskExecutor2' completed its stop procedure

2026-01-22T21:26:58.436+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase -2147483647
2026-01-22T21:26:58.436+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.c.support.DefaultLifecycleProcessor  : Bean 'springBootLoggingLifecycle' completed its stop procedure

2026-01-22T21:26:58.436+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'taskExecutor2'
2026-01-22T21:26:58.622+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 2์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:26:58.726+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 3์ดˆ ๊ฒฝ๊ณผ
...
2026-01-22T21:27:11.812+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 16์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:12.729+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 16์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:12.820+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 17์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:13.448+09:00  WARN 60684 --- [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Timed out while waiting for executor 'taskExecutor2' to terminate
2026-01-22T21:27:13.448+09:00 DEBUG 60684 --- [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'taskExecutor1'
2026-01-22T21:27:13.734+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 17์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:13.825+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 18์ดˆ ๊ฒฝ๊ณผ
...
2026-01-22T21:27:26.849+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 30์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:26.849+09:00  INFO 60684 --- [       Async2-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ!
2026-01-22T21:27:26.926+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 31์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:27.942+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 32์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:28.944+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 33์ดˆ ๊ฒฝ๊ณผ
...
2026-01-22T21:27:41.009+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 45์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:42.021+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 46์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:43.027+09:00  INFO 60684 --- [       Async1-1] p.shutdown.config.TestController         : ๋น„๋™๊ธฐ ์ž‘์—… ์ง„ํ–‰ ์ค‘: 47์ดˆ ๊ฒฝ๊ณผ
2026-01-22T21:27:43.459+09:00  WARN 60684 --- [ionShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Timed out while waiting for executor 'taskExecutor1' to terminate

 

4.3 ์™œ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฒˆ์€ 30์ดˆ ์ž‘์—…์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์—ˆ์„๊นŒ?

์™œ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฒˆ์€ ์ž‘์—… ๊ธฐ๋‹ค๋ฆฌ๊ธฐ๊นŒ์ง€ 15์ดˆ๋‹ˆ๊นŒ ์‹ค์ œ ์ž‘์—…์„ ์™„๋ฃŒํ•˜์ง€ ๋ชปํ•˜๊ณ  ์ข…๋ฃŒ๋  ๊ฒƒ ๊ฐ™์€๋ฐ, ์–ด๋–ป๊ฒŒ ์ž‘์—…์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์—ˆ์„๊นŒ์š”?

 

์ด๋Š” Spring์ด ์“ฐ๋ ˆ๋“œ ํ’€์„ ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ ์œ„ํ•ด ๊ธฐ๋Šฅ์„ ํ™•์žฅํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.(Java์˜ ThreadPoolExecutor ๋ฅผ Spring ์˜ ThreadPoolTaskExecutor ๋กœ ํ™•์žฅ)

 

4.4 Spring์˜ Java ์“ฐ๋ ˆ๋“œ ํ’€ ํ™•์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜

 

Shutdown ํ›…์— ์˜ํ•ด ThreadPoolTaskExecutor์˜ shutdown์ด ํ˜ธ์ถœ๋˜๋ฉด ์šฐ์•„ํ•œ ์ข…๋ฃŒ๋ฅผ true๋กœ ์„ค์ •ํ–ˆ์„ ์‹œ, Java ์“ฐ๋ ˆ๋“œ ํ’€ ์ข…๋ฃŒ ๋ฐฉ์‹์ธ ThreadPoolExecutor ๊ตฌํ˜„์ฒด์— ๋งž์ถฐ ์ด๋ฏธ ์ œ์ถœ๋œ ์ž‘์—…๋“ค์€ ์‹คํ–‰๋˜์ง€๋งŒ ์ƒˆ๋กœ์šด ์ž‘์—…์€ ๋ฐ›์ง€ ์•Š๋Š” ์ข…๋ฃŒ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. (์—ฌ๊ธฐ ๊นŒ์ง€๋Š” Java์˜ ThreadPoolExecutor ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.) ํ•˜์ง€๋งŒ, Spring ์€ ์“ฐ๋ ˆ๋“œ ํ’€์˜ shutdown ์„ ๋๋งˆ์น˜๊ณ  ์ถ”๊ฐ€๋กœ awaitTerminationIfNecessary() ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ ๋ช…๋งŒ ๋ด๋„ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด "์“ฐ๋ ˆ๋“œ ํ’€์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค"์ž…๋‹ˆ๋‹ค.

 

์ด๋•Œ ์„ค์ •๋œ ์‹œ๊ฐ„(15์ดˆ ๋˜๋Š” 30์ดˆ) ๋™์•ˆ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ๋˜๊ณ , ๋งŒ์•ฝ ๊ทธ ์‹œ๊ฐ„ ๋‚ด์— ์ž‘์—…์„ ๋‹ค ๋ชป ๋๋‚ด๋ฉด WARN ๋ ˆ๋ฒจ์˜ ๋กœ๊ทธ๋งŒ ๋…ธ์ถœํ•˜๊ณ  ๊ฐ•์ œ ์ข…๋ฃŒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ์€ "๊ฐ•์ œ ์ข…๋ฃŒํ•˜์ง€ ์•Š๋Š”๋‹ค"๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์“ฐ๋ ˆ๋“œ ํ’€ 2์˜ shutdown hook์ด ํ˜ธ์ถœ๋˜์–ด 15์ดˆ ๋™์•ˆ ๊ธฐ๋‹ค๋ ธ์ง€๋งŒ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ด๋•Œ Spring์€ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋ฅผ ๊ฐ•์ œ๋กœ ์ข…๋ฃŒํ•˜์ง€ ์•Š๊ณ  WARN ๋กœ๊ทธ๋งŒ ๋‚จ๊ธด ์ฑ„ ๋‹ค์Œ phase๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ , ์“ฐ๋ ˆ๋“œ ํ’€ 1์˜ ์ข…๋ฃŒ phase๊ฐ€ ์‹œ์ž‘๋˜๊ณ  30์ดˆ ๋™์•ˆ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ, ์“ฐ๋ ˆ๋“œ ํ’€ 2์˜ ์ž‘์—…๋„ ํ•จ๊ป˜ ๊ณ„์† ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์“ฐ๋ ˆ๋“œ ํ’€2๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ 1๋„ ์ข…๋ฃŒํ•  ํ•„์š” ์—†์ด ๊ฐ™์ด ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ฒฐ๊ณผ์ ์œผ๋กœ ์“ฐ๋ ˆ๋“œ ํ’€ 2๋Š” ์ž์‹ ์—๊ฒŒ ํ• ๋‹น๋œ 15์ดˆ๋ฅผ ๋„˜์–ด ์“ฐ๋ ˆ๋“œ ํ’€ 1์ด ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” 30์ดˆ ๋™์•ˆ ์ถ”๊ฐ€๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด, ์ด 30์ดˆ ์†Œ์š” ์ž‘์—…์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ •๋ฆฌํ•˜๋ฉด, Spring ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถฐ ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์ž‘์—…์ด ์ตœ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์“ฐ๋ ˆ๋“œ ํ’€์€ ์ž์‹ ์—๊ฒŒ ์„ค์ •๋œ ์‹œ๊ฐ„๋งŒํผ ๊ธฐ๋‹ค๋ฆฌ์ง€๋งŒ, ๊ทธ ์‹œ๊ฐ„์ด ์ง€๋‚ฌ๋‹ค๊ณ  ํ•ด์„œ ๊ฐ•์ œ๋กœ ์ž‘์—…์„ ์ค‘๋‹จํ•˜์ง€ ์•Š๊ณ , ๋‹ค๋ฅธ phase๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ๊ณ„์†ํ•ด์„œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ์œ ๋ฅผ ์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ, ์“ฐ๋ ˆ๋“œ ํ’€2 ์˜ shutdown hook ์ด ํ˜ธ์ถœ๋˜์—ˆ์ง€๋งŒ ์“ฐ๋ ˆ๋“œ ํ’€1์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์‹œ๊ฐ„์„ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํ”„๋ง์ด ์“ฐ๋ ˆ๋“œ ํ’€2 ๋„ ๊ทธ๋ƒฅ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ๋งŒ ์ผ๋‹จ ์‹คํ–‰ ์‹œํ‚ฌ๊ป˜~ ํ•˜์ง€๋งŒ, Warn ๋ ˆ๋ฒจ ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ์ž‘์—…์ด ๋‹ค ์™„๋ฃŒ๋˜์ง„ ์•Š์„ ์ˆ˜ ์žˆ์–ด.๋ผ๊ณ  ํƒ€์ž„์•„์›ƒ ๊ฒฝ๊ณ ๋ฅผ WARN ๋กœ๊ทธ๋กœ ์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ์Šคํ”„๋ง์—์„œ ์“ฐ๋ ˆ๋“œ ํ’€์„ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ๋•Œ, ์Šคํ”„๋ง์ด ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์— ๋งž์ถฐ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ThreadPoolExecutor๊ฐ€ ์•„๋‹Œ ThreadPoolTaskExecutor๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ํ…Œ์ŠคํŠธํ•  ๋•Œ Executors.newFixedThreadPool()์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ด ์“ฐ๋ ˆ๋“œ ํ’€์ด ์Šคํ”„๋ง ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถฐ์งˆ ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

5. SmartLifecycle๊ณผ Phased: Spring์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜

๊ทธ๋Ÿผ Spring์€ ์–ด๋–ป๊ฒŒ ์ƒ๋ช…์ฃผ๊ธฐ์— ๋งž์ถฐ ์“ฐ๋ ˆ๋“œ ํ’€์„ ์ข…๋ฃŒํ•˜๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ์„๊นŒ์š”?

 

SmartLifecycle(Bean์„ ๊ด€๋ฆฌํ•˜๋Š” ApplicationContext๊ฐ€ refresh๋˜๊ฑฐ๋‚˜ shutdown๋  ๋•Œ, ํŠน์ • ์ˆœ์„œ์— ๋งž๊ฒŒ ๋นˆ์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ข…๋ฃŒํ•˜๋„๋ก ์ง€์›ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค)๊ณผ Phased๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋นˆ๋“ค์„ ์ˆœ์„œ(Phased)์— ๋”ฐ๋ผ ํšจ์œจ์ ์œผ๋กœ ์ •๋ฆฌํ•ด์ค๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ Phased์˜ getPhase๋Š” int๋ผ์„œ Integer.MIN_VALUE๋ถ€ํ„ฐ Integer.MAX_VALUE ๊ฐ’์„ ๊ฐ€์ง€๋Š”๋ฐ, ThreadPoolTaskExecutor์™€ ThreadPoolTaskScheduler๋Š” Integer.MAX_VALUE / 2์˜ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

 

์Šคํ”„๋ง ์ปจํ…์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ, Phased๊ฐ€ ๋†’์€ ์ˆœ์„œ(ํ†ฐ์บฃ ์“ฐ๋ ˆ๋“œ ํ’€)๋ถ€ํ„ฐ ์ข…๋ฃŒ๋จ์œผ๋กœ์จ ์›น ์š”์ฒญ์„ ๋ง‰๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋ฉฐ ์ฒœ์ฒœํžˆ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋ก๋œ ๋นˆ๋“ค์„ Phased๊ฐ€ ๋‚ฎ์€ ์ˆœ์œผ๋กœ ์ •๋ฆฌํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

 

SmartLifecycle์„ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นˆ๋“ค, ์ผ๋ฐ˜ Lifecycle ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ @Component ๋กœ ๋“ฑ๋ก๋œ ๋นˆ๋“ค์€ Phase๋ฅผ 0์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

6. ๊ฒฐ๋ก 

7.1 timeout-per-shutdown-phase์˜ ์ •ํ™•ํ•œ ์˜๋ฏธ

์ง์—ญํ•˜์ž๋ฉด "๊ฐ Phase๋ฅผ shutdown ํ•  ๋•Œ๋งˆ๋‹ค 10์ดˆ์˜ ํƒ€์ž„์•„์›ƒ"์ด๋ผ๋Š” ์–˜๊ธฐ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ๋Š” Phase ๊ฐ’์ด ํฐ Bean ๋ถ€ํ„ฐ ์ข…๋ฃŒํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. 

Phase 2147482623 (Tomcat):
→ stop() ํ˜ธ์ถœ
→ ์ตœ๋Œ€ 10์ดˆ ๋Œ€๊ธฐ
→ 10์ดˆ ์ง€๋‚˜๋ฉด ๋‹ค์Œ Phase๋กœ

Phase 1073741823 (TaskExecutor๋“ค):
→ stop() ํ˜ธ์ถœ
→ ์ตœ๋Œ€ 10์ดˆ ๋Œ€๊ธฐ
→ 10์ดˆ ์ง€๋‚˜๋ฉด ๋‹ค์Œ Phase๋กœ

Phase 0:
→ stop() ํ˜ธ์ถœ
→ ์ตœ๋Œ€ 10์ดˆ ๋Œ€๊ธฐ

 

7.2 import(ํŒจํ‚ค์ง€)์˜ ์ค‘์š”์„ฑ

์ž, ThreadPoolExecutor, Executor, ExecutorService์˜ import๋Š” java์ž…๋‹ˆ๋‹ค. ThreadPoolTaskExecutor, ThreadPoolTaskScheduler์˜ ํŒจํ‚ค์ง€๋Š” spring์ž…๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์‚ฌ์†Œํ•˜๊ฒŒ ํŒจํ‚ค์ง€๋งŒ ๋‚˜๋‰˜๊ฒŒ ๋˜์ง€๋งŒ ๊ทธ์— ๋”ฐ๋ผ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๋ฉฐ, "์–ด? ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋Š”๋ฐ Spring์€ ๋ฌด์Šจ ์ด์œ ๋กœ ํ™•์žฅํ•œ ๊ฑฐ์ง€? ์–ด๋–ค ๊ธฐ๋Šฅ์ด ๋” ์žˆ๋Š” ๊ฑฐ์ง€?"๋ฅผ ์ƒ๊ฐํ•˜๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

7.3 ThreadPoolTaskExecutor์˜ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ

์ด์™ธ์—๋„ ThreadPoolTaskExecutor๋Š” ๋นˆ์ด ๋“ฑ๋ก๋  ๋•Œ Core ์ˆ˜๋งŒํผ ์“ฐ๋ ˆ๋“œ๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋†“๋Š” ์„ค์ •, Max ์“ฐ๋ ˆ๋“œ๋ฟ ์•„๋‹ˆ๋ผ Core ์“ฐ๋ ˆ๋“œ๋„ keep-alive ์‹œ๊ฐ„์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ์„ค์ • ๋“ฑ ๋‹ค์–‘ํ•˜๋‹ˆ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

7.4 ์ž‘์—…์˜ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋Š” TransactionalOutbox ํŒจํ„ด

๋งˆ์ง€๋ง‰์œผ๋กœ, ๊ทธ๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ข…๋ฃŒํ•  ๋•Œ ๋ชจ๋“  ์ž‘์—…์„ ๊น”๋”ํ•˜๊ฒŒ ๋๋‚ด์•ผ๋งŒ ํ•œ๋‹ค๋ฉด, ์ž‘์—…์ด ์œ ์‹ค๋˜์–ด์•ผ ํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค๋ฉด ๊ทธ ์ž‘์—…์ด ์–ผ๋งˆ๋‚˜ ๊ฑธ๋ฆด์ง€ ์˜ˆ์ธกํ•˜๊ณ  ๊ณ„์‚ฐํ•ด์„œ ์šฐ์•„ํ•œ ์ข…๋ฃŒ์˜ ์‹œ๊ฐ„์„ ์„ค์ •ํ•˜๋ฉด ๋ ๊นŒ์š”? 

 

์ฐจ๋ผ๋ฆฌ DB ๋‚˜ ๋ฉ”์‹œ์ง€ ํ ๊ฐ™์€ ๋ฏธ๋“ค์›จ์–ด์— ๋จผ์ € ์ €์žฅํŽธ์ด ๋‚˜์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ฉ”์‹œ์ง€ ํ ๊ฐ™์€ ๋ฏธ๋“ค์›จ์–ด์— ์ €์žฅํ•˜๋ ค๋ฉด TransactionalOutbox ํŒจํ„ด์„ ํ†ตํ•ด at least once ๋ฐฉ์‹์œผ๋กœ ์ •ํ•ฉ์„ฑ์„ ๋งž์ถ”๋Š” ๊ฒƒ์€ ๋ค์ด๊ตฌ์š”.

 

 

์ฐธ๊ณ ์ž๋ฃŒ

Graceful Shutdown

SmartLifeCycle

 

์ถ”๊ฐ€๋กœ ์ฝ์–ด๋ณผ๋งŒํ•œ ์ž๋ฃŒ

ํ†ฐ์บฃ์˜ ์šฐ์•„ํ•œ ์ข…๋ฃŒ ์ง€์› ๊ณผ์ •