[Nest.js]03. Caching

김지엽·2023년 10월 7일
0
post-custom-banner

1. Caching이란?

사용자들이 반복적으로 요청하는 데이터나 자주 사용되는 데이터, 빠른 처리가 필요한 데이터 등을 다른 저장장치에 미리 저장해두어 database에 불필요한 요청을 막고 또한 빠르게 resource를 제공할 수 있게 해주는 기술

2. Caching의 구조

WASWeb Application Server를 뜻하는 말로 database 에서 데이터를 가져오고 로직을 처리하는 서버라고 말하며 NestJs는 NodeJs에서 WAS를 만들게 도와주는 프레임워크 라고 볼 수 있다.

3. Caching 구현

- CacheModule

cache-manger 설치

$ npm install @nestjs/cache-manager cache-manager

ioredis 설치

  $ npm install cache-manager-ioredis

Redis 연결

[caching.module.ts]
import { CacheModule } from '@nestjs/cache-manager';
import { DynamicModule, Module, Scope } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

import * as Redis from "cache-manager-ioredis";

@Module({})
export class CachingModule {

    static register(): DynamicModule {

        const cacheModule: DynamicModule = CacheModule.registerAsync({
            isGlobal: true,
            useFactory: async (configService: ConfigService) => ({
                stroe: Redis,
                host: configService.get<string>("REDIS_HOST"),
                port: configService.get<string>("REDIS_PORT"),
                password: configService.get<string>("REDIS_PASSWORD")
            }),
            inject: [ConfigService]
        });
        

        return {
            module: CachingModule,
            imports: [cacheModule],
            exports: [cacheModule],
        }
    }
}

Redis는 메모리 기반 데이터 스토어로 데이터에 빠르게 접근할 수 있다. 이는 캐시에서 데이터를 검색할 때 빠른 응답 시간을 제공하는 데 큰 장점을 가지고 NestJS에서 Redis를 사용하면 캐싱, 세션 관리 등 다양한 기능을 구현할 수 있다.

- Cache Manager 사용하기

webtoon Module

[webtoon.module.ts]
import { Module } from '@nestjs/common';
import { WebtoonController } from './webtoon.controller';
import { WebtoonService } from './webtoon.service';
import { webtoonProvider } from 'src/custom-provider/model.provider';

@Module({
    imports: [],
    controllers: [WebtoonController],
    providers: [WebtoonService, webtoonProvider],
})
export class WebtoonModule {}

CacheModule을 isGlobal: true 옵션을 활성화 했기에 다른 모듈에서 import하지 않아도 사용할 수 있다.

webtoon Service
nest에서 caching을 할 때에는 controller-level에서 할 수도 있지만 다음과 같이 CACHE_MANAGER를 주입해 service-level에서도 구현할 수 있다.

[webtoon.service.ts]
...

constructor(
        @Inject("WEBTOON") private webtoonModel: typeof Webtoon,
        @Inject(CACHE_MANAGER) private cacheManager: Cache
    ) {}

    async getWebtoonForId(id: string): Promise<Webtoon> {
        // cache-key
        const webtoonCacheKey: string = `webtoonCache-${id}`; 

        // cache 값이 있다면 바로 값을 반환
        const webtoonCache: string = await this.cacheManager.get(webtoonCacheKey);
        if (webtoonCache) {
            const webtoonInfo = JSON.parse(webtoonCache);
            return new Webtoon(webtoonInfo);
        }

        // database에서 해당 id의 웹툰 가져오기
        const webtoon: Webtoon =  await this.webtoonModel.findOne({ where: { webtoonId: id }});
        if (!webtoon) {
            throw new Error("webtoonId is not exsit..");
        }

        // cache 값 저장
        this.cacheManager.set(
            webtoonCacheKey,
            JSON.stringify(webtoon),
            webtoonCacheTTL, // cache 만료 시간
        );

        return webtoon;
    }
    
...

- Cache Interceptor 사용하기

위와 같이 service-level에서 구현하지 않고 controller-level에서 구현한다면 훨씬 간단하게 구현할 수 있다.

[webtoon.controller.ts]
import { Controller, Get, Param, UseInterceptors } from '@nestjs/common';
import { WebtoonService } from './webtoon.service';
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
import { webtoonCacheTTL } from 'src/constatns/redis.constants';

@UseInterceptors(CacheInterceptor)
@Controller('webtoon')
export class WebtoonController {

    constructor(private webtoonService: WebtoonService) {}

    @CacheTTL(webtoonCacheTTL)
    @Get("/:id")
    getWebtoonForId(@Param("id") id: string) {
        return this.webtoonService.getWebtoonForId(id);
    }
}

UseInterceptors 데코레이터만 사용하면 위와 같이 controller-level에서 간단하게 구현할 수 있다. 그렇다면 controller-level에서만 caching을 구현하면 될텐데 왜 service-level에서 구현하는걸까?

- service-level에서 caching하는 이유

  1. service의 종속
    Recommend 모듈과 Webtoon 모듈이 있다고 가정했을때 우리는 Recommend모듈에서 Webtoon 모듈의 getWebtoonForId 메서드가 필요할 수 있다. 이럴 때 controller-level에서 caching을 구현했을 때 우리는 service-level에서 caching이 작용하지 않기에 불필요한 database와의 교류가 많이 일어날 수 있다. 그래서 특정 메서드들을 service-level에서 caching을 구현한 것이다.

  2. CacheInterceptor는 Get만 캐싱한다.
    controller-level에서 CacheInterceptor로 caching을 구현했을때는 Get Request만 caching을 한다. 즉 POST, PATCH 등은 caching하지 않는다. 하지만 경우에 따라 POST, PATCH에도 caching을 적용하고 싶을 때는 service-level에서 caching을 구현해야 한다.

참고

https://docs.nestjs.com/techniques/caching - NestJs 공식문서

profile
욕심 많은 개발자
post-custom-banner

0개의 댓글