주말중에 대부분 기능 구현은 끝냈고 오늘은 내일 발표를 위한 최종 테스트, 시연 영상을 찍고 발표 자료와 Readme를 작성하는 시간이 길었다.
주말을 조금 쓰긴 했지만 일정 관리 자체는 굉장히 잘한 것 같아 다행이고 팀원 분들의 불만사항도 다른 주차에 비해 현저히 적은 걸로 느껴져서 많이 기쁜 프로젝트가 된 것 같다.
물론 정말 프론트만 있으면 실제 서비스로 내놓을 수 있을 만큼 정책이 완벽하게 설정되어 있거나 한 건 아니다. 하지만 학습 주차 프로젝트의 목적인 협업 경험과 복습의 측면에서는 정말 훌륭한 시간이 됐다고 스스로 생각하기로 했다.
프로젝트가 점점 험난해져서 기술 스택을 늘리기 위한 강의로 방향성이 약간 바뀌게 되었다. 그 중에서도 오늘은 두고두고 써먹는 Redis에 대해 진행하게 됐다.
Key-Value 구조로 데이터를 저장하는 비관계형 데이터베이스로 NoSQL로 구분된다. 이럴땐 쿼리가 아니라 커맨드 기반으로 동작한다고 본다.
특징은 Disk가 아닌 Memory를 쓰는 In-memory DB인 점이고 휘발성에 속도 빠른 점인데 이건 요즘 많이 접해봐서 이 특징이 체감되는 것 같다.
추가로 Single Thread로 동작한다고 하는데 이거는 이번에 처음 알게 됐다.
이렇게 3가지 키워드를 Redis의 상징적인 특징으로 여긴다고 한다.
Value 에 어떤 자료구조가 올 수 있는지에 대해 알아봤는데 이메일 인증때 알아본 것 처럼 굉장히 다양하게 설정할 수 있는 것 같다.
기본적으로 Expire 용도로 쓰는 TTL(Time To Live)는 Key에만 설정 가능하다고 한다.
여기까지만 다뤘고 이외에도 다양한 자료구조가 제공된다. 그렇기에 비즈니스 요구사항 구현을 위해 Redis를 쓸 때는 적절한 자료구조를 결정하기 위해 고민이 필요하다.
기술을 사용할 때 근거가 필요한 것은 항상 중요하다.
In-memory로 빠르고 TTL을 지원하고 다양한 자료구조를 지원하기 때문에 Remote Cache Server로 많이 사용된다. 그리고 기술적으로 충분한 성숙을 이뤄서 안정적이기도 하다.
비슷한 역할로 In-memory인 memcached를 같이 고민해보면 좋다.
똑같은 일을 처리하는 서버를 여러대 추가해서 확장하는 Scale-out 상황에서 중앙 저장소로 쓰기에 좋다.
예를 들면 사용자가 세션 로그인을 인스턴스 A, B, C중 A에 했을 때 A에 세션을 저장해뒀지만 다음 접근이 C로 가게 되면 세션이 없으므로 문제가 생긴다. 그러니 이 Session을 각 인스턴스에 저장하지 않게 Session 저장소의 역할로 활용이 가능하다.
추가로 Scale-out은 요즘 굉장히 자주 다루지만 State의 저장, scheduler의 다중 실행 문제등 문제가 있으니 Scale-out 할 때는 미리 준비를 잘해야한다.
그리고 이런 단점때문에 Stateless가 뜨게 됐다.
In-memory 특성상 영속성적인 목적보다는 서버 사이에서 주고받을 데이터를 저장하는 것에 가깝다.
그리고 Redis는 Single thread로 동작하기 때문에 Race condition 이슈에서 자유롭고 빠르다.
예를 들면 코인 시세 조회 서비스가 있을 경우 코인 시세 조회 API 서버와 코인 시세 수집 서버를 나눠 수집 서버는 Redis에 저장하고 조회 서버는 Redis에서 조회하는 것이다.
빠른 것이 여전히 장점이다.
Redis가 영속성 기능을 제공하긴 하지만 잘 사용하지 않는다.
Redis는 Kafka와 유사한 Event Pub/Sub 기능을 제공한다.
이를 이용해 EDA(Event Driven Architecture)를 구현하는 활용 용도로 쓸 수 있다.
Redis는 Single thread 기반이므로 선착순 티켓팅같은 기능을 Lock을 구현하면 쉽게 해결 가능하다.
MySQL같은 것도 Lock을 구현할 수 있지만 기본적으로 Multi thread라 Lock 구현이 어렵다.
어려운 내용인데 강의에서는 보여주고만 가셨다. 써볼 일이 많이는 없어 이런 기능이 있다고 소개해주셨다.
Redis node를 1대로 운영하는 건 위험하다고 한다.
한 대만 꺼져도 해당 컴포넌트 자체가 모두 죽는 SPOF (Single Point Of Failure) 문제의 위험이 있다.
그래서 Redis Node를 1000개까지 확장할 수 있는 Redis Cluster 기능을 제공한다.
Spring Data Repository를 이용하는 방법과 RedisTemplate를 이용하는 방법이 있는데 강의에선 RedisTemplate만 다루고 넘어갔다.
Spring Data Repository를 쓰면 추상화가 많이 이루어진다고 한다.
이는 의존성 추가하고 @Configuration Bean으로 만들고 RedisTemplate를 주입받아 사용하면 된다.
Redis를 다루는 법은 코드 몇줄이면 끝날 정도로 쉽지만 Service가 RedisTemplate를 직접 주입받는 건 여타 로직의 이유로 권장하지 않는다고 하셨다. 그래서 튜터님은 RedisRepository로 분리하는 걸 추천하셨다.
Redis는 Single thread 기반으로 각각의 커맨드에 대한 원자성(다른 커맨드가 끼어들지 못함)을 보장해준다.
그런데 여러 클라이언트에서 Redis 커맨드가 연달아 호출되면 Race condition 이슈가 존재할 수 있다.
이를 해결하는 방법은 위에서 말한 Lock도 있고 Redis의 Transaction을 이용하는 방법이 있다. -> MULTI 커맨드
MULTI 커맨드 실행 후 입력되는 커맨드를 Queue에 쌓다가 EXEC을 실행하면 여러 커맨드에 대한 원자성을 지키며 실행한다. (Rollback을 원하면 DISCARD를 쓰면 된다.)
별도의 Transactional manager 구현체는 존재하지 않는다.
RedisTemplate bean configuration에서 redisTemplate.setEnableTransactionSupport(true)
만 처리해주면 된다. 이후 @Transactional 을 사용하면 Jpa의 Transactional manager가 구현해준다.