본문 바로가기
Spring

Spring - Spring Redis Session 적용기

by 오늘부터개발시작 2023. 3. 31.

문제점

담당하고 있는 프로젝트는 세션을 이용하여 인증을 처리하고 있다. 어드민 툴이라서 배포가 잦은 편인데, 배포로 인해 Pod가 재시작되면 세션 정보가 초기화 돼서 모든 사용자의 로그인이 모두 풀린다는 문제가 있었다. 그래서 배포를 할 때 사용자가 최대한 없는 시간을 활용하거나, 업무 시간 이후에 하는 등, 배포 시간에 많은 신경을 써야 했다. 이 문제를 해결하기 위해 Redis 세션을 적용하게 되었다. 세션이 스프링 Pod이 아닌 Redis에 저장되기 때문에, Pod가 재시작되어도 세션이 휘발되지 않게 된다.

Redis Pod 배포

먼저 사용할 Redis를 K8S에 배포했다. 어드민 툴의 로그인 용도로만 사용되기 때문에, PVC를 활용해서 단일 Pod로 배포하기로 결정하였다. Redis 이미지 버전은 배포 당시 최신 버전인 7.0.8을 사용하였고, K8S Secret을 활용하여 비밀번호 설정을 추가하였다. Helm 차트를 활용하였고 간단한 yaml 파일은 다음과 같다.

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
  labels:
    app: redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
        - name: redis
          image: redis:7.0.8
          imagePullPolicy: IfNotPresent
          command: ["redis-server", "--requirepass", "$(password)"]
          volumeMounts:
            - name: data
              mountPath: /data
          env:
              - name: password
                valueFrom:
                  secretKeyRef:
                    key: password
                    name: redis-secret
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: redis-pvc
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: redis-pvc
  labels:
    app: redis
spec:
  storageClassName: general
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
      
---
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
  labels:
    app: redis
spec:
  selector:
    app: redis
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 6379
      targetPort: 6379

---
apiVersion: v1
kind: Secret
metadata:
  name: redis-secret
type: Opaque
data:
  password: {base64비밀번호}

 

Spring 설정

다음 차례는 Spring과 Redis 연동이다. Spring에서 라이브러리를 제공해 줘서 간단하게 설정할 수 있다. 

라이브러리는 spring-boot-starter-data-redis와 spring-session-data-redis를 maven 혹은 gradle로 설치할 수 있다. 그다음, 라이브러리를 활용해서 다음과 같은 Redis Configuration 클래스를 만들어주면 된다. 

 

@Configuration
@EnableSpringHttpSession
public class RedisConfig extends AbstractHttpSessionApplicationInitializer {
    private String host = "localhost";
    private int port = 6379;
    private String password = "pwd";

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(host, port);
        redisConfig.setPassword(password);
        return new LettuceConnectionFactory(redisConfig);
    }

    @Bean
    public RedisOperations<String, Object> sessionRedisOperations() {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory());
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        return template;
    }
    
    @Bean
    public RedisSessionRepository sessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
        RedisSessionRepository redisSessionRepository = new RedisSessionRepository(sessionRedisOperations);

        redisSessionRepository.setDefaultMaxInactiveInterval(Duration.ofSeconds(sessionExpireTime));
        return redisSessionRepository;
    }

}

 

위에서 사용된 LettuceConnectionFactory는 Spring과 Redis가 통신하는 데 사용되는 라이브러리이다. 구버전의 스프링에서는 Jedis라는 라이브러리를 디폴트로 사용했지만, 훨씬 더 좋은 성능의 Lettuce라는 새로운 라이브러리가 디폴트로 바뀌게 되었다. 

그 외 설정은 Redis 엔드포인트, 접근 비밀번호 설정, Serialization 설정 및 Redis 키 값의 만료 시간 설정이다. 

 

결론

로그인을 하면 세션 정보가 Redis에 저장되기 때문에, 배포를 위해 Pod가 재시작되어도 더 이상 사용자들의 로그인은 끊어지지 않게 되었고, 시간에 상관없이 배포를 진행할 수 있게 되었다.