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

์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

@Async ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœ ์‹œ์— SecurityContext ์ „๋‹ฌํ•˜๊ธฐ - Spring Security, Thread Pool

๐Ÿ˜“ ๋ฌธ์ œ์ƒํ™ฉ

๋ฐฐ๊ฒฝ

ํ”„๋กœ์ ํŠธ์—์„œ Request์‹œ์— RTT๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” API๊ฐ€ ์žˆ์–ด์„œ

Request๊ฐ€ ๋“ค์–ด์˜ค๊ณ  ํ•ด๋‹น ์“ฐ๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ๋กœ ์ƒˆ๋กœ์šด ์“ฐ๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์ž‘์—…์„ ์œ„์ž„ํ•˜๊ณ 

Response๋ฅผ ๋ฐ”๋กœ ๋‚ด๋ฆฌ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ์—ˆ๋‹ค. 

FLOW

 

Spring Security๋กœ Requestํ•œ ์œ ์ ธ์˜ ์ •๋ณด๋ฅผ Thread ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์–ด์„œ

SecurityContextHolder.MODE_INHERITABLETHREADLOCAL ๋กœ ์„ค์ •ํ•˜์—ฌ ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์—์„œ ํŒŒ์ƒ๋œ ์ž์‹ ์“ฐ๋ ˆ๋“œ์— SecurityContext๊ฐ€ ์ „ํŒŒ๋  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค 

 

์ƒํ™ฉ - ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext(์ธ์ฆ์ •๋ณด)๊ฐ€ ์™„์ „ ๊ผฌ์ž„ ๐Ÿ‘…

Local์—์„œ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” Requestํ•œ ์œ ์ ธ์˜ ์ธ์ฆ์ •๋ณด๊ฐ€ ์žˆ๋Š” SecurityContext๊ฐ€ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋กœ ๋„˜์–ด๊ฐ€๋Š”๋ฐ

๊ฐœ๋ฐœ ์™„๋ฃŒํ•˜๊ณ , Dev ์„œ๋ฒ„์— ๋ฐฐํฌํ•˜๋ฉด ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext๊ฐ€ ๋‹ค๋ฅธ ์œ ์ ธ์ธ ๊ฒƒ์ด๋‹ค!

์‹ฌ์ง€์–ด ๋งค๋ฒˆ ๊ฐ™์€๊ณ„์ •์œผ๋กœ Request ์—ฌ๋Ÿฌ๋ฒˆ ํ• ๋•Œ๋งˆ๋‹ค ์—ฌ๋Ÿฌ๋ฒˆ ๋žœ๋ค์˜ ์œ ์ ธ๊ฐ€ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext์—์„œ ๋ฐœ๊ฒฌ๋˜์—ˆ๋‹ค!!!

(JVM์ธ๋ฐ Local์—์„œ๋Š” ๋˜๊ณ , Dev์—์„œ๋Š” ์•ˆ๋œ๋‹ค?? ๊ฐ€ ์‚ฌ์‹ค ๋ง์ด ์•ˆ๋˜๋Š” ๊ฑด๋ฐ ๋’ค์—์„œ ๋งํ•˜์ง€๋งŒ, ์‚ฌ์‹ค ์—†์ง€์•Š์•„ ์†์•˜๋‹ค)


๐Ÿ”Ž ๋ฌธ์ œ์˜ Config

์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ์˜ ์ƒํ™ฉ์—์„œ ์“ฐ๋ ˆ๋“œ ๊ด€๋ จ Config๋ฅผ ์‚ดํŽด๋ณด๋ฉด

 

Spring Security Mode

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SpringSecurityConfig {

    @PostConstruct
    public void init() {
    	SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }
    ...์ƒ๋žต

}

 

Spring Security๋Š” ์ธ์ฆ/์ธ๊ฐ€ ์ •๋ณด๋ฅผ SecurityContext์— Thread ๋ณ„๋กœ ๊ฐ€์ง€๊ฒŒ๋œ๋‹ค

 

Thread๋ณ„๋กœ ๊ฐ€์ง€๊ฒŒ๋˜๋Š” SecurityContext

 

์ข€ ๋” ์ž์„ธํžˆ ๋งํ•˜๋ฉด

SecurityContext๋Š” SecurityContextStrategy๋กœ Wrapping ๋˜์–ด์„œ strategy ์•ˆ์— ThreadLocal ํƒ€์ž…์œผ๋กœ ์กด์žฌํ•œ๋‹ค

SecurityContextHolderStrategy ๊ตฌํ˜„์ฒด 4๊ฐœ

Strategy๋Š” 4๊ฐœ์˜ ๊ตฌํ˜„์ฒด๊ฐ€ ์žˆ๋Š”๋ฐ

Spring config ์‹œ์ ์— MODE๋ฅผ ํ•˜๋‚˜ ์„ ํƒํ•˜๋ฉด ์ „๋žตํŒจํ„ด์œผ๋กœ strategy๊ฐ€ ์„ ์ •์ด๋œ๋‹ค

