๋ณธ ๊ธ€์€ ๋ ˆ๋””์Šค ๊ด€๋ จ ๊ฒฐํ•จ์„ ๊ฒฝํ—˜ํ•œ ๊ฒƒ์„ ํ† ๋Œ€๋กœ ์Šคํ”„๋ง ์„ธ์…˜ ๋ ˆ๋””์Šค์— ๋Œ€ํ•œ ๋™์ž‘์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ž‘์„ฑํ•œ ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

๋งŽ์€ ์ž๋ฐ” ๊ฐœ๋ฐœ์ž๊ฐ€ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ณ„๋‹ค๋ฅธ ์ฝ”๋“œ ๊ตฌํ˜„ ์—†์ด๋„ ์—ฌ๋Ÿฌ ๊ฐœ๋ฐœ์ž๋“ค์— ์˜ํ•ด ์ž‘์„ฑ๋˜์–ด์ง„ ๋กœ์ง์„ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด์„œ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ๊ณผ ๋™์ž‘์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ์šฉ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์ด ํฌ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ž๋ฐ” ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ์–ธ์–ด์˜ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋„ ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋“ค์„ ์ „๋ถ€ ํ™•์ธํ•˜๊ณ  ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์„ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์„ํ…๋ฐ์š”. ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ตฌํ˜„ ๋ฒ”์œ„๊ฐ€ ์ƒ๋‹นํžˆ ๋งŽ๋‹ค๋ณด๋‹ˆ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฐœ๋… ํ˜น์€ ๋™์ž‘์— ๋Œ€ํ•ด์„œ ๋Œ€์ถฉ ์ดํ•ดํ•˜๊ณ  ๋„˜์–ด๊ฐ€๊ฑฐ๋‚˜ ์•„๋Š” ์„ ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.

์Šคํ”„๋ง ์„ธ์…˜ ๋ ˆ๋””์Šค์— ์˜ํ•œ ๊ฒฐํ•จ์„ ๋งŒ๋“ค์–ด๋‚ธ ์ด์œ ๋„ RequestContextHolder๋ฅผ ํ†ตํ•ด์„œ ์Šค๋ ˆ๋“œ ๋กœ์ปฌ์— ์ €์žฅ๋œ ์š”์ฒญ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ณ  RequestContextHolder๋กœ ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ RequestAttributes์— ์„ธ์…˜ ์•„์ด๋””๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๊ฐ€ ์žˆ๊ธฐ์— ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒƒ์œผ๋กœ๋ถ€ํ„ฐ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ๊ฐœ์ธ์ ์ธ ๊ฒฝํ—˜์œผ๋กœ ๋ณผ๋•Œ๋Š” ์Šคํ”„๋ง ์„ธ์…˜๊ณผ ๋ ˆ๋””์Šค๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋‹จ์ˆœํ•˜๊ฒŒ ์Šคํ”„๋ง ์„ธ์…˜ ๋ ˆ๋””์Šค์— ๋Œ€ํ•œ ์˜์กด์„ฑ๋งŒ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ ˆ๋””์Šค์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด ๊ทธ๋ฆฌ๊ณ  ์Šคํ”„๋ง ์„ธ์…˜์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ ๋ฟ์ด๋ฏ€๋กœ ๋‚ด์žฌ๋œ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ œ๋Œ€๋กœ ํ™•์ธํ•  ํ•„์š”์„ฑ์€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

TCP์™€ HTTP์˜ ์„ธ์…˜์€ ๋‹ค๋ฅด๋‹ค.

Spring Session provides transparent integration with HttpSession. This means that developers can switch the HttpSession implementation out with an implementation that is backed by Spring Session.

TCP์—์„œ์˜ ์„ธ์…˜์€ ์—ฐ๊ฒฐ์„ ์˜๋ฏธํ•˜์ง€๋งŒ HTTP์—์„œ์˜ ์„ธ์…˜์€ ์—ฐ๊ฒฐ์— ๋Œ€ํ•œ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง ์„ธ์…˜์€ TCP ๋ ˆ๋ฒจ์ด ์•„๋‹Œ HTTP ์„ธ์…˜์— ๋Œ€ํ•œ ํ†ตํ•ฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์„ธ์…˜์„ ์ €์žฅํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์„ JDBC ๊ธฐ๋ฐ˜์œผ๋กœ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•œ๋‹ค๊ฑฐ๋‚˜ ๋ ˆ๋””์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„ธ์…˜ ์ €์žฅ์†Œ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

WAS๋Š” ์„ธ์…˜ ๊ด€๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.

ํ†ฐ์บฃ์ด๋‚˜ ์–ธ๋”ํ† ์šฐ์™€ ๊ฐ™์€ WAS์—์„œ๋„ ์ž์ฒด์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋ฐ˜์˜ ์„ธ์…˜์„ ์ง€์›ํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง ์„ธ์…˜์—์„œ๋Š” ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ(WAS)๊ฐ€ ์ž์ฒด์ ์ธ ์„ธ์…˜์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๋„๋ก AbstractHttpSessionApplicationInitializer๋ฅผ ํ†ตํ•ด springSessionRepositoryFilter ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ํŠน์ˆ˜ํ•œ ํ•„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ๋ชจ๋“  ์š”์ฒญ์— ๋Œ€ํ•ด์„œ ์ฒ˜๋ฆฌ๋˜๋„๋ก ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค.

