[Nest.js] redis 캐시 사용하기!!

ChuYong·2022년 8월 20일
2

안녕하세요티비~
제 개념 형상관리를 위해서 오늘부터 벨로그를 쓰기로 했어요 ^~^

사용하게 된 계기

처음에는 cache-manager에서 타입을 redis로 잡고 사용을 하고 있었는데,
저는 redis의 hash기능이나 set기능을 쓰고 싶었어요!
그런데 cache-manager는 이런 레디스 자료형을 사용하는 기능을 지원하지 않는것 같더라구요..
(아님말고?)

그래서 그냥 redis 라이브러리를 가져와서 만들기로 결정했어요!

설치하기

npm install @liaoliaots/nestjs-redis ioredis
yarn add @liaoliaots/nestjs-redis ioredis
pnpm add @liaoliaots/nestjs-redis ioredis

사용하시는 패키지 매니저에 따라서 쓰시는걸로 설치해주세요!

RedisConfig 생성

저는 데이터베이스를 임포트할때 콘피그를 따로 빼는걸 좋아하는 편이에요.
그리고 env에서 크레덴셜을 받아올거라서 따로 콘피그 클래스를 만들어주었어요!

cache.config.ts

import { Injectable } from '@nestjs/common';
import {
  RedisOptionsFactory,
  RedisModuleOptions,
} from '@liaoliaots/nestjs-redis';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class RedisConfigService implements RedisOptionsFactory {
  constructor(private configService: ConfigService) {}

  async createRedisOptions(): Promise<RedisModuleOptions> {
    return {
      config: {
        host: this.configService.get<string>('REDIS_HOSTNAME'),
        port: this.configService.get<number>('REDIS_PORT'),
        password: this.configService.get<string>('REDIS_PASSWORD'),
      },
    };
  }
}

이렇게 하면 .env 파일에서 3가지 크레덴셜만 있으면 레디스에 접속할 수 있어요.

cache.module.ts

import { Module } from '@nestjs/common';
import { RedisModule } from '@liaoliaots/nestjs-redis';
import { CacheService } from './cache.service';

@Module({
  imports: [RedisModule],
  providers: [CacheService],
  exports: [CacheService],
})
export class CacheModule {}

app.module.ts

@Module({
  imports: [
    ConfigModule.forRoot(),
    RedisModule.forRootAsync({
      imports: [ConfigModule],
      useClass: RedisConfigService,
      inject: [ConfigService],
    }),
    CacheModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

이렇게 AppModule에서 ConfigModule을 RedisConfigService에 주입하게 해서
RedisConfigService 내부에서 ConfigService를 사용하실 수가 있어요~~

useClass로 분리하지 않고 그냥 useFactory를 사용해서 설정하시는 방법도 있는데 저는 중괄호가 길어지는걸 싫어하는 스타일이셔서 그냥 콘피그를 위와같이 따로 분리했어요~~

Cache Service 생성

cache.service.ts

import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';
import { RedisService } from '@liaoliaots/nestjs-redis';

@Injectable()
export class CacheService {
  private readonly redisClient: Redis;
  constructor(private readonly redisService: RedisService) {
    this.redisClient = redisService.getClient();
  }

  async get(key: string): Promise<string> {
    return this.redisClient.get(key);
  }

  async set(key: string, value: string, expire?: number): Promise<'OK'> {
    return this.redisClient.set(key, value, 'EX', expire ?? 10);
  }

  async del(key: string): Promise<number> {
    return this.redisClient.del(key);
  }
}

우선 간단하게 get, set, del만 구현을 해보았어요!
hset, mset같은것도 redisClient에다가 쓸 수 있어요!

공식문서에 따르면 저는 constructor에서 di로 인젝션을 받았지만, @InjectRedis 데코레이터를 달아서 그냥 바로 Redis 객체를 받을수도 있다고 해요.

Cache Unit Test 작성

import { CacheService } from './cache.service';
import { Test } from '@nestjs/testing';
import { RedisModule } from '@liaoliaots/nestjs-redis';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RedisConfigService } from './cache.config';

describe('CacheService', () => {
  let cacheService: CacheService;
  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot(),
        RedisModule.forRootAsync({
          imports: [ConfigModule],
          useClass: RedisConfigService,
          inject: [ConfigService],
        }),
      ],
      providers: [CacheService],
    }).compile();
    cacheService = moduleRef.get<CacheService>(CacheService);
  });
  describe('set', () => {
    it('should be defined', () => {
      expect(cacheService.set).toBeDefined();
    });
    it('should return Ok', async () => {
      const result = await cacheService.set('test', 'test', 100);
      expect(result).toBe('OK');
    });
  });
  describe('get', () => {
    it('should be defined', () => {
      expect(cacheService.get).toBeDefined();
    });
    it('should return test', async () => {
      await cacheService.set('test', 'test', 100);
      const result = await cacheService.get('test');
      expect(result).toBe('test');
    });
  });
});

저는 참된 개발자가 되기 위하여 유닛 테스트도 간단하게 작성해보았어요~

아무쪼록 잘되는것같으니까 이만 줄이도록 하겠습니다티비~
즐코딩하세요 OwO~

profile
백엔드 & 인프라 를 좋아하는 개발자에요

2개의 댓글

comment-user-thumbnail
2022년 12월 12일

안녕하세요 글 재밌게 잘봤습니다
혹시 설치하신 패키지 버전이 v9 일까요?

답글 달기
comment-user-thumbnail
2023년 1월 23일

재밌게 읽었습니다 ㅎㅎ

답글 달기