๊ฐ strategy ๊ธฐ๋ฐ˜์œผ๋กœ SecurityContext๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋‹ค

  • MODE_THREADLOCAL : ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์— ์ข…์†๋œ ThreadLocal ๊ฐ์ฒด์— SecurityContext  ์ €์žฅ (default)
  • MODE_INHERITABLETHREADLOCAL : ์ƒ์„ฑ๋˜๋Š” ์ž์‹ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext ์ƒ์† ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ
  • MODE_GLOBAL : ์•ฑ ์ „์ฒด์— ํ•œ๊ฐœ์˜ SecurityContext๋ฅผ ์ƒ์„ฑ ๊ณต์œ  for ๋‹จ์ผ ์œ ์ ธ
  • MODE_PRE_INITIALIZED : ์•ฑ ์‹œ์ž‘ ๋•Œ SecurityContext ๋ฏธ๋ฆฌ ์ดˆ๊ธฐํ™”

-> ํ”„๋กœ์ ํŠธ๋Š” ์ž์‹์“ฐ๋ ˆ๋“œ๊ฐ€ ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext๋ฅผ ๋ฐ›์•„์„œ ๋น„๋™๊ธฐ ์ž‘์—…์„ ํ•ด์•ผํ–ˆ์–ด์„œ MODE_INHERITABLETHREADLOCAL์„ ์„ ํƒํ–ˆ๋‹ค

Async Config

@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        int corePoolSize = Runtime.getRuntime().availableProcessors(); // CPU ์ฝ”์–ด ์ˆ˜์— ๋งž์ถฐ ์Šค๋ ˆ๋“œ ํ’€ ํฌ๊ธฐ ์„ค์ •
        int maxPoolSize = corePoolSize * 2; // ์ตœ๋Œ€ ํ’€ ํฌ๊ธฐ - CPU ์ฝ”์–ด ์ˆ˜์˜ ๋‘ ๋ฐฐ
        int queueCapacity = 500; // ํ ์šฉ๋Ÿ‰ ์„ค์ •

        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("AsyncExecutor-");

        ... ์ค‘๋žต

        executor.initialize();

        return executor
    }

}

 

@Async ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ ์‹œ

์•„๋ž˜์˜ ์ŠคํŽ™์˜ Thread Pool์—์„œ 

  • Core Pool Size : ํ•ญ์ƒ ์œ ์ง€ ๋˜๋Š” ์ตœ์†Œ ์Šค๋ ˆ๋“œ ๊ฐœ์ˆ˜ - CPU ์ฝ”์–ด์ˆ˜์— ๋งž๊ฒŒ
    • ์ž‘์—…์ด ์—†๋”๋ผ๋„ ์ด ์ˆซ์ž ๋งŒํผ์€ ํ•ญ์ƒ ์ƒ์„ฑ๋˜์–ด ๋Œ€๊ธฐ ์ƒํƒœ ์œ ์ง€
  • Max Pool Size : ์ตœ๋Œ€ ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜ - corerPoolSize ์˜ 2๋ฐฐ
  • Queue Capacity : ์ž‘์—… ํ์˜ ์‚ฌ์ด์ฆˆ - 500
    • Core Pool Size๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด Queue์— ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๊ณ , 
    • Queue๊ฐ€ ๊ฝ‰์ฐจ๋ฉด MaxPoolSize ๋งŒํผ ์“ฐ๋ ˆ๋“œ๊ฐ€ ํ™•์žฅ

Pool์— Thread๊ฐ€ ์ƒ์„ฑ์•ˆ๋˜์–ด์žˆ์œผ๋ฉด ์ƒ์„ฑ / ์ƒ์„ฑ๋˜์–ด์žˆ์œผ๋ฉด ์žฌ์‚ฌ์šฉ ํ•˜๊ฒŒ ๋œ๋‹ค

 


โœŒ ์žฌํ˜„ํ•ด๋ณด๊ธฐ

์‚ฌ์‹ค ์ œ์ผ ์ดํ•ด๊ฐ€ ์•ˆ๋˜์—ˆ๋˜ ๋ถ€๋ถ„์€ Local ํ™˜๊ฒฝ์—์„œ๋Š” ๋˜๊ณ , Dev ํ™˜๊ฒฝ์—์„œ๋Š” ์•ˆ๋˜๋Š” ๊ฒƒ์ด ์ œ์ผ ์ด์ƒํ–ˆ๋‹ค

Thread๋Š” ๊ฒฐ๊ตญ ๊ฐ€์ƒ ๋จธ์‹  ์œ„์—์„œ JVM์— ์˜ํ•ด ๋งŒ๋“ค์–ด์ง€๋Š”๋ฐ, OS ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค

 

