Project/Study

Spring Session + Redis

by somida 2021. 7. 19.

Redis

key-value 형태로 비정형 데이터를 저장하는 메모리 기반 DB

단순한 key-value 구조이기 때문에 많은 저장 메모리 용량을 요구하지 않고 빠른 속도가 중요하므로 세션을 저장하기에 가장 알맞는 DB

RDBMS처럼 중요한 데이터를 다루기 보다는 Cache성의 데이터를 처리하는 store

 

Redis 설치(mac OS)

Brew 설치

$ brew install redis

 

Redis 시작

# Redis 시작
$ brew services start redis

# Redis 정지
$ brew services stop redis

# Redis 재시작
$ brew services restart redis

 

Redis 실행 및 종료

# Redis 실행
$ redis-cli

# Redis 종료
$ shutdown

 

Redis 명령어

# 저장
> set [key이름] [value 값]

# 조회
> keys *

# key에 대한 value 조회
> get [key이름]

# 삭제
> del [key이름]

# 수정
> rename [key이름] [변경할 key이름]

 

Redis Cluster

아직..

Cluster
각각의 다른 서버를 하나로 묶어 하나의 시스템처럼 동작하게 함으로써 클라이언트에게 고가용성을 제공하는 것
- 여러대의 서버에 데이터가 분산되어 저장되므로 트래픽이 분산되는 효과
- 특정 서버에 장애가 발생해도 백업 서버의 보안을 통해 데이터 유실 없이 서비스 가능

 

Session Clustering

두 대 이상의 WAS를 이용하는 경우 로드밸런싱(대용량 트래픽 분산시키는 것) 또는 Failover(장애 발생시 예비시스템으로 자동 전환, 서버 이중화), Auto Scaling(AWS에서 EC2 인스턴스 자동 생성, 삭제해주는 서비스) 등의 대체된 WAS에게도 동일한 세션을 공유하는 기술

 

Redis, Infinispan과 같은 별도의 Session Server를 두는 방법도 있음

 

Cluster 고려할 점

세션 정보 저장 영역을 늘리기 위해서는 JVM의 Heap 사이즈를 늘릴 수 있지만, 너무 늘리면 Full GC 처리 시간으로 인해 장애요인 발생

 

Cluster 특징

  • 노드 장애 시 다른 노드에서 클라이언트와 통신해 서비스 제공(ex. Multicast 통신)
  • 부하 분산(LB)을 통해 성능 저하 방지, 다수의 서버에서 처리가능(고가용성)
  • 자동 장애 복구(정상적인 서비스 보장) (ex. 서버 이중화)
  • 단점으로는 관리의 어려움과 프로그램의 병렬화의 어려움

Cluster 종류

  • 계산용 클러스터(HPC)
  • 부하분산 클러스터(LVS)
  • 고가용성 클러스터(HA)
로드밸런싱과 세션 클러스터링 차이점
- 로드밸런싱 : 클러스터링된 WAS의 한 쪽에 부담이 가지 않게 하기 위해 분산시켜주는 역할
- 세션 클러스터링 : 분산시켜주는 의미보단 한 쪽의 장애를 방지하기 위해 임시방편의 역할

 

Session으로 유저 관리

Spring Security 대신 HttpSession을 이용해서 로그인한 유저의 정보를 관리하려고 한다.

사용자가 로그인을 하게 되면 서버에서 해당 정보를 모두 가지고 있고 특정 작업을 수행하려고 하면 HttpSession을 통해서 User 인스턴스를 받아오는 과정

 

HttpSession

HttpSession을 이용해서 처음 로그인 시에 사용자 세션을 저장해 두었다가 필요할 때마다 꺼내서 사용

 

 

 

Spring Boot 개발

Local Redis Test

application.yml

spring:
  redis:
    host: 127.0.0.1
    port: 6379
  session:
    store-type: redis
    redis:
      flush-mode: on_save

 

UserController.java

@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @PostMapping("/testRedis")
    public void GetRedis(String key) {
        ValueOperations<String, String> stringValueOperations = stringRedisTemplate.opsForValue();
        System.out.println("key : " + key);
        System.out.println("val : " + stringValueOperations.get(key));
    }
}

 

결과

key, value가 잘 나오는 것을 확인할 수 있다.

 

Spring Boot 진행

