RSS Reader 프로젝트를 진행하며 Session을 도입하는 과정에서 겪은 직렬화의 문제와 Redis의 활용, 도입에 대해 정리해 보고자 합니다.
RSS Raader는 벨로그, 티스토리, 깃허브 블로그 등 다양한 플랫폼의 블로그들을 구독으로 새로운 글을 알림을 받거나 즐겨 찾기 할 수 있는 서비스입니다.
저희는 Session 회원에 대한 정보를 저장하는 방식을 채택했는데요.
Spring Session과 Redis를 통한 Session 관리와 Redis의 다양한 활용 과정에서 고민과 해결을 소개하고자 합니다.
Scale Out시 세션과의 불일치와 세션 정보의 유연한 관리를 위해 Redis를 활용하였고 Spring Session을 도입했습니다.
기본적으로 스프링 세션은 세션 데이터를 서버의 메모리에 저장하는 대신 외부 스토리지에 저장하고 관리합니다.
yml 파일에spring.session.store-type을 지정해 주면 별도의 설정 없이 Spring Boot의 마법 같은 AutoConfiguration 으로 인해 @EnableRedisHttpSession 을 추가한 것과 같은 효과를 냅니다.
@SpringBootApplication
@EnableRedisHttpSession
public class RedisSessionExamApplication {
public static void main(String[] args) {
SpringApplication.run(RedisSessionExamApplication.class, args);
}
}
이러한 별도의 어노테이션이 필요 없어집니다. @EnableRedisHttpSession 의 역할로는
결론적으로 yml의 설정을 통해 Redis메모리를 이용해 세션을 저장하게 됩니다.
다음은 간단한 시퀀스 다이어 그램입니다.
sequenceDiagram
autonumber
actor A as client
participant B as 서버 A,B,C
participant C as 세션 서버(redis)
A->>B: 요청
B->>C: 세션 아이디 요청
C->>C: 세션 아이디 발행
C->>B: 세션 정보 응답(Member DTO)
B->>A: 응답

만약 각각의 서버 A, B, C가 각 독립적인 서버일 때 하나의 세션 서버에서 발급이 아닌 A,B,C가 발급한다면 세션 ID 생성 전략이 변경된다면 각 모듈은 전부 재배포 해야 하고, 확률은 매우 낮지만, 중복된 세션 아이디가 생성될 확률 또한 존재합니다.
하지만 시퀀스 다이어 그램과 같은 흐름을 따른다면 세션 아이디를 하나의 서버에서 관리에서 관리하기 때문에 유연한 시스템 설계를 할 수 있습니다.
Spring은 별도의 벤더를 지정하지 않아도 기본적으로 내장되어있는 Session과 Cache가 있습니다.
@EnableCaching 을 사용해 Cash를 사용할 수 있습니다.
학습하며 알게 된 사실인데 Cache처럼 Spring에서 지원해 주는 기본 SessionStorage는 없다고 합니다. 하지만 지금까지@SessionAttributes 를 활용하여 세션을 썼는데 말이지요 .스택 오버 플로우를 참고해 보면 톰캣에서는 "SESSIONS.ser" 파일에 session의 상태가 저장된다고 합니다.
What is the default Session Storage for Spring Boot?
프로젝트 진행 도중 Redis를 활용한 Session까지는 해결했지만 Session에 객체를 넣는 부분에서 팀원간 의견을 갈렸습니다.
Session에 도메인 객체 (저희 프로젝트에서는 Member class입니다.) Serializable인터페이스를 구현해준 후 그대로 넣자는 팀원의 의견과 Serializable인터페이스는 신중히 고려 해야 하기 때문에 보류하자 다른 방법을 찾아 보자 라는 제 의견이 나왔습니다.
막상 직렬화를 조심 하자라는 말은 어디선가 들어본거같은데 .. 구체적으로 왜 직렬화를 조심 해야하는지? 또 직렬화라는게 정확히 어떨 때 쓰는 것인지? 설명 할 수 없었고 이를 게기로 Redis와 Session 관리를 공부하며 같이 정리해 봤습니다.
일단 Spring session을 사용할 때 session.setAttribute(); 를 사용해 session에 객체를 저장합니다.
참고 setAttribute는 다음과 같은 형태를 가집니다
void* setAttribute(String name, Object valuevalue가 변수가 직렬화 가능하지 않다면. Non-serializable objects should not be stored in "HttpSession" objects 가 발생합니다.
즉 세션 변수가 직렬화할 수 있어야 합니다. 이러한 Config File을 만들어 주어 RedisTemplate<String, Object> 즉 Object를 value를 받도록합니다.
@Configuration
@RequiredArgsConstructor
public class RedisConfiguration {
----- 생략-------
@Bean
public RedisTemplate<String, Object> redisTemplate() {
final RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(lettuceConnectionFactory());
template.setDefaultSerializer(new StringRedisSerializer());
return template;
}
}
위와 같은 config 파일을 만들지 않더라도 yml에서 Spring이 지원하는 기본적인 Redis 설정만을 가지고도 잘 작동합니다.
spring:
session:
store-type: redis
redis:
namespace: spring:session
data:
redis:
host: localhost
port: 6379
지금까지 내용을 정리해 보면 Redis Session을 활용할 때 RedisTemplate을 사용하며 session.setAttribute() 하는 경우 객체를 넣어야 하고 ****이 객체는 직렬화(Serializable)가능 해야 합니다.
최종적으로 팀원들과 Member 도메인에 직접적으로 Serializable 을 거는 것이 아닌(추후 문제가 될 수 있기에) 세션에 들어갈 커스텀 값을 지정해 주는 새로운 객체를 만들어 Serializable을 implements 했습니다.
직렬화에 관한 더 많은 이야기
public record SessionMember(long id) implements Serializable {
public static SessionMember from(Member member) {
return new SessionMember(member.getId());
}
}