Spring Security์— ๋Œ€ํ•ด์„œ ์ข€ ๋” deep diveํ•œ ํ›„์— 

๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณด๋ฉฐ ์ฐจ๊ทผํžˆ ๋‹ค์‹œ ์‚ดํŽด๋ณด๊ธฐ๋กœ ํ•˜์˜€๋‹ค

 

์‹คํ—˜ํ™˜๊ฒฝ

๊ธฐ์กด Async Config๋กœ๋Š” Thread Pool ์˜ ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜๊ฐ€ ๋งŽ์•„์„œ ๋กœ๊ทธ ์ฐ๊ธฐ๊ฐ€ ์–ด๋ ค์šธ๊ฑฐ ๊ฐ™์•„์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ธํŒ…ํ–ˆ๋‹ค

  • Core Pool Size : 2๊ฐœ
  • Max Pool Size : 2๊ฐœ

์ฆ‰ ๊ทธ๋ƒฅ ์“ฐ๋ ˆ๋“œ 2๊ฐœ๋กœ๋งŒ ์‹คํ—˜ํ•ด๋ณด์•˜๋‹ค

 

ํ”Œ๋กœ์šฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๊ณ 

 

Request -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ(Servelet Request๋กœ ์ƒ์„ฑ๋œ ์“ฐ๋ ˆ๋“œ) -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ Response ๋™์‹œ์— ์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ์‹คํ–‰

Request -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ(Servelet Request๋กœ ์ƒ์„ฑ๋œ ์“ฐ๋ ˆ๋“œ) -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ Response ๋™์‹œ์— ์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ์‹คํ–‰

 

API ํ˜ธ์ถœ์„ ์œ ์ ธ ๋กœ๊ทธ์ธ์„ ๋ฐ”๊ฟ”๊ฐ€๋ฉฐ ์•„๋ž˜์˜ ์ˆœ์„œ๋กœ 4๋ฒˆ ํ˜ธ์ถœํ•˜์—ฌ 4๊ฐœ์˜ ๋ถ€๋ชจ์ž์‹ ์“ฐ๋ ˆ๋“œ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค

  1. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ
  2. ์ง์›B ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ
  3. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ
  4. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-id=33
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-name=http-nio-8080-exec-2
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›A]
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] Locale=ko_KR
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] --------------------------- [test]

2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-id=53
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-name=AsyncExecutor-1
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›A]
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] Locale=null
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] --------------------------- [test]



2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-id=34
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-name=http-nio-8080-exec-3
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›B]
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] Locale=ko_KR
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] --------------------------- [test]

2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-id=58
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-name=AsyncExecutor-2
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›B]
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] Locale=null
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] --------------------------- [test]


2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-id=35
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-name=http-nio-8080-exec-3
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›A]
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] Locale=ko_KR
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] --------------------------- [test]

2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-id=53
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-name=AsyncExecutor-1
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›A]
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] Locale=null
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] --------------------------- [test]



2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-id=36
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-name=http-nio-8080-exec-5
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›A]
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] Locale=ko_KR
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] --------------------------- [test]

2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-id=58
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-name=AsyncExecutor-2
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์›B]
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] Locale=null
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] --------------------------- [test]

 

๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ

4๋ฒˆ์งธ ํ˜ธ์ถœ์„ ๋ณด๋ฉด

์ง์›B ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœํ–ˆ๋Š”๋ฐ,

๋น„๋™๊ธฐ ์ž์‹ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext๊ฐ€ ์ง์›B ์ธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ๐Ÿšจ

 

๋ถ„๋ช… Spring Security์—์„œ ์ž์‹ ์“ฐ๋ ˆ๋“œ๋กœ SecuritContext ์ „ํŒŒํ•˜๋Š” ์„ค์ •์„ ํ–ˆ๋Š”๋ฐ????????

 

์—ฌ๊ธฐ์„œ ๋ฉ˜๋ถ•์ด ์™€์„œ ํ•œ๋™์•ˆ ๋งŽ์€ ์ž๋ฃŒ๋“ค์„ ์ฐพ์•„๋ณด๊ณ  ์„ ์ž„๊ณผ ๋‹ค์–‘ํ•œ ํ† ๋ก ์„ ํ–ˆ์—ˆ๋‹ค


๐Ÿค” MODE_INHERITABLETHREADLOCAL์— ๋Œ€ํ•œ ์˜์‹ฌ ๊ทธ๋ฆฌ๊ณ  Thread Pool 

์ž๋ฃŒ๋ฅผ ๋” ์ฐพ์•„๋ณด๊ณ , ๋””๋ฒ„๊น…์„ ํ•ด๋ณด๋‹ˆ 

Thread๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์‹œ์ ์— SecurityContext๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ๊นŒ์ง€ ํ™•์ธ์„ํ–ˆ๋‹ค

์ค‘์š”ํ•œ๊ฑด Thread๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์‹œ์ ์ด์—ˆ๋‹ค

 