Fortunately, both HttpSession and HttpServletRequest (the API for obtaining an HttpSession) are both interfaces. This means that we can provide our own implementations for each of these APIs.
This highlights why it is important that Spring Sessionโ€™s SessionRepositoryFilter be placed before anything that interacts with the HttpSession.

์Šคํ”„๋ง ์„ธ์…˜์€ ์ž์ฒด ๊ตฌํ˜„ ์„ธ์…˜์œผ๋กœ ์ „ํ™˜ํ•œ๋‹ค.

SessionRepositoryFilter๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ์ค‘์š”ํ•œ ์—ญํ• ์€ ์ž๋ฐ” ์„œ๋ธ”๋ฆฟ ์ŠคํŽ™์˜ HTTP ์„ธ์…˜์„ ์ž์ฒด์ ์ธ ์„ธ์…˜ ํด๋ž˜์Šค๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚ด๋ถ€์ ์œผ๋กœ HTTP ์„ธ์…˜๊ณผ ์Šคํ”„๋ง ์„ธ์…˜์„ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฟ ํ‚ค ๊ธฐ๋ฐ˜์˜ HttpSessionIdResolver๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋˜์–ด์žˆ์ฃ . ๊ฒฐ๊ตญ SessionRepository ๊ตฌํ˜„์ฒด์— ๋”ฐ๋ผ JDBC ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š”์ง€ ๋ ˆ๋””์Šค์— ์ €์žฅํ•˜๋Š”์ง€ ๊ตฌ๋ถ„๋˜์–ด์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Switches the HttpSession implementation to be backed by a Session. The SessionRepositoryFilter wraps the HttpServletRequest and overrides the methods to get an HttpSession to be backed by a Session returned by the SessionRepository.

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
            response);

    try {
        filterChain.doFilter(wrappedRequest, wrappedResponse);
    }
    finally {
        wrappedRequest.commitSession();
    }
}

SessionRepositoryFilter์˜ ์œ„ ๊ตฌํ˜„์ฒ˜๋Ÿผ ๊ฐ€์žฅ ๋จผ์ € ์ฒ˜๋ฆฌ๋จ์œผ๋กœ์จ ์š”์ฒญ ์Šค๋ ˆ๋“œ ๋‚ด๋ถ€์—์„œ ๋ณ€๊ฒฝ๋˜์–ด์ง„ ์„ธ์…˜์— ๋Œ€ํ•ด์„œ๋Š” ์‘๋‹ต์ด ์™„๋ฃŒ๋œ ์ดํ›„์— ์ตœ์ข…์ ์œผ๋กœ ๋ฐ˜์˜๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฒฝํ—˜ํ–ˆ๋˜ ๋ ˆ๋””์Šค ๊ฒฐํ•จ์—์„œ๋„ ์„œ๋น„์Šค ํ˜น์€ ํผ์‹œ์Šคํ„ด์Šค ๋ ˆ์ด์–ด์—์„œ ์„ธ์…˜์ด ์ƒ์„ฑ๋˜๋”๋ผ๋„ ์„ธ์…˜ ์ •๋ณด๋ฅผ ๋ ˆ๋””์Šค์— ์ €์žฅํ•˜๊ฒŒ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ ˆ๋””์Šค์— ์ €์žฅ๋˜๋Š” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์Šคํ”„๋ง ์„ธ์…˜ ๊ณต์‹ ๋ฌธ์„œ์˜ Storage Details์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง ์„ธ์…˜ ๋ ˆ๋””์Šค๋Š” ์Šค์ผ€์ค„๋ง์„ ํ†ตํ•ด ๋งŒ๋ฃŒ๋œ ํ‚ค๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

SessionCleanupConfiguration์—์„œ RedisSessionExpirationPolicy์˜ cleanExpiredSessions ํ•จ์ˆ˜๋ฅผ ์Šค์ผ€์ค„๋Ÿฌ์— ๋“ฑ๋กํ•˜์—ฌ ์Šคํ”„๋ง์—์„œ ์ž์ฒด์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ์Šค์ผ€์ค„๋ง์— ์˜ํ•ด ๋งŒ๋ฃŒ๋œ ํ‚ค๊ฐ€ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ, ๋ ˆ๋””์Šค์˜ ๋งŒ๋ฃŒ ์ด๋ฒคํŠธ์˜ ํƒ€์ด๋ฐ ๋ฌธ์ œ๋กœ ์ธํ•ด์„œ TTL์ด ๋งŒ๋ฃŒ๋œ ์ดํ›„์— ๋ ˆ๋””์Šค๊ฐ€ ์•Œ์•„์„œ ์‚ญ์ œํ•˜๋„๋ก ์Šคํ”„๋ง ์„ธ์…˜ ๋ ˆ๋””์Šค์—์„œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํ‚ค๋ฅผ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœํžˆ ์กฐํšŒ(์•ก์„ธ์Šค)ํ•ฉ๋‹ˆ๋‹ค.

We do not explicitly delete the keys, since, in some instances, there may be a race condition that incorrectly identifies a key as expired when it is not. Short of using distributed locks (which would kill our performance), there is no way to ensure the consistency of the expiration mapping. By simply accessing the key, we ensure that the key is only removed if the TTL on that key is expired.