Http Session과 Session Clustering

남예준·2025년 10월 24일

HttpSession

HTTP 요청에는 상태가 없다.

  • 각각의 요청이 독립적으로 이뤄지며, 서버는 사용자가 보낸 몇 번째 요청인지에 대한 정보 같은 걸 저장되지 않는다는 의미
  • 다른 방향에서 해석하면, 사용자 브라우저 측에서 자신을 식별할 수 있는 정보를 서버에 요청할 때 마다 알려줘야 한다는 의미

이때 사용자의 브라우저에 응답을 보내면서, 브라우저에 특정 데이터를 저장하도록 전달할 수 있다. 이때 서버에서 작성해서 응답을 받은 브라우저가 저장하는 데이터쿠키라고 부른다.

서버는 응답을 보내면서 쿠키에 요청을 보낸 브라우저를 특정지을 값을 보내줄 수 있다. 이후 사용자가 요청을 보낼 때, 해당 값을 보내주면 서버에서 저장 해놓은 정보를 기반으로 브라우저에 로그인한 사용자가 누구인지를 구분할 수 있게 된다.

상태를 저장하지 않는 HTTP 통신을 사용하면서, 이전에 요청을 보낸 사용자를 기억하는 상태를 유지하는 것을 세션이라고 부른다.

Spring Boot를 사용하면, Spring Boot 프로젝트에 내장되어 있는 Tomcat 서버가 세션을 생성. 이때, 이 세션을 확인하고 싶다면, Chrome의 개발자 모드로 들어가, Application > Storage > Cookie를 확인

  • JSESSIONID라고 하는 쿠키값은, 서버 내부의 Tomcat이 처음 접근한 브라우저에게 발급하는 쿠키
  • Tomcat은 이 쿠키의 값을 바탕으로 세션을 관리하고, HttpSession 객체로 만든다. 그리고 Spring Boot의 컨트롤러 메서드에서 해당 HttpSession을 가져와 사용할 수 있다.
@RestController
public class SessionController {
    @GetMapping("/set")
    public String set(
            @RequestParam("q")
            String q,
            HttpSession session
    ) {
        session.setAttribute("q", q);
        return "Saved: " + q;
    }

    @GetMapping("/get")
    public String get(
            HttpSession session
    ) {
        return String.valueOf(session.getAttribute("q"));
    }
}

이런 식으로 사용되는 건 몇 번 해봐서 알긴 안다만

문제!

만약, 사용자가 늘어서 하나의 서버로는 사용자의 요청을 감당하기 어려워지면??

한 가지 대안은 똑같은 기능을 하는 Spring Boot 서버를 여러 개 사용하는 것(Scale-Out)

  • 서버 성능 업그레이드 같은 건 Scale-up

여러 서버가 동작한다면, 각 서버들에게 요청을 분산하여 부하를 줄일 수 있다. (Load Balancing)

💡

한 pc에서 여러 개의 서버를 돌리는 법

원래 알던 건 module로 만들어서 수행하는 법이었다.

강의에서 제공하는 내용은 다음과 같다.

  • Intellij 설정으로 포트 바꿔서 여러 서버 올리기 실행 버튼 옆에 실행할 설정을 클릭하면, Untitled Edit Configurations를 찾을 수 있습니다. Untitled 구분하기 쉽게 이 설정의 이름을 바꾸고, 복제 버튼을 누릅니다. Untitled 그럼 두번째 설정이 나오게 되는데, 이름을 8081로 바꿉니다. Untitled 그다음 오른쪽의 Modify options를 누르고, Add VM options를 선택합니다. Untitled Untitled 그럼 JVM에 전달할 설정을 정할 수 있는데, 여기에 -Dserver.port=8081를 작성하고, Apply → OK Untitled 그럼 서로다른 포트에 두개의 Spring Boot 프로젝트를 실행할 수 있습니다! Untitled Untitled

여튼 포트 번호를 바꿔가면서 세션에 정보를 저장, 회수를 진행해 보면, 회수를 다른 포트의 서버에서 하려고 할때 데이터를 정상적으로 찾을 수 없다.

결국 여러 서버를 가동하게 되면, 세션의 정보를 서버 내부에서 관리하기 어려워지며 이에 따라 적당한 대안을 마련해야 한다.

Sticky Session

특정 사람이 보낸 요청을 하나의 서버로 고정하는 방법

