redis 설치 및 기존 프로젝트에 도입

chan·2023년 12월 18일
0

redisremote dictionary server의 약자로 key-value 구조로 데이터를 저장하는 NoSql 데이터베이스이다. 엔메모리라 속도가 빠르고 사용이 쉽다는 장점이 있다. 주로 캐싱, 세션 관리등에 사용된다.

나는 이번 프로젝트가 끝나고 지금까지 인메모리로 저장하던 room 정보들을 redis로 마이그레이션 해보려고 한다. room 정보들을 인메모리로 저장하면 서버를 클러스터로 돌리거나 수평확장할 때 데이터를 복사해서 각각의 서버에 저장해야 하는 문제가 있다. 지금은 단일 서버라 문제가 없지만, 한번 공부해보고 싶기도 했고, 시간도 있고 이력서 쓸 때도 한 줄이라도 더 쓸 꺼리가 생겨서 한번 도입해보려고 한다.

설치

Window에서 Redis 무설치 실행하기

Redis는 공식적으로 리눅스 기반의 OS만 지원한다. 하지만 ms에서 윈도우에서도 손쉽게 redis를 사용할 수 있게 소스를 제공하고 있다.

https://github.com/microsoftarchive/redis/releases

영구 설치를 원하면 msi 파일을 다운로드해서 설치하면 되고, 간단하게 하려면 zip 파일로 다운로드 하자.

나는 로컬인 윈도우에서 간단하게 실행하고 테스트해보려고 후자를 택했다.

redis.window 파일을 열면

포트를 설정하는 부분이 있는데, 나는 디폴트로 설정하고 진행했다.

redis.sever를 실행하면 세팅은 끝이다.

원하는 명령어는 redis-cli를 통해서 가능하다. 서버를 실행하고 redis-cli를 실행해서 접속한 후 여러가지 커멘트를 칠 수 있다.

Linux에서 Redis 설치하기

gpg

curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list

sudo apt-get update
sudo apt-get install redis

snap

sudo snap install redis

Redis Data Types

https://redis.io/docs/data-types/

공식 문서에서 모든 명령어와 관련된 설명과 예제를 얻을 수 있다. 기본적인 자료구조만 일단 학습하고 필요할 때 더 많은 자료구조를 학습하도록 하자.

자료구조는 다음을 제공한다.

  1. Strings
  2. JSON
  3. Lists
  4. Sets
  5. Hashes
  6. Sorted Sets
  7. Streams
  8. Geospatial
  9. Bitmaps
  10. BitFields
  11. Probablitic
  12. Time Series

나는 지금 프로젝트에서 JSON 자료구조랑 Lists 자료구조만 사용할 것 같아서 다음에 필요할 때 배우도록 하겠다.

JSON과 Lists는 우리가 평소에 배운 것과 거의 동일하다. Lists는 렌덤 액세스가 가능한 양방향 큐와 비슷해 보인다. 파이썬의 deque 느낌이 난다.

필요한 내용을 학습했으니, 내 프로젝트에 도입해보자.

인스턴스에 설치

위에 작성한 리눅스 설치법에 따라 내 인스턴스에 설치해주었다.

정상적으로 설치가 되었고, 6379 포트에서 실행되고 있다. 이제 우리의 프로젝트에 연동할 차례이다.

연동

npm install @liaoliaots/nestjs-redis ioredis를 통해 패키지를 설치하자.

그 다음, app.module.ts에 다음을 작성하자

import { Module } from '@nestjs/common';
import { RedisModule } from '@liaoliaots/nestjs-redis';

@Module({
  imports: [
    RedisModule.forRoot({
      readyLog: true,
      config: {
        host: 'localhost',
        port: 6380,
        password: 'bitnami'
      }
    })
  ]
})
export class AppModule {}
export interface RedisModuleOptions {
    /**
     * If set to `true`, all clients will be closed automatically on nestjs application shutdown.
     *
     * @defaultValue `true`
     */
    closeClient?: boolean;
    /**
     * Common options to be passed to each client.
     */
    commonOptions?: RedisOptions;
    /**
     * If set to `true`, then ready logging will be displayed when the client is ready.
     *
     * @defaultValue `false`
     */
    readyLog?: boolean;
    /**
     * If set to `true`, then errors that occurred while connecting will be displayed by the built-in logger.
     *
     * @defaultValue `true`
     */
    errorLog?: boolean;
    /**
     * Used to specify single or multiple clients.
     */
    config?: RedisClientOptions | RedisClientOptions[];
}

포트는 디폴트 값으로 설정했다.

자 이제 우리의 프로젝트에 도입하고 기존에 있던 인메모리 방식을 걷어내고 redis로 전환해보자.

redis 사용을 원하는 곳에

constructor(@InjectRedis() private readonly client: Redis) {}를 통해 주입 받고 사용하면 된다. 아주 간단하다.

문제

걷어내다가 문제가 발생했다. timer는 직렬화가 안된다. 직렬화 하다가 무한 재귀에 빠져서 서버가 터지는 일이 종종 있었다. 타이머를 배제하는 방향으로 해야하나? 혹시 모르니까 한번 더 해보자.

역시나 안된당.. 그러면 어떻게 이것을 해결해야 할까?? 타이머를 직렬화 하지 못한다면 지금까지 한 것들이 모두 헛수고가 되어버린다. 방 정보엔 지금은 타이머가 필수로 들어가기 때문이다.

깊은 고민을 한 결과, 지금 room 정보들을 redis로 전환하는 것은 불가능하다고 판단됐다. 하려면 할탠데, 그러면 클라이언트쪽에서도 많은 것을 바꿔야 한다. 서버쪽 코드도 엄청나게 바꿔야 한다. 애초에 기획부터 잘못되어서 리팩터링도 못하게 되는 수준인 것 같다.

채점 서버도 redis를 이용해서 pub/sub 구조로 바꿀려고 생각했는데, 채점 서버 express-queue 걷어내고, 사용자가 무한정 대기하는 구조가 아니라, 채점 요청에 대한 token을 주고, 채점이 완료되면 SSEWebSocket으로 해당 token으로 완료됐다는 이벤트를 날려줘야 한다. 나 혼자 리팩터링하는 지금, 이거는 일주일짜리가 아니라 몇주간 길게 이어질 것이라 판단해서 리팩터링은 안하기로 했다.

한번 써본다는 의미로 리팩터링을 진행하려던 것이어서, 그냥 로컬에서 프로젝트 하나 만들어서 pub/sub을 한번 써봐야겠다. 혼자 엄청난 대규모 공사를 거치면서 한번 써보는 것은 정말 비효율적이라 판단했기 때문이다.

다음 포스팅에서 redis pub/sub을 맛봐야겠다.

0개의 댓글