RAM은 그냥 단순히 메모리 공간일 뿐이다. 데이터 구조도 없고, 프로세스 간 공유도 불가능하며 영속성도 없어서 프로세스가 죽으면 데이터가 사라진다.

단순히 “메모리에 올려서 빠르다”는 이유만으로 Redis를 도입하는 것은 아니다. 애플리케이션 내부의 변수나 로컬 메모리가 가질 수 없는 한계를 Redis는 다음과 같은 구조적 이점으로 해결한다.

1. Data Structures

RAM 단독 메모리에 데이터를 올린다는 것은 본질적으로 단순한 byte[] 덩어리나 객체 형태로 저장하는 것에 불과합니다. 정렬이나 필터링이 필요하면 애플리케이션 레벨에서 복잡한 로직을 직접 구현해야 합니다.

Redis 단순한 Key-Value 쌍을 넘어서 List, Set, Sorted Set, Hash 등 고도로 최적화된 자료구조를 자체적으로 제공합니다. 개발자는 복잡한 로직 구현 없이, 제공되는 명령어만으로 데이터를 즉각적이고 효율적으로 가공하여 사용할 수 있습니다.

2. 프로세스 간 상태 공유

RAM 단독 로컬 메모리는 해당 프로세스에 종속됩니다. 트래픽이 늘어나 서버를 2대, 3대로 늘리는(Scale-out) 순간, 프로세스 A의 메모리와 프로세스 B의 메모리는 서로 격리되어 상태를 공유할 수 없습니다. (예: 1번 서버에서 로그인한 유저 세션을 2번 서버가 모름)

Redis 여러 대의 애플리케이션 서버가 공통으로 바라보는 단일 중앙 저장소(Single Source of Truth) 역할을 합니다. 서버가 아무리 늘어나도 모든 인스턴스가 동일한 상태를 공유할 수 있습니다.

3. Network-based Access

RAM 단독 애플리케이션과 생사고락을 함께하는 종속된 자원입니다.

Redis 자체적인 TCP 네트워크 통신을 지원하는 ‘독립된 서버’입니다. 따라서 Spring Boot 앱 서버뿐만 아니라 Node.js, Python 등 다양한 이기종 환경의 인스턴스들이 동시에 네트워크를 통해 접근하여 데이터를 읽고 쓸 수 있는 뛰어난 확장성을 가집니다.

4. Persistence

RAM 단독 인메모리의 치명적인 단점은 ‘휘발성’이다. 프로세스가 종료되거나 서버가 재부팅되면 메모리 위의 모든 데이터는 영구적으로 증발하게 된다.

Redis 메모리 기반임에도 불구하고 데이터를 디스크에 안전하게 백업하는 영속성 기능을 제공한다. RDB(Redis Database)는 특정 시간 간격마다 메모리의 상태를 통째로 사진 찍듯 디스크에 스냅샷으로 저장한다. AOF(Append Only File)는 데이터가 변경되는 모든 쓰기 연산(Log)을 디스크에 기록하여, 장애 발생 시 로그를 재실행하여 데이터를 손실 없이 복구한다.

TTL(Time To Live)

데이터에 수명(만료 시간)을 부여하여 시간이 다 되면 Redis가 백그라운드에서 알아서 데이터를 삭제하는 기능이다. RDBMS에 별도의 만료 시간 컬럼을 만들고 배치(Batch) 스케줄러를 돌려가며 데이터를 지울 필요가 없다. 현재 Apex-F1 프로젝트의 처리율 제한(Rate Limiting) 로직에서도 카운터를 증가시킴과 동시에 EXPIRE 명령어로 만료 시간을 설정하여, 특정 시간이 지나면 자동으로 한도가 초기화되도록 구현되어 있다. 세션 관리나 임시 인증번호(OTP) 저장 등에도 필수적으로 사용된다.

Caching

디스크 기반의 데이터베이스(PostgreSQL 등) 앞단에 배치되어, 무겁고 반복적인 조회의 방패막이 역할을 수행한다. Spring의 @Cacheable 어노테이션과 결합하여 Look-aside 패턴으로 동작하며, DB에서 복잡한 조인(Join) 연산이 필요한 쿼리를 O(1)의 메모리 조회로 치환한다. 이는 트래픽 폭주 시 RDBMS 서버의 CPU 사용률이 한계치에 도달하여 서버가 다운되는 현상을 원천적으로 차단하는 가장 강력한 아키텍처 패턴이다.

분산 락 (Distributed Lock) 다수의 서버로 확장된(Scale-out) 분산 환경에서는 Java의 synchronized 키워드와 같은 로컬 메모리 수준의 제어가 무용지물이 된다. 이때 모든 서버가 공통으로 바라보는 Redis를 단일 진실 공급원(Single Source of Truth)으로 삼아 락(Lock)을 획득하고 반납하는 방식으로 동시성 문제를 해결한다. 수만 명이 동시에 접속하는 선착순 이벤트나 재고 차감 비즈니스 로직에서 데이터 정합성(Race Condition)이 깨지는 것을 방지한다.

랭킹 시스템 (Sorted Set) Redis의 ZSET 자료구조는 데이터를 삽입하는 즉시 지정된 점수(Score)를 기준으로 데이터를 자동 정렬한다. 내부적으로 스킵 리스트(Skiplist) 구조를 사용하여 평균 O(log N)의 극히 빠른 속도로 순위를 계산한다. 수백만 명의 유저가 참여하는 게임의 실시간 랭킹보드를 RDBMS의 ORDER BY로 구현하면 극심한 병목이 발생하지만, Redis의 Sorted Set을 활용하면 실시간 상위권 추출 및 특정 유저의 등수 조회를 즉각적으로 처리할 수 있다.

큐 (List) Redis의 List 자료구조는 양방향 연결 리스트(Doubly Linked List)로 구현되어 있어, 데이터의 양 끝에서 삽입과 삭제(LPUSH, RPOP)를 O(1)의 속도로 처리한다. 이를 통해 이메일 발송, 파일 처리 등 무거운 비동기 작업을 메인 스레드에서 분리하여 백그라운드로 넘길 수 있다. Kafka나 RabbitMQ와 같은 무거운 메시지 브로커를 도입하기 전, 가볍고 직관적인 비동기 메시지 큐(Message Queue) 시스템으로 훌륭하게 동작한다.

Pub/Sub (발행/구독) 데이터를 영구적으로 저장하지 않고, 특정 채널(Channel)에 메시지를 발행(Publish)하면 해당 채널을 구독(Subscribe)하고 있는 모든 클라이언트에게 즉시 메시지를 브로드캐스팅(Fire-and-forget)하는 기능이다. 다수의 사용자에게 메시지를 전달해야 하는 실시간 채팅방 기능이나 알림 시스템에 주로 쓰이며, 여러 대의 서버 간 ‘분산 캐시 무효화(Cache Invalidation) 이벤트’를 전파할 때 핵심적인 통신 수단으로 사용된다.