์œ„ ์‹คํ—˜์—์„œ Thread Pool์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋Š” 2๊ฐœ๋‹ค

 

์ฒ˜์Œ์—

Spring init ์‹œ์ ์— Executor๊ฐ€ Thread Pool์— CoreSize ๋งŒํผ ์“ฐ๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋†“๋Š”๋‹ค?

๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค

 

ํ•˜์ง€๋งŒ ์‹ค์ œ ๋Ÿฐํƒ€์ž„์—์„œ ๋””๋ฒ„๊น…์„ ํ•ด๋ณด๋‹ˆ

 

  1. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-1 ์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ(์ง์›A) -> Pool์— ๋ฐ˜๋‚ฉ
  2. ์ง์›B ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-2 ์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ(์ง์›B) -> Pool์— ๋ฐ˜๋‚ฉ
  3. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-1 ์“ฐ๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ (์ง์›A) -> Pool์— ๋ฐ˜๋‚ฉ
  4. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-2 ์“ฐ๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ (์ง์›B) -> Pool์— ๋ฐ˜๋‚ฉ

 (์‹คํ—˜์„ ์œ„ํ•ด ์œ„์˜ 4๊ฐœ์˜ ํ˜ธ์ถœ์€ Localํ™˜๊ฒฝ์—์„œ ์‹œ๊ฐ„์ฐจ๋ฅผ ๋‘๊ณ  4๋ฒˆ ํ˜ธ์ถœ)

 

JVM๋งˆ๋‹ค ์„ค์ •์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์ง€๋งŒ (์ถ”ํ›„ ๊ณต๋ถ€๊ฑฐ๋ฆฌ๋‹ค)

  • Spring์ด initํ• ๋•Œ๋Š” ThreadPool์ด ๋น„์–ด์žˆ๋‹ค๊ฐ€
  • ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ์ฒซ ํ˜ธ์ถœ ๊ธฐ์ ์œผ๋กœ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•ด์„œ Pool์— ๋„ฃ์–ด์ฃผ๊ธฐ ์‹œ์ž‘
  • MODE_INHERITABLETHREADLOCAL์€ ์ƒ์„ฑ! ์‹œ์ ์— Thread์— SecurityContext ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—
  • 3,4๋ฒˆ ํ˜ธ์ถœ ๋•Œ๋Š” Thread ์žฌ์‚ฌ์šฉ์ด๋ฏ€๋กœ ์ ์šฉ๋˜์ง€ ์•Š์Œ 

 

๊ทธ๋ ‡๋‹ค๋ฉด 3,4๋ฒˆ ํ˜ธ์ถœ ๋•Œ๋Š” ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋“ค์˜ SecurityContext๊ฐ€ null์ด์–ด์•ผํ•˜์ง€ ์•Š์€๊ฐ€? ์‹ถ์€๋ฐ

 

 

  • 2๋ฒˆ AsyncExecutor-2๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ๋‚จ์•„์žˆ๋Š” ์ง์› B ์ธ์ฆ์ •๋ณด๊ฐ€
  • 4๋ฒˆ ํ˜ธ์ถœ์ผ ๋•Œ AsyncExecutor-2๊ฐ€ ์žฌ์‚ฌ์šฉ๋˜๋ฉด์„œ
  • ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ์™€ ์ž์‹๋น„๋™๊ธฐ์“ฐ๋ ˆ๋“œ์˜ SecurityContext๊ฐ€ ๋ถˆ์ผ์น˜

๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๊ฐ€ Pool์— ๋ฐ˜ํ™˜๋ ๋•Œ SecurityContext๋ฅผ ์ง€์›Œ์ฃผ์ง€ ์•Š๋Š”๋‹ค!!

 

 

์„œ๋ธ”๋ฆฟ Request๋กœ ์ƒ์„ฑ๋œ ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ์—์„œ๋Š” SecurityContext๊ฐ€

์ž์‹ ์“ฐ๋ ˆ๋“œ SecurityContext์™€ ๋‹ค๋ฅด๋‹ค๋ฉด ๊ฐ ์“ฐ๋ ˆ๋“œ์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ด๊ณ  ๊ฐ ์“ฐ๋ ˆ๋“œ์—์„œ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋Š” ๋ฌด์—‡์ธ์ง€ ๊ณต๋ถ€ํ•ด๋ด์•ผํ–ˆ๋‹ค


๐Ÿค” SecurityContext์˜ ์ƒ๋ช…์ฃผ๊ธฐ

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด, Servelet Request๋กœ ์ƒ์„ฑ๋œ ์“ฐ๋ ˆ๋“œ ๋‚ด์—์„œ๋งŒ Spring Security๊ฐ€ ์ž๋™์œผ๋กœ ์œ ํšจํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์ค€๋‹ค

 

Architecture :: Spring Security

