여러분이라면 Redis를 연동하고자 할 때 Caching과 Redis 둘 중 어느것을 먼저 들어가보시겠나요?

대부분 Redis를 보고 Caching을 잊어버릴것같아요!
NestJS Docs에서 Redis를 연결하는 방법을 찾아보니 직접적으로 Redis가 보이는 것은 Micro Service로 설정하는 것밖에 안보였어요. 실제로 MICROSERVICE/Redis 항목이 떡하니 보이기도 했고요. 이거에 꽂힌 것 때문에 어떻게 적용해야하나... 싶었어요.
이 때문에 검색을 많이 해보고 얻은 결론은 죄다 CacheModule을 쓰네...? 그러면 Cache쪽에 있나?였고, 실제로 Techniques/Caching에 가보니 떡하니 redis-store가 있던거에요!
이에 Redis의 정의를 먼저 정립할 필요가 생겼어요.
REmote DIctionary Server, 즉 원격 "딕셔너리" 서버
메모리 기반의 데이터 저장소이다. 키-밸류(key-value) 데이터 구조에 기반한 다양한 형태의 자료 구조를 제공하며, 데이터들을 저장할 수 있는 저장소이다.
레디스는 메모리에 데이터를 저장하기 때문에 저장 공간에 제약이 있어, 주로 보조 데이터 저장소로 사용한다.
레디스 내부에서 명령어를 처리하는 부분은 싱글 스레드 아키텍처로 구현되어 있다.
“스프링 부트로 개발하는 MSA 컴포넌트 - 김병부“
키는 문자열으로 고정되어있다는 단점이 있지만, 값은 무엇을 담아도 상관없습니다. 단순 값은 물론 객체도 말이죠!
서버 메모리에 저장되므로 I/O 관련 오버헤드가 다른 데이터베이스와는 현저히 적고, 속도도 빠릅니다. 다만, 서버 메모리에 적재되는 방식이므로 저장용량에 한계가 있습니다.
메모리에 저장된 데이터를 물리 저장장치에 저장하지 않는다면 데이터가 사라질 수 있습니다. 이러한 특성으로 캐시로 사용될 수 있겠네요!
싱글스레드로 이루어져있어 아무래도 멀티스레드보다는 구조가 간단합니다. 다만, 싱글스레드로 이루어져있다보니 큰 요청에 대해서는 대기시간이 길 수밖에 없습니다. 하지만 데드락, Context Switch가 발생하지 않으니 관리에 효율적이라고 할 순 있겠네요.
Redis는 메모리에 저장되는 방식이다보니 데이터가 계속 쌓이다보면 서버 전체 메모리를 사용할 수도 있습니다. 이때 TTL을 설정하여 해당 시간 이후에는 데이터를 삭제해 메모리를 관리하게 됩니다.
Docker를 이용하는 방법과 직접 설치하는 방법 2가지가 있는데, 저는 Docker를 이용했어요.
Redis는 또 Redis와 Redis Stack으로 나뉘는데, Redis(또는 Redis OSS)는 단일 애플리케이션, Redis Stack은 Redis+추가기능 이라 생각하면 돼요.
Redis Stack에는 아래의 기능이 추가되어있다고 해요.
하지만 단순히 Redis를 사용하겠다! 하면 Redis OSS를 사용하면 돼요.
docker pull redis를 통해 이미지를 받아줍시다. 특정 버전의 이미지를 받고싶다면 Docker/redis에서 원하는 이미지를 pull 해줍시다.
docker run -p 6379:6379 --name test-redis -d redis:latest --requirepass "1234"

위 명령어를 입력했다면 컨테이너가 하나 생성 됐을거에요. 접근을 해서 ping 명령어를 입력해봅시다!
docker exec -it test-redis redis-cli

