가상계좌 관련 결제 시스템의 결제 관련 필요 정보들을 저장하는 현재 로직 플로우로는 해결이 불가능한 품절/적립금 적립 관련 이슈가 있었는데 프론트엔드 전역변수로의 처리에 대해 이야기하다 Redis를 사용하면 가능할 거 같다라는 생각에 NestJS에 Redis 캐싱을 적용시키게 되었다.
Redis의 경우, AWS ElasticCache를 사용할 수 있지만, 기본적으로 메모리 용량 = 돈이기 때문에 docker 공식 redis 이미지를 기존 ec2 인스턴스 내에 docker 컨테이너로 standalone 모드로 띄워두는 방식을 사용하였다.
의존성 추가시 cache-manager 라이브러리 버전을 명시하는 이유는 cache-manager 최신 버전인 5.x.x 버전과 cache-manager-redis-store의 버전이 호환되지 않는 이슈로 store.set is not a function 타입 에러가 발생되기 때문이다.
DI 세팅
Redis 캐시 관련 환경 설정하려면 CacheModule을 register해서 import해야한다. CacheModule의 경우, @nestjs/common에 기본적으로 포함되어있다. ttl 옵션의 경우 4.x.x 버전은 초(s) 단위로 입력해야하고 5.x.x 버전은 밀리초(ms) 단위로 추가하면 된다.
[Redis] NestJs에 Redis 캐시를 적용해보자 (feat. cache-manager)
가상계좌 관련 결제 시스템의 결제 관련 필요 정보들을 저장하는 현재 로직 플로우로는 해결이 불가능한 품절/적립금 적립 관련 이슈가 있었는데 프론트엔드 전역변수로의 처리에 대해 이야기하다 Redis를 사용하면 가능할 거 같다라는 생각에 NestJS에 Redis 캐싱을 적용시키게 되었다.
Redis의 경우, AWS ElasticCache를 사용할 수 있지만, 기본적으로 메모리 용량 = 돈이기 때문에 docker 공식 redis 이미지를 기존 ec2 인스턴스 내에 docker 컨테이너로 standalone 모드로 띄워두는 방식을 사용하였다.
Redis 관련 의존성 추가
Redis 캐시를 사용하기 위한 의존성을 추가해준다.
의존성 추가시 cache-manager 라이브러리 버전을 명시하는 이유는 cache-manager 최신 버전인 5.x.x 버전과 cache-manager-redis-store의 버전이 호환되지 않는 이슈로 store.set is not a function 타입 에러가 발생되기 때문이다.
DI 세팅
Redis 캐시 관련 환경 설정하려면 CacheModule을 register해서 import해야한다. CacheModule의 경우, @nestjs/common에 기본적으로 포함되어있다. ttl 옵션의 경우 4.x.x 버전은 초(s) 단위로 입력해야하고 5.x.x 버전은 밀리초(ms) 단위로 추가하면 된다.
import { CacheModule, Module } from '@nestjs/common'; import * as redisStore from 'cache-manager-redis-store'; import { RedisService } from './redis/redis.service'; const cacheModule = CacheModule.register({ useFactory: async () => ({ store: redisStore, host: process.env.REDIS_HOST, port: process.env.REDIS_PORT, ttl: 1000, // 캐시 default 유지시간: 1000s }), }); @Module({ imports: [cacheModule], providers: [RedisService], exports: [RedisService], }) export CacheModule {}
cache-manager의 Cache 타입에서 get/set/reset/del 메소드를 RedisService 클래스를 생성하여 다른 서비스 로직에서 사용이 간편하게 매핑해둔다.
import { CACHE_MANAGER, Inject, Injectable } from '@nestjs/common'; import { Cache } from 'cache-manager'; @Injectable() export class RedisService { constructor(@Inject(CACHE_MANAGER) private readonly redis: Cache) {} async get(key: string): Promise<any> { return await this.redis.get(key); } async set(key: string, value: any, option?: any) { await this.redis.set(key, value, option); } async reset() { await this.redis.reset(); } async del(key: string) { await this.redis.del(key); } }
RedisService 서비스 로직에 추가해보기
Redis 캐싱이 필요한 로직이 위치해있는 Service 클래스의 생성자에 import해서 사용하면 된다.
// 레디스 적용 예제 @Injectable() export class PaymentService { constructor( @InjectRepository(Payment) private readonly repository: PaymentRepository, private readonly redisService: RedisService ){} async commitPayment(dto: ExampleDto) { const orderNo = generateRandomToken(20); await this.redisService.set(orderNo, dto, {ttl: 60 * 5}); /* * PG사 api 통신 로직 */ } async approve(orderNo: string) { const cachedExampleDto: ExampleDto = await this.redisService.get(orderNo); /* * 결제 검증 로직 */ } }
Redis에 저장하는 서로 다른 데이터의 key가 중복되면 안되므로 randomToken 20자리를 key, dto를 value로 저장하고, 검증 로직 단계에서 해당 key에 매칭되는 dto를 가져와서 로직을 진행하는 방식으로 리팩토링을 진행하였다.
'개발 > NestJS' 카테고리의 다른 글