The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API. Those filters can be used for a number of different purposes, like exploit protection,authentication, authorization, and more. The filters are executed in a speci

docs.spring.io

 

Spring Security๋Š” ์ธ์ฆ/์ธ๊ฐ€๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  SecurityContext๋ฅผ ์„ธํŒ…ํ•˜๋Š” ๋ฐ์— Filter๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

HTTP Request๊ฐ€ ์•„๋ž˜์™€ ๊ฐ™์ด Filter๋ฅผ ๊ปด์„œ ์ง„ํ–‰์ด ๋˜๋Š”๋ฐ

  1. ํด๋ผ์ด์–ธํŠธ๊ฐ€ HTTP Request๋ฅผ ์ „์†กํ•˜๋ฉด, Servelet ์ปจํ…Œ์ด๋„ˆ(Tomcat ๋“ฑ)๊ฐ€ ์š”์ฒญ ์ˆ˜์‹ 
  2. Servelet ์ปจํ…Œ์ด๋„ˆ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์“ฐ๋ ˆ๋“œ Pool์„ ์œ ์ง€ํ•˜๊ณ  Pool์—์„œ ์“ฐ๋ ˆ๋“œ๋ฅผ ๊บผ๋‚ด์„œ ์š”์ฒญ์— ํ• ๋‹น
  3. Servlet ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์š”์ฒญ์„ Filter์— ์ „๋‹ฌ
  4. ๊ฐ Filter๋ฅผ ์ง€๋‚˜๋ฉด์„œ Filter์— ํ• ๋‹น๋œ ์ž‘์—…์ด Servelet Request/Response ๊ธฐ๋ฐ˜์œผ๋กœ ์ฒ˜๋ฆฌ
  5. ๋ชจ๋“  Filter ์ง€๋‚˜๋ฉด Spring Controller์— ์ „๋‹ฌ
  6. ์‘๋‹ต์€ ์š”์ฒญ์‹œ ๊ฑฐ์ณค๋˜ Filter๋“ค์„ ์—ญ์ˆœ์œผ๋กœ ์ง€๋‚˜์„œ Servelet ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ฐ˜ํ™˜
  7. Servelet ์ปจํ…Œ์ด๋„ˆ๋Š” ์‘๋‹ต์„ Serializeํ•˜์—ฌ HTTP Response๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜

FilterChainProxy > SecurityFilterChain > SecurityFilter

Spring Bean๊ณผ Servelet Filter๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” DelegatingFilterProxy๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”

Spring Security์˜ FilterChainProxy๊ฐ€ ์‚ดํŽด๋ด์•ผํ•  ๋ฉ”์ธ์ด๋‹ค

FilterChainProxy๋ฅผ ๊นŒ๋ณด๋ฉด, 

  • ํ”„๋กœ์ ํŠธ์—์„œ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ SecurityFilter ๋ชฉ๋ก์„ ๊ฐ€์ง
  • SecurityFilter ๋ชฉ๋ก์„ chain์œผ๋กœ ๋ฆฌ์ŠคํŠธ๋กœ ๊ฐ€์ ธ์„œ, ์š”์ฒญ์ด SecurityFilter๋“ค์„ ํ†ต๊ณผํ•˜๋„๋ก ๊ด€๋ฆฌ
  • ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปค์Šคํ…€ํ•œ ์ธ์ฆ SecurityFilter ๋นˆ์„ ํ†ต๊ณผํ•˜๋ฉด์„œ securityContext๊ฐ€ ์„ธํŒ…๋จ

๊ทธ๋ฆฌ๊ณ  Response๊ฐ€ ๋‚ด๋ ค๊ฐˆ ๋•Œ 

finally! context clear

securityContext๋ฅผ clearํ•ด์ฃผ๋ฉด์„œ

Thread ๋ฐ˜ํ™˜ํ•˜๊ธฐ์ „์— SecurityContext๊ฐ€ ์žˆ๋Š” ThreadLocal์ด ๊น”๋”ํ•ด์ง€๋Š” ๊ฒƒ์ด๋‹ค!

  • Servlet Filter๋ฅผ Spring Security๊ฐ€ SecurityContext ์ƒ์„ฑ/์†Œ๋ช… ์‹œํ‚ค๋Š”๋ฐ์— ์‚ฌ์šฉ
  • Servelet Request๋กœ ์ƒ์„ฑ๋œ ์“ฐ๋ ˆ๋“œ ๋‚ด์—์„œ๋Š” SecurityContext๋Š” Spring Security FilterChainProxy์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ -> ์†Œ๋ฉธ

 

๐Ÿ˜ฎ ๊ทธ๋ ‡๋‹ค๋ฉด @Async๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋Š”?

๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋Š” Servelet Request๋กœ ๋งŒ๋“ค์–ด์ ธ์„œ Filter๋ฅผ ํƒ€์ง€ ์•Š๋Š”