비밀번호를 설정했기 때문에 처음에는 인증 오류가 발생하지만, auth <password> 명령어를 입력하고 다시 ping 명령어를 입력하면 PONG 결과를 볼 수 있어요.
여기까지 했다면 Docker로 설정은 끝!
version: "3.8"
services:
redis:
image: redis:alpine
ports:
- 6379:6379
command: redis-server --port 6379 --requirepass "1234"
자세한 내용은 NestJS Docs의 Caching을 참고해주세요!
npm install @nestjs/cache-manager cache-manager
npm install -D @types/cache-manager
캐시 모듈을 사용하기 전, 버전이 몇인지 확인해야됩니다. 이는 TTL 값을 표현하는 방식의 차이인데요, V4까지는 "초" 단위를 사용했지만, V5부터는 "밀리초" 단위를 사용한다고 합니다. NestJS는 V4의 캐시 매니저를 타겟으로 출시돼서 Docs는 V4를 기반, 즉 초 단위를 기반으로 한다고 하네요.
실제로 Module에서 TTL을 적용할 때 초 단위긴 했습니다. V5라도요.
// redis-cache.module.ts
import { Module } from '@nestjs/common';
import { CacheModule } from '@nestjs/cache-manager';
@Module({
imports: [CacheModule.register()],
providers: [RedisCacheService],
exports: [RedisCacheService],
})
export class RedisCacheModule {}
// redis-cache.service.ts
...
// ! 캐시 매니저와 상호작용하기 위해서는
// CACHE_MANAGER 토큰을 주입해야 합니다!
constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {}
...
// key의 값을 가져옵니다.
// 캐시에 값이 없다면 undefined를 출력합니다.
await this.cacheManager.get('key') => Promise<unknown>;
// ttl을 특정 값으로 설정하고싶을 때 아래와 같이 사용합니다.
// ttl의 기본 값은 5초입니다.
// 구현은 MilliSec으로 되어있지만, 실제 적용은 Sec으로 적용됩니다.
await this.cacheManager.set(key: string, value: unknown, ttl?: number);
// 캐시의 만료 시각을 정하고싶지 않을때는 0을 적어주면 됩니다.
await this.cacheManager.set(key: string, value: unknown, ttl=0);
// 캐시 매니저에서 특정 key를 제거합니다.
await this.cacheManager.del('key');
앞전에 했던 과정은 NestJS 서버의 캐시메모리를 이용하는 방법이었습니다. 서버를 껐다 켜면 데이터가 모조리 날아가죠. 이를 막고자 Redis를 도입하는 것이고, Redis와 NestJS를 연동해봅시다!
사용 가능한 Store의 종류는 이곳을 참고해주세요!
해당 페이지에서 보니 Redis와 사용 가능한 것은
official
cache-manager-redis-yet - 밀리초 단위
import { redisStore } from 'cache-manager-redis-yet';
CacheModule.registerAsync({
useFactory: async () => {
const store = await redisStore({
socket: {
host: 'localhost',
port: 6379,
},
password: '1234',
ttl: 60 * 1000,
});
return {
store,
};
},
}),
cache-manager-ioredis-yet - 밀리초 단위
import { redisStore } from 'cache-manager-ioredis-yet';
CacheModule.registerAsync({
useFactory: async () => {
const store = await redisStore({
host: 'localhost',
port: 6379,
password: '1234',
ttl: 60 * 1000,
});
return {
store,
};
},
}),
third party
node-cache-manager-ioredis
- 초 단위
import { ioRedisStore } from '@tirke/node-cache-manager-ioredis';
CacheModule.register({
store: ioRedisStore,
host: 'localhost',
port: 6379,
password: '1234',
ttl: 60,
}),
node-cache-manager-redis
: require 사용으로, es6에서 사용 불가
docs에는 없지만...
cache-manager-ioredis - 초 단위
import * as redisStore from 'cache-manager-ioredis';
CacheModule.register({
store: redisStore,
host: 'localhost',
port: 6379,
password: '1234',
ttl: 60,
}),
이정도밖에 없는것같네요. 어느 것을 사용하나 동일하겠지만, 저는 cache-manager-ioredis를 사용하였습니다! 어느 것을 사용하나 CacheModule.register부분에서의 설정만 조금씩 바뀌는 정도니까요.
웬만하면 코드 첨부한 store을 사용하길 바랄게요.
캐시 매니저에 redis 관리 모듈을 넣어주기만 하면 끝!
이후 전에 작성한 Service를 이용하면 캐시를 사용할 수 있게 되었어요~