build.gradle

  • spring-boot-starter-data-redis : spring boot data redis를 연동하는 dependency
  • spring-session-data-redis : redis에서 session을 관리할 수 있게 해주는 dependency
    • springboot main 클래스에 @EnableRedisHttpSession을 추가하면 끝!
    • @EnableRedisHttpSession : Filter를 구현한 SpringSessionRepositoryFilter를 Bean으로 생성해주고, Spring Session이 지원하는 HttpSession 구현체를 대체하는 역할 (여기서는 Redis가 Spring Session 지원)
    • Java Redis Client에는 Jedis, lettuce가 있는데 그 중 Spring Boot 2.0 부터는 lettuce가 default로 설정되어 있다. 그래서 별도로 모듈을 추가하지 않아도 spring-boot-starter-data-redis을 통해 Lettuce 관련 모듈을 사용할 수 있다.
Lettuce와 Jedis
Lettuce는 Netty(비동기 이벤트 기반 고성능 네트워크 프레임워크) 기반의 Redis 클라이언트
Jedis에 비해 성능이 뛰어나고 하드웨어 자원 절약도 가능하다.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-redis'
	implementation 'org.springframework.session:spring-session-data-redis'
}

 

application.yml

  • session:store-type: redis로 설정
  • session:redis:flush-mode : on-save(호출될 때만 redis에 기록, default), immediate(즉시 redis에 기록)
  • session:redis:namespace : session저장하는 key값이 spring:session(default)
spring:
  redis:
    host: 호스트
    port: 포트번호
    password: 비밀번호
  session:
    store-type: redis
    redis:
      flush-mode: on-save
      namespace : spring:session

 

UserController.java

  • 회원 가입은 세션과 관계없으니 코드는 생략
  • 로그인 - Session 저장(Redis 창 해석)
    • session.setAttribute("key이름", 저장할 value)를 통해서 세션에 저장
  • 로그아웃
    • session.removeAttribute("key이름")으로 세션에서 제거
더보기

Redis 창 해석

> keys *

# 결과
1) "spring:session:sessions:~"
2) "spring:session:expirations:~"
3) "spring:session:sessions:expires:~"

Key의 의미

  • spring:session:sessions:[session id]
    • hash로 저장된 데이터
    • 세션 생성 시간, 마지막 세션 조회 시간, 최대 타임아웃 허용 시간과 해당 세션에 저장한 데이터 정보
  • spring:session:expirations:[expire time]
    • set으로 저장된 데이터
    • expire time에 삭제될 세션 정보를 담고 있음
    • 해당 시간이 되면 저장된 데이터를 조회해 해당 세션 모두 삭제
  • spring:session:sessions:expires:[session id]
    • string으로 저장된 데이터
    • 해당 세션 만료 키로 사용


내가 저장한 데이터 조회하기("/login" 요청을 통해 저장된 세션 찾기)

1. hkeys [key이름] 으로 조회
(session은 map을 저장소로 사용하기 때문에 hash(1)에 있을 것이라고 판단해서 1)로 조회)

> hkeys "spring:session:sessions:~"

# 결과
1) "creationTime"
2) "lastAccessedTime"
3) "maxInactiveInterval"
4) "sessionAttr:testkey"	# 내가 testkey라는 이름으로 저장한 데이터!!!

2. hget [key이름] [field 이름] 으로 저장한 데이터 조회

> hget "spring:session:sessions:~" "sessionAttr:testkey"

# 결과
저장된 데이터가 나옴
@PostMapping("/login")
public Boolean UserLogin(String email, String password, HttpSession session) {
  	...
    session.setAttribute("user", email);
	...
}

@DeleteMapping("/logout")
public Boolean UserLogout(HttpSession session) {
	session.removeAttribute("user");
	...
}

 

 

Spring Boot만을 이용해서 구현하는 것은 어렵지 않았지만, React에 Session을 유지하기 위한 방법이 localStorage, sessionStorage 만 생각이 났다.

하지만, Spring Security를 활용하면 그 문제가 해결이 된다는 조언을 듣고 Spring Security의 늪으로.....

 

 

 

 

 

 

 

 

 

 

반응형

'Project > Study' 카테고리의 다른 글

[WebSocket] Spring Boot + React WebSocket 코드 구현  (0) 2021.07.26
Spring Boot 유효성 검사  (0) 2021.07.20
[IntelliJ] Spring 서버 재시작 없이 반영  (0) 2021.07.19
Spring Boot Annotation  (0) 2021.07.16
WebSocket  (0) 2021.07.15

댓글