๊ฐœ๋ฐœ์ž๊ฐ€ ์ปค์Šคํ…€ํ•œ Thread Pool์—์„œ ๊ด€๋ฆฌ๋˜๋Š” Thread์ด๋‹ค

 

์ฆ‰, ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋Š” Servlet Request/Response๋„ ์•„๋‹ˆ๊ณ  Filter๋ฅผ ํƒ€๋Š” ๊ฒƒ๋„ ์•„๋‹ˆ๋‹ค

์“ฐ๋ ˆ๋“œ๋ฅผ Pool์— ๋ฐ˜ํ™˜์ „์— ์ž๋™์œผ๋กœ SecurityContext๋ฅผ clearํ•ด์ฃผ๋Š” FilterChainProxy ๋„ ์•ˆ ํƒ„๋‹ค๋Š” ๊ฒƒ์ด๋‹ค

 

๊ทธ๋ž˜์„œ 

  1. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-1 ์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ(์ง์›A) -> Pool์— ๋ฐ˜๋‚ฉ
  2. ์ง์›B ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-2 ์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ(์ง์›B) -> Pool์— ๋ฐ˜๋‚ฉ
  3. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-1 ์“ฐ๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ (์ง์›A) -> Pool์— ๋ฐ˜๋‚ฉ
  4. ์ง์›A ๊ณ„์ •์œผ๋กœ API ํ˜ธ์ถœ -> ๋น„๋™๊ธฐ @Async ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ -> AsyncExecutor-2 ์“ฐ๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ (์ง์›B) -> Pool์— ๋ฐ˜๋‚ฉ

4๋ฒˆ ํ˜ธ์ถœ ๋•Œ, ์“ฐ๋ ˆ๋“œ ์žฌ์‚ฌ์šฉ์ด๋ฏ€๋กœ SpringSecurity๋Š” SecurityContext๋ฅผ ๋„˜๊ฒจ์ฃผ์ง€ ์•Š์•˜๊ณ 

2๋ฒˆ์—์„œ ์“ฐ์ธ AsyncExecutor-2์— ๋‚จ์•„์žˆ๋Š” ์ง์›B๊ฐ€ ๊ทธ๋Œ€๋กœ ์“ฐ์ธ๊ฒƒ์ด๋‹ค

 

๊ทธ๋ ‡๋‹ค๋ฉด 3๋ฒˆ ํ˜ธ์ถœ๋•Œ๋„ ์‚ฌ์‹ค ์šด์ด ์ข‹์•„์„œ ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ๋ž‘ ์ž์‹์“ฐ๋ ˆ๋“œ์˜ SecurityContext๊ฐ€ ์ผ์น˜ํ•œ๊ฒƒ์ด๋‹ค

 

๐Ÿ˜ฎ  ์•„ ๊ทธ๋ž˜์„œ?? Local ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋ฏธ๋ฆฌ ๋ชป์žก์•˜๋‚˜?

Local ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋Š” ํ†ต์ƒ์ ์œผ๋กœ

  • ํ•œ์‚ฌ๋žŒ์˜ ๊ณ„์ •์œผ๋กœ๋งŒ ๊ฐ€์ง€๊ณ  API ํ…Œ์ŠคํŠธ
  • ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์— ๋ชจ๋‘ ๊ฐ™์€ ์‚ฌ๋žŒ์˜ SecurityContext๊ฐ€ ์ž”์—ฌ
  • ์–ด์ฐจํ”ผ ํ•œ์‚ฌ๋žŒ์˜ ๊ณ„์ •๋งŒ ์“ฐ๋ฏ€๋กœ ๋ถ€๋ชจ์“ฐ๋ ˆ๋“œ์™€ ์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ธ์ฆ์ •๋ณด๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ผ€์ด์Šค ๋ฐœ๊ฒฌ ๋ชปํ•จ

Dev ์„œ๋ฒ„๋กœ ๋ฐฐํฌํ›„์—๋Š”

  • ์—ฌ๋Ÿฌ ํ…Œ์Šคํ„ฐ๋“ค์ด ๋‹ค์ˆ˜์˜ ๊ณ„์ •์œผ๋กœ API ํ…Œ์ŠคํŠธํ•˜๋ฉด์„œ ์œ„์˜ ๋ฌธ์ œ๋กœ ์žฅ์•  ๋ฐœ์ƒ

๊ทธ๋ž˜์„œ Local ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ๋Š” ๋˜๊ณ , Dev ์„œ๋ฒ„์—์„œ๋Š” ์•ˆ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๊ฑด๋ฐ

์•Œ๊ณ  ๋ณด๋‹ˆ๊นŒ ํ˜„์ƒ์€ ๊ฐ™์•˜๊ณ , ์—ฌ๋Ÿฌ ๊ณ„์ •์„ ๊ณ ๋ คํ•˜๋ฉฐ Local์—์„œ ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๊ธด์ผ์ด์—ˆ๋‹ค

 


