[Spring Boot] Redis & Spring Session 연동 + Spring Session Redis \xac\xed\x00\x05sr\x00\x0ejava.lang.Long 해결법

정훈스·2022년 8월 1일
9
post-thumbnail

Spring Session Redis 설정 건너뛰고
Spring Session Redis \xac\xed\x00\x05sr\x00\x0ejava.lang.Long 해결법은 2.7로

1. 인증

Web또는 API 서버에서 요청을 보내는자의 신분을 인증할 수 있는 장치가 없다면
Anonymous한 모든 유저가 서비스 접근 권한을 얻게되어 엄청난 일을 경험할 수 있다.
그렇기 때문에 헤더나 쿠키에 인증정보를 담아서 보내고 서버는 이것을 인증하고 권한을 부여한다.
이 때 보통 세션, JWT 토큰을 사용한다.

1.1 JWT 토큰

JSON Web Token의 약자로 대칭키 방식으로 암호화 된 신분증이라고 생각하면 편하다.
이 방식은 대칭키의 비밀키가 유출되었을 때 굉장히 취약한 방식으로 이를 방지하기 위한
복잡한 로직이 필요하다. 고려 사항이 많고 구현이 어렵다.
오늘은 세션이 메인이므로 자세한 건 구글에게 물어보도록 하는걸로 하고 생략하겠다.

1.2 세션

세션이란 말 그대로 유저가 인증요청을 했을 때 서버에서 세션을 생성하고
유저의 인증에 대한 상태관리를 하는 구조를 말한다.
서버에서 유저의 인증에 대한 상태관리를 하기 때문에
Stateless한 JWT와는 다르게 Stateful한 방법이다.

장점으로는 구현이 어렵지 않고, 특정 상황에서 세션을 직접 핸들링할 수 있기 때문에
상황 대처가 더 용이하다. JWT는 탈취되면 기도메타

2. 스프링 부트 세션 + Redis

2.1 Redis

Redis(레디스)Key-Value형태비관계형 데이터베이스다.
속도가 매우 빠른 장점이 있어, 캐시나 세션 저장소로 많이 사용한다.
오늘은 이 Redis를 스프링 부트와 함께 세션 저장소로 사용한 경험을 공유해보려고 한다.

나는 ubuntu서버를 사용하는데
$ sudo apt-get install redis-sever
를 통해 간단히 설치 가능하다.

꼭 비밀번호를 설정해준다
안해주면 바로 크롤러(?)같은 무언가가 들어와서 활개친다.
설정법은 구글링

2.2 스프링 세션

앞서 말한 세션을 생성하고, 관리하는 기능을 스프링에서 제공한다.
Spring Session이다.
스프링 세션을 설정하면 Request에 대해서 세션을
(반)자동으로 생성하고 관리, 및 업데이트 해준다.
자동으로 관리해 주지만 설정을 해줘야 하기 때문.
같은 세션을 expire전에 접속하면 expire기한이 자동으로 늘어난다.

2.3 Spring boot redis 설정

우선 나는 개발서버의 Spring Boot를 사용해 만들어진 API에 대해
세션을 적용해 보았고, 마찬가지로 개발서버에 redis를 설치했다.
나는 gradle을 사용해서 build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-data-redis'

을 추가해 주었다.
그 후
application.yml의 로컬 개발 프로필에

spring:
  profiles: local
  redis:
    host: XX.XX.XXX.XX
    port: 6379
    password: XXXXXXXXXX

host -> 레디스 서버 주소, 같은 서버라면 localhost
port -> 레디스의 기본 포트는 6379, 실제 서버에 적용시 바꿔준다.
password -> 레디스에 설정한 비밀번호

2.4 Config 파일 생성

@NoArgsConstructor
@Configuration
@EnableRedisHttpSession()
public class RedisConfig{

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

    @Value("${spring.redis.password}")
    private String redisPassword;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisHost);
        redisStandaloneConfiguration.setPort(redisPort);
        redisStandaloneConfiguration.setPassword(redisPassword);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }
    
    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();

        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());

        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
        stringRedisTemplate.setDefaultSerializer(new StringRedisSerializer());
        stringRedisTemplate.afterPropertiesSet();
        return stringRedisTemplate;
    }

}

