
1. 왜 Redis인가?
- 빠른 성능: Redis는 메모리 기반 데이터 저장소이기 때문에 대기열 처리 속도가 매우 빠릅니다.
- 명령어의 다양성: LPUSH, RPOP, BLPOP 등을 사용해 큐(queue) 구조를 손쉽게 구현할 수 있습니다.
- 분산 환경에서 유용: 여러 서버가 동시에 대기열을 공유할 수 있어, 대규모 시스템에서도 적용 가능.
📌 구현 시나리오
- 유저가 접속 요청을 하면 Redis의 대기열 리스트에 유저 ID를 넣음 (LPUSH).
- 서버는 일정 간격으로 큐에서 유저를 하나씩 꺼내어 자원을 할당 (RPOP).
- 특정 시간 동안 응답이 없거나 실패하면 다시 큐에 넣을 수 있음 (optional).
🗃️ Redis 구조 설계
| Key 이름 | 예시 설명 |
| waiting_queue | 접속 대기열을 저장하는 리스트 |
| allowed_users | 접속 허용된 사용자 저장용 Set 또는 TTL 기반 Key |
✅ 주요 Redis 명령어
LPUSH waiting_queue user_123 # 대기열 앞쪽에 삽입
RPOP waiting_queue # 대기열 뒤쪽에서 추출
LLEN waiting_queue # 대기열 길이 확인
SADD allowed_users user_123 # 접속 허용 사용자 등록
EXPIRE allowed_users 60 # 60초 후 자동 삭제
💻 Java(Spring Boot) 예제
1. 의존성 설정 (build.gradle)
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
2. RedisConfig.java
@Configuration
public class RedisConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory("localhost", 6379);
}
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}
3. 대기열 서비스 구현
@Service
public class WaitingQueueService {
private final StringRedisTemplate redisTemplate;
private static final String QUEUE_KEY = "waiting_queue";
private static final String ALLOWED_KEY = "allowed_users";
private static final long ALLOW_TTL_SECONDS = 60;
public WaitingQueueService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void enqueue(String userId) {
redisTemplate.opsForList().leftPush(QUEUE_KEY, userId);
}
public String dequeue() {
return redisTemplate.opsForList().rightPop(QUEUE_KEY);
}
public long getQueueSize() {
return redisTemplate.opsForList().size(QUEUE_KEY);
}
public void allowUser(String userId) {
redisTemplate.opsForSet().add(ALLOWED_KEY, userId);
redisTemplate.expire(ALLOWED_KEY, Duration.ofSeconds(ALLOW_TTL_SECONDS));
}
public boolean isAllowed(String userId) {
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(ALLOWED_KEY, userId));
}
}
4. REST 컨트롤러 예시
@RestController
@RequestMapping("/connect")
public class ConnectController {
private final WaitingQueueService queueService;
public ConnectController(WaitingQueueService queueService) {
this.queueService = queueService;
}
@PostMapping("/request")
public ResponseEntity<String> request(@RequestParam String userId) {
queueService.enqueue(userId);
return ResponseEntity.ok("접속 요청 완료: 대기열에 등록되었습니다.");
}
@GetMapping("/status")
public ResponseEntity<String> status(@RequestParam String userId) {
if (queueService.isAllowed(userId)) {
return ResponseEntity.ok("접속 가능");
} else {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("대기 중");
}
}
@Scheduled(fixedRate = 1000)
public void processQueue() {
String userId = queueService.dequeue();
if (userId != null) {
queueService.allowUser(userId);
System.out.println("접속 허용: " + userId);
}
}
}
⚠️ 고려해야 할 점
- 중복 요청 방지: 대기열에 중복 삽입을 막기 위해 중복 체크용 Set을 둘 수 있습니다.
- TTL 설정: 허용된 유저가 일정 시간 내 접속하지 않으면 자동 만료.
- 스케일링: 여러 서버에서 동시에 Redis 대기열을 사용 가능.
🏁 정리
Redis를 활용하면 접속 대기열을 빠르고 간단하게 구현할 수 있으며, 분산 환경에서도 확장성이 뛰어납니다. 스프링에서 StringRedisTemplate을 이용하면 다양한 방식으로 유저 흐름을 제어할 수 있어, 게임 서버, 이벤트 참가 제한, API Rate Limiting 등에 효과적으로 활용할 수 있습니다.
'데이터베이스 > Redis' 카테고리의 다른 글
| redisson: Redis를 분산형 Java 객체로 확장하다 (2) | 2025.08.30 |
|---|---|
| Redis 사용 시 주의할 점과 구조 설계 팁 (0) | 2025.05.27 |
| Redis로 캐시 구현하는 방법 (0) | 2025.05.10 |
| Redis 자료구조 완벽 정리: Strings, Lists, Sets, Hashes, Sorted Sets (0) | 2025.05.05 |
| Redis와 AWS SQS (0) | 2025.04.17 |