๐Ÿคฃ ์›์ธ ์ •๋ฆฌ

์›์ธ์„ ๊ทธ๋ž˜์„œ ๋‹ค์‹œ ์š”์•ฝํ•ด๋ณด์ž๋ฉด

๐Ÿ‘‰ ThreadPool์— ์กด์žฌํ•˜๋Š” Thread ๊ฐœ์ˆ˜๊ฐ€ Core Pool Size ๋ฏธ๋งŒ์ธ ๊ฒฝ์šฐ

  • Thread๊ฐ€ ์ง€์† `์ƒ์„ฑ`๋˜๋ฏ€๋กœ, Spring Security Mode ์„ค์ •์— ๋”ฐ๋ผ ๋ถ€๋ชจ SecurityContext ์ „๋‹ฌ
  • ์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์—์„œ ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์˜ ์ธ์ฆ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์ •์ƒ ๋™์ž‘
  • ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์ด๋ฏ€๋กœ, ServeletFilter๋ฅผ ํƒ€์ง€ ์•Š์Œ -> Thread ๋ฐ˜ํ™˜์‹œ์ ์— SecutiryContext๊ฐ€ clear๋˜์ง€ ์•Š์Œ

๐Ÿ‘‰ ThreadPool์— ์กด์žฌํ•˜๋Š” Thread ๊ฐœ์ˆ˜๊ฐ€ Core Pool Size๋ฅผ ๋‹ฌ์„ฑํ–ˆ๊ฑฐ๋‚˜, Max Pool Size ๋งŒํผ ์ปค์ง„ ๊ฒฝ์šฐ

  • ThreadPool ์ „๋žต/์ƒํ™ฉ์— ์˜ํ•ด Thread๊ฐ€ ์žฌ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Œ
  • Thread๊ฐ€ `์žฌ์‚ฌ์šฉ`๋˜๋ฏ€๋กœ, Spring Security Mode(`์ƒ์„ฑ`์‹œ์ ์— ์ ์šฉ)๊ฐ€ ์ ์šฉ์•ˆ๋˜์–ด ๋ถ€๋ชจ SecurityContext ์ „๋‹ฌ ์•ˆ๋จ
  • ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์ด๋ฏ€๋กœ, ServeletFilter๋ฅผ ํƒ€์ง€ ์•Š์Œ -> Thread ๋ฐ˜ํ™˜์‹œ์ ์— SecutiryContext๊ฐ€ clear๋˜์ง€ ์•Š์Œ -> ์ฒ˜์Œ ์“ฐ๋ ˆ๋“œ ์ƒ์„ฑ ์‹œ์˜ SecurityContext๊ฐ€ ๊ณ„์† ์ž”์—ฌ
  • ์ด์ „ ์‚ฌ์šฉ์—์„œ SecurityContext๊ฐ€ clear๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, ๋‚จ์•„์žˆ๋Š” SecurityContext์‚ฌ์šฉํ•˜์—ฌ ๋กœ์ง ์‹คํ–‰

๐Ÿ’ก ํ•ด๊ฒฐ์ฑ…

ํ•ด๊ฒฐํ•ด์•ผ๋  ์ง€์ ๋Š” ๋‘๊ฐ€์ง€์˜€๋‹ค

  • Thread Pool์—์„œ ๋น„๋™๊ธฐ Thread ์žฌ์‚ฌ์šฉ ์‹œ์— Spring Security๊ฐ€ SecurityContext ๋ชป๋„˜๊ฒจ์ฃผ๋Š” ์ง€์ 
  • ๋น„๋™๊ธฐ Thread๊ฐ€ Pool๋กœ ๋ฐ˜ํ™˜๋  ๋•Œ, ํ•ด๋‹น ์“ฐ๋ ˆ๋“œ์˜ ThreadLocal์ธ SecurityContext๊ฐ€ clear ์•ˆ๋˜๋Š” ์ง€์ 

 

์ผ๋‹จ ThreadPool์„ ๋งŒ๋“ค๊ณ  ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” Executor์— ์กฐ์น˜๋ฅผ ํ•ด์ค˜์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ์„œ์นญํ–ˆ๋”๋‹ˆ

๐Ÿ˜„ DelegatingSecurityContextExecutor ๋นˆ ๋“ฑ๋ก

Executor๋ฅผ wrappingํ•ด์„œ DelegatingSecurityContextExecutor๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์ฃผ๋ฉด

 