@EnableRedisHttpSession()은 메인 Application에 해줘도 무방하다.
위의 코드가 다들 기본적으로 Spring Session에 Redis를 사용하는 코드다.

2.4.1 Redis-server 연결 설정

 	@Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

    @Value("${spring.redis.password}")
    private String redisPassword;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisHost);
        redisStandaloneConfiguration.setPort(redisPort);
        redisStandaloneConfiguration.setPassword(redisPassword);
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

아까 application.yml에서 설정해준 redis-server에 관한 설정이다.
이를 통해 Spring Session이 쓸 redis-server를 연결한다.
어플리케이션 실행 시 redis연결 관련 에러라면 여기를 살펴본다.

2.4.2 Redis Serializer설정

    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();

        stringRedisTemplate.setConnectionFactory(redisConnectionFactory());

        stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        stringRedisTemplate.setDefaultSerializer(new StringRedisSerializer());
        stringRedisTemplate.afterPropertiesSet();
        return stringRedisTemplate;
    }

Redis에 데이터를 작성할 때는 룬문자(아닙니다)를 사용하는데
이것을 일반적인 형태로 바꿔주려면 serializer를 다른 걸로 설정해줘야한다.

2.5 Controller에 세션 설정 적용

    @CrossOrigin
    @PostMapping("/session/test")
    public int sessionTest(HttpSession httpSession){
        int number = 123;
        httpSession.setAttribute(number);
        return number;
    }

여기서 파라미터로 HttpSession을 넣어주면 아무것도 해주지 않아도
위에서 설정한 Redis Config에 의해
자동으로 Redis에 세션이 생성된다.

2.6 데이터 확인

실제 redis서버에 가서 redis-cli를 통해 데이터를 조회해본다.

데이터 조회 방법
1. redis 설치 서버 접속
2. redis-cli
3. auth "내가 설정한 비밀번호"
4. keys *
(Redis에 저장된 모든 데이터 보기) (처음 가면 String,hashMap 등 4가지 타입의 데이터가 있을텐데 자동 생성되는 데이터들이다.)
5. hgetall "spring:session:sessions:b76ff8d4-3f20-4179-b152-30de7d70190a"
( 4개 중에 이렇게 spring:session:sessions로 시작하는 데이터가 hashMap이고 이 안에 내가 custom하게 넣어준 데이터가 들어간다.)

1) "lastAccessedTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82Yd^~"
3) "creationTime"
4) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01\x82Yd^~"
5) "sessionAttr:number"
6) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x13\x8f"
7) "maxInactiveInterval"
8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00v\xa7\x00"

근데 갑자기 룬문자를 발견한다.
중간에 sessionAttr:number는 내가 custom하게 저장한 값이다.
보다싶이 숫자가 java.lang.Number로 나온다. 나는 분명 int형 숫자를 저장했다.

여기서 꿀팁
Spring Session Redis \xac\xed\x00\x05sr\x00\x0ejava.lang.Long 해결법

물론 나만 몰랐을 수도 있는데 이렇게 설정하고
구글 한테 아무리 물어봐도 이렇게 나와있다.
스프링 부트에서 직접 읽어도 룬문자로 보인다.

대체 왜???????????

2.7 *해결법

많이 헤맸다. 아까 설정해준 RedisConfig 파일에

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }

을 추가해야한다. 이건 왜 아무도 안알려줘
아까 우리가 설정해준 serializer는 우리가 직접 데이터를 읽고 쓸 때의 설정을 해 준 것이고
이름만 봐도 Spring Boot가 세션 생성할 때 사용할 것 같은
springSessionDefaultRedisSerializer가 자동으로 생성되는 Session의 Seriealizer였던 것이다.
이걸 추가해주면 정상적으로 데이터가 쓰여지고 읽을 수 있는걸 확인할 수 있다.

profile
MACHUDA. Inc CTO 입니다.

0개의 댓글