요청을 분산하는 로드밸런서를 통해 요청을 보낸 사용자를 기록, 해당 사용자가 다시 요청을 할 경우 최초로 요청이 전달된 서버로 요청을 전달하는 방식

  • 특정 서버로 보내진 사용자만 활발하게 활동한다면, 요청이 균등하게 분산되지 않는다
    • ex ) 9명의 사용자가 동일한 요청을 보내서 3대의 서버로 요청을 분산한다고 가정해 봅시다. 서버는 이들을 위해 세션을 발급합니다.
      근데 이중 A, C 서버로 요청이 연결된 사용자들은 서비스에 관심이 떨어져 금방 떠난 반면, B 서버의 사용자들은 활발하게 서비스를 사용한다고 가정하면?
      결과적으로는 다른 널널한 서버들에 비해 B 서버가 더 바쁘게 동작하게 되며, 이는 과부하로 이어질 수 있습니다. 여기에 만약 B 서버가 다운되게 된다면, 해당 서버에서 관리하는 세션도 다 사라지고, 사용자는 갑자기 자기 데이터가 사라지게 될 수 있습니다.

Session Clustering

sticky session의 문제점을 보완할 수 있는 방식

여러 서버들이 하나의 저장소를 공유하고, 해당 저장소에 세션에 대한 정보를 저장함으로서, 요청이 어느 서버로 전달이 되든 세션 정보가 유지

아무 사용자나 서버(의 로드밸런서)로 요청을 보낸다고 가정해했을 때 그러면 서버는 세션을 생성하되, 이 세션에 연결된 정보를 내부에 저장하는게 아닌 외부의 저장소에 저장

Sticky session의 문제를 완벽히 해결할 수 있다.

  • 서버 외부에 세션을 저장하는 것이므로, 관리 포인트가 늘어나게 되며, 통신 과정에서 어쩔 수 없는 지연이 발생 ⇒ 지연이 적은 Redis와 같은 인메모리 데이터베이스가 많이 사용
Sticky SessionSession Clustering
장점애플리케이션 입장에선 구현이 쉬움
외부와 통신할 필요가 없음Load Balancer에서 균등하게 요청 분산 가능
서버의 추가 제거가 비교적 자유로움
단점요청이 분산되지 않아 과부하 가능성 존재
한 서버가 다운되면 그 서버가 관리하는 세션도 삭제외부 저장소라는 관리 포인트 추가
외부와 통신하는 지연이 발생

Spring Session

Spring Boot와 Redis를 함께 사용하고 있다면, 매우 쉽게 Session Clustering을 적용 가능

'org.springframework.session:spring-session-data-redis'

Spring Session은 Spring의 하위 프로젝트 중 하나로, 사용자의 세션 정보를 다루는데 유용한 API를 제공하는데 그것에 Spring Session Data Redis를 추가하면, 내장 Tomcat의 세션 기능을 사용하지 않고 Redis에 별도로 세션을 저장할 수 있다.

더이상 Tomcat을 사용하지 않기 때문에 JSESSIONID 대신 SESSION이라는 새로운 쿠키를 사용하고 있는것을 확인

Default로는 자바 기반 직력화가 적용되고 있으며 만약 RedisTemplate을 사용할때 처럼 JSON을 비롯한 방식으로 직렬화를 하고 싶다면, springSessionDefaultRedisSerializer Bean을 등록

@Configuration
public class RedisConfig {
    // ...
    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return RedisSerializer.json();
    }
}

이렇게 되면 읽을 수 있는 형태로 Redis에 저장되기는 하지만, Spring Security의 일부인 SecurityContext와 같은 객체가 Redis에 저장될 경우, SecurityContext의 기본 생성자가 없기 때문에 오류가 발생

⇒ 굳이 redis에서 읽어야 하나 싶다. 그냥 Java에서 역직렬화 해서 읽도록 하자.

⚠️ `SecurityContext` 객체의 직렬화, 역직렬화의 경우 본 강의의 범주를 벗어나기 때문에, 지금 단계에서는 해당 오류가 발생할 수 있다는 점만 염두에 두도록 하겠습니다. 차후 공부를 통해 직접 `RedisSerializer`를 구성하거나, 직렬화가 쉽게 되는 `SecurityContext`를 만드는 것을 연구해 보세요!

그렇다고 한다.

0개의 댓글