Spring Session과 Redis 활용(부제: 직렬화)

이경환·2024년 1월 7일

프로젝트_RSS Reader

목록 보기
1/3

개요

Rss 프로젝트 링크

RSS Reader 프로젝트를 진행하며 Session을 도입하는 과정에서 겪은 직렬화의 문제와 Redis의 활용, 도입에 대해 정리해 보고자 합니다.

RSS Raader는 벨로그, 티스토리, 깃허브 블로그 등 다양한 플랫폼의 블로그들을 구독으로 새로운 글을 알림을 받거나 즐겨 찾기 할 수 있는 서비스입니다.

저희는 Session 회원에 대한 정보를 저장하는 방식을 채택했는데요.

도입 배경

  • 서버가 하나일 때는 문제가 없지만 다중 서버일 때 서버 간 세션 불일치 문제가 발생할 수 있다는 생각이 들어 글로벌한 세션 관리가 필요하다는 의견이 나왔습니다.

Spring Session과 Redis를 통한 Session 관리와 Redis의 다양한 활용 과정에서 고민과 해결을 소개하고자 합니다.

Scale Out시 세션과의 불일치와 세션 정보의 유연한 관리를 위해 Redis를 활용하였고 Spring Session을 도입했습니다.

Spring Session과 Redis

기본적으로 스프링 세션은 세션 데이터를 서버의 메모리에 저장하는 대신 외부 스토리지에 저장하고 관리합니다.

yml 파일에spring.session.store-type을 지정해 주면 별도의 설정 없이 Spring Boot의 마법 같은 AutoConfiguration 으로 인해 @EnableRedisHttpSession 을 추가한 것과 같은 효과를 냅니다.

  • Spring Session은 Redis를 통해 세션 정보를 유연하게 관리 할 수 있고 통해 세션 저장소의 내용을 변경해야 할 경우도 유연하게 변경이 가능합니다.
@SpringBootApplication
@EnableRedisHttpSession
public class RedisSessionExamApplication {

	public static void main(String[] args) {
		SpringApplication.run(RedisSessionExamApplication.class, args);
	}

}

이러한 별도의 어노테이션이 필요 없어집니다. @EnableRedisHttpSession 의 역할로는

  • Redis 가 지원하는 분산 세션을 사용할 수 있게 해줍니다.
  • 어플리케이션 메모리 대신 Redis 메모리를 사용하게 되어 결론적으로 수평적 확장에 유용하고 모든 인스턴스에서 데이터가 일관되게 유지됩니다.

결론적으로 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 value value가 변수가 직렬화 가능하지 않다면. 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());
    }
}
profile
개선하는 개발자, 이경환입니다

0개의 댓글