Proxy ์—ญํ• ์„ ํ•ด์„œ

  • Thread๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ์žฌ์‚ฌ์šฉ๋˜๋Š” ์‹œ์ ์— ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์˜ SecurityContext์™€ strategy๋ฅผ ์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์— ์„ธํŒ…
  • Thread๊ฐ€ Pool์— ๋ฐ˜ํ™˜๋˜๋Š” ์‹œ์ ์— ์“ฐ๋ ˆ๋“œ์˜ SecurityContext๋ฅผ clearํ•œ๋‹ค
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        int corePoolSize = Runtime.getRuntime().availableProcessors(); // CPU ์ฝ”์–ด ์ˆ˜์— ๋งž์ถฐ ์Šค๋ ˆ๋“œ ํ’€ ํฌ๊ธฐ ์„ค์ •
        int maxPoolSize = corePoolSize * 2; // ์ตœ๋Œ€ ํ’€ ํฌ๊ธฐ - CPU ์ฝ”์–ด ์ˆ˜์˜ ๋‘ ๋ฐฐ
        int queueCapacity = 500; // ํ ์šฉ๋Ÿ‰ ์„ค์ •

        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("AsyncExecutor-");

        ... ์ค‘๋žต

        executor.initialize();

        return new DelegatingSecurityContextExecutor(executor); // ์ˆ˜์ •!!
    }

}

 

 

๐Ÿ˜„ MODE_INHERITABLETHREADLOCAL -> MODE_THREADLOCAL ๋กœ ์ˆ˜์ •

DelegatingSecurityContextExecutor ๊ฐ€ Thread ์ƒ์„ฑ/์žฌํ™œ์šฉ ์‹œ์— SecurityContext ์ „๋‹ฌ์„ ๋ชจ๋‘ ์ปค๋ฒ„ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด,

SecurityContext strategy๋ฅผ ๊ตณ์ด MODE_INHERITALBETHREADLOCAL๋กœ ์„ค์ •ํ•˜์—ฌ ์ƒ์„ฑ ์‹œ์— SecurityContext ์ „๋‹ฌ์„ ์ค‘๋ณต์ฒ˜๋ฆฌ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค

 

๋” ๋‚˜์•„๊ฐ€ ๊ฐ์ฒด์˜ ์ฑ…์ž„ ์ž…์žฅ์—์„œ ๋ณธ๋‹ค๋ฉด, 

์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ž์‹ ์˜ SecurityContext์— ๋Œ€ํ•œ ๊ฒฐ์ •์„ ๋ถ€๋ชจ ์“ฐ๋ ˆ๋“œ์˜ Config๊ฐ€ ๊ฒฐ์ •ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ

์ž์‹ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ ์ž์‹ ์˜ Config ์ฆ‰, AsyncConfig์—์„œ ์ •ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ๋„, ๊ด€๋ฆฌ ํฌ์ธํŠธ ์ ์œผ๋กœ๋„ ๋” ์˜ณ์€๊ฑฐ ๊ฐ™๋‹ค

 

๊ทธ๋ž˜์„œ SecurityContext๊ฐ€ ์ƒ์„ฑ๋œ ์“ฐ๋ ˆ๋“œ ์•ˆ์—์„œ๋งŒ ์ „ํŒŒ ๋ฒ”์œ„๋ฅผ ํ•œ์ •ํ•˜๋Š” MODE_THREADLOCAL๋กœ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SpringSecurityConfig {

    @PostConstruct
    public void init() {
    	SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL); // ์ˆ˜์ •!!
    }
    ...์ƒ๋žต

}

 

๊ทธ๋Ÿฌํ•œ ์ด์œ ์—์„œ Spring Security๋Š” MODE_THREADLOCAL์„ default๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ์žˆ๋Š”๊ฑฐ ๊ฐ™๋‹ค

์ฆ‰, PostConstruct๋„ ์•ˆํ•ด๋„ ๋œ๋‹ค๋Š” ๊ฒƒ!

 


๐Ÿ‘ ๋งˆ์น˜๋ฉฐ

์ด๋ฒˆ Spring Security ์ด์Šˆ ํ•ด๊ฒฐ์„ ํ†ตํ•ด์„œ Filter, Thread Pool ๋™์ž‘์›๋ฆฌ / Servelet ์ปจํ…Œ์ด๋„ˆ์˜ ๊ธฐ์—ฌ ์‹œ์  ๋“ฑ์„ ์ถ”๊ฐ€๋กœ ์•Œ๊ฒŒ๋œ๊ฑฐ ๊ฐ™๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ๊ฒฐ๊ตญ Spring Security๋Š” ์ด๋Ÿฐ๊ฒƒ๋“ค ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ผ ๋ฟ

Low level์˜ ๊ธฐ๋ฐ˜๊ธฐ์ˆ ์— ๋Œ€ํ•ด์„œ ์ข€ ๋” ํ•™์Šตํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค

๋‹ค์Œ์—๋Š” Thread Pool์„ ๊ด€๋ฆฌํ•˜๋Š” Executor Service ๊ตฌ์กฐ์™€ ์›๋ฆฌ

Servlet์ด HTTP request๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€์— ์— ๋Œ€ํ•ด์„œ ์ข€๋” ๊นŠ๊ฒŒ ๊ณต๋ถ€ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค