Redis는 훌륭한 도구지만, 우리 서비스에 지금 필요한지부터 질문했습니다.
단일 서버에서는 과잉 설계를 피하고, 필요해지면 안전하게 전환할 수 있도록 길을 열어두었습니다.
부스트캠프 팀 프로젝트를 진행하던 중, “다른 팀들은 Redis를 쓰던데, 우리도 써야 하지 않을까요?”라는 의견이 나왔습니다.
Redis가 워낙 널리 쓰이다 보니 자연스러운 질문이었지만, 조금 더 들여다보니 ‘필요해서’라기보다 ‘회사에서 많이 쓰니까’ 도입한 경우도 적지 않았습니다.
저는 기술 역량을 보여주는 프로젝트라 하더라도 필요한 만큼만 활용하는 것이 바람직하다고 생각했습니다. 그런 이유로 Redis를 꼭 써야 한다는 의견을 조율하며 동료들을 설득했는데, 이번 글에서는 그 과정을 공유드리려 합니다.
Redis가 보통 쓰이는 곳
Redis는 메모리 기반이라 매우 빠르며, 기본 저장은 RAM에 이루어집니다. 영속성은 기본 동작에 포함되지 않지만, 필요에 따라 RDB 스냅샷이나 AOF로 설정할 수 있습니다 (캐시 용도로는 보통 최소화하거나 비활성화합니다).
애플리케이션 프로세스 외부에서 메모리를 관리하기 때문에, 여러 서버가 상태를 공유해야 할 때 세션·캐시·레이트리밋·분산 락 등에 유용합니다. 또한 서버 간 Pub/Sub 같은 이벤트 브로커로도 활용됩니다.

- 여러 서버 간 공유 메모리 저장소(세션/상태 공유)
- 캐시 계층(DB 부하 완화)
- Pub/Sub(분산 이벤트 브로커)
이 요구가 명확하다면 Redis는 아주 좋은 선택입니다.
Redis 도입의 트레이드오프
레디스 공식 문서에 따르면 단순 명령(PING)은 약 14만 ops/sec 수준으로 처리할 수 있고, JSON 형식의 데이터라도 작은 객체(380B) ~ 큰 객체(3.5KB)까지 SET/GET 시나리오 테스트 결과 크기가 커질수록 성능은 저하되지만, 여전히 수만 ~ 수십만 ops/sec 수준을 유지할 만큼 뛰어난 성능을 보여줍니다.
이러한 수치만 볼 때는 Redis 도입이 당연히 좋아 보입니다. 하지만 실제로는 어떨까요?
Redis 도입의 트레이드오프는 Redis 자체 성능이 아니라, 별도의 프로세스로 실행된다는 사실에서 발생합니다.
네트워크 비용
별도의 프로세스로 실행되기 때문에 매 요청마다 네트워크 왕복 비용이 발생합니다.

위 자료는 Jeff Dean의 Latency Numbers Every Programmer Should Know 자료로, 컴퓨터 시스템에서 연산·메모리·스토리지·네트워크 등 다양한 계층의 지연 시간을 시각화하기 위한 자료입니다.
위 자료에 따르면 메인 메모리 접근은 약 100ns 수준에 불과하지만, 같은 데이터센터 내 네트워크 왕복은 약 500,000ns(≈ 500μs)로, 단순 메모리 접근 대비 수천 배 이상의 지연이 발생합니다.
결국 Redis처럼 외부 프로세스를 경유하는 경우, 단일 서버에서의 단순 메모리 접근과는 큰 성능 차이가 불가피합니다.
관리 복잡성
Redis를 별도로 운영해야 하기 때문에 관리 복잡성이 늘어납니다.
추가 서버나 컨테이너를 띄워야 하고, 장애 대응·보안 설정·모니터링·백업 같은 관리 포인트가 생깁니다.
규모가 작은 서비스에서는 이런 관리 비용이 성능상의 이점보다 더 크게 다가올 수 있습니다.
즉, Redis는 분명 빠르지만, 단일 서버 + 단순 상태 공유 구조에서는 오히려 성능 저하와 불필요한 복잡성을 초래할 수 있습니다.
당시 서비스 상황

- Nginx 리버스 프록시로 정적 파일, Nest API, WebSocket을 분기
- 단일 서버 환경 (인스턴스 간 세션/상태 공유 요건 없음)
- DB 부하도 Redis 캐시 없이 충분히 감당
즉, 이 단계에서는 서버 내부 메모리(Map 등) 만으로도 서비스가 안정적으로 동작했습니다.
지금은 In-Memory, 나중엔 Redis
당장은 Redis를 도입하지 않고, 언제든 쉽게 붙일 수 있도록 추상화만 해두었습니다.
핵심은 도메인 로직이 저장소 구현을 모르게 하는 것입니다.
Store 인터페이스
도메인 로직이 의존할 공통 인터페이스입니다.
구현체는 In-Memory 버전이든 Redis 버전이든 동일한 인터페이스를 만족하기만 하면 됩니다.
| |
In-Memory 구현 (현재)
현재 서비스 상황에서는 Map 기반 메모리 저장소만으로 충분했습니다.
Nest의 의존성 주입을 통해 Map을 주입받아 사용합니다.
| |
Redis 구현 (향후 확장용)
규모가 커져 여러 서버에서 상태를 공유해야 할 때는 동일한 인터페이스를 구현하는 Redis 저장소로 교체할 수 있습니다.
| |
이러한 설계로 인해 서비스 로직은 QuizZoneStore 인터페이스만 바라봅니다.
따라서 구현체가 메모리이든 Redis이든, 비즈니스 로직은 그대로 유지됩니다.
Nest도 같은 방향입니다
NestJS의 CacheModule은 메모리 캐시 / Redis 캐시를 같은 인터페이스로 다룰 수 있게 합니다.
아래는 공식 문서의 예시 코드입니다.
| |
메시지는 명확합니다. “인터페이스는 고정하고, 구현체만 바꿔라.”
그래서 우리는 Redis가 필요 없었습니다
요약하면, Redis의 본질은 외부에 존재하는 공유 메모리입니다. 하지만 저희는 단일 서버였기 때문에,
- 공유 메모리 자체가 필요 없었고
- 캐시는 서버 메모리(Map) 로 충분했고
- Pub/Sub 역시 여러 서버 간 메시지 브로커가 필요하지 않았습니다
따라서 Redis는 그 시점의 우리에게 과잉 설계였습니다.
Redis가 필요해질 신호
- 다중 인스턴스/오토스케일링으로 세션/상태 공유가 필요한 경우
- DB 핫스팟/지연 증가로 캐시 계층이 필요한 경우
- 마이크로서비스 전환으로 이벤트 브로커(Pub/Sub) 가 필요한 경우
- 대규모 동시성(랭킹, 레이트리밋 등)을 다뤄야 하는 경우
이런 신호가 보이면, 이미 준비해 둔 인터페이스 뒤에 Redis 구현체를 안전하게 붙이면 됩니다.
마무리
기술은 문제 해결을 위해 존재합니다. “남들도 쓰니까”가 아니라, “우리에게 지금 필요한가”를 먼저 묻는 것이 중요합니다.
저희는 당시 Redis를 도입하지 않았고, 그 선택 덕분에 불필요한 복잡성을 피하면서도 추상화를 통해 향후 전환 가능성은 충분히 확보할 수 있었습니다.
