개인 프로젝트2026.07
LineUp WebFlux · Redis 기반 고성능 대기열 서버
티켓 오픈처럼 접속이 한꺼번에 몰릴 때, 도착한 순서대로 줄을 세우고 정해진 인원만 입장 토큰을 받아 들어가게 하는 가상 대기열 서버다. Spring WebFlux와 Reactive Redis로 블로킹 없이 만들었다.
GitHub ↗
한눈에 보기
기간 2026.07
형태 개인 프로젝트
기술 Java 21 · Spring Boot 4.1 · Spring WebFlux · Reactive Redis(Upstash) · k6 직접 만든 기능 · 대기열 등록 · 순번 조회 · 이탈 API· 3초마다 앞에서 5명씩 입장을 허용하는 스케줄러· 5분짜리 입장 토큰 발급과 예매 페이지 보호 필터· 브라우저는 안내 페이지로, API는 JSON으로 나눠 주는 오류 응답· k6 부하 테스트 시나리오
화면 실제 서버(Spring WebFlux)를 로컬 Redis에 붙여 띄우고 찍었다. 대기 인원은 테스트용 사용자로 채우고 시각은 20시로 맞췄다. k6 결과는 저장소 README의 실행 로그
대기 화면
입장 허용
입장 완료
토큰 없음 (401)
만료된 토큰 (403)
k6 부하 테스트
최고 5,000명 · 121,420건 · p95 3.25ms · 실패 0%
← →
흐름 대기 → 입장 흐름 WAITING 대기(ZSET 순번) → ALLOWED 입장 허용 → TOKEN 입장 토큰 5분 WAITING → ALLOWED 3초마다 앞에서 5명씩 꺼내 입장 허용 — 분당 약 100명 토큰 없음 → 401 안내 페이지 예매 페이지에 토큰 없이 오면 303으로 안내 페이지로 보냄 만료 · 잘못된 토큰 → 403 안내 페이지 Redis에 없는 토큰이면 303으로 안내 페이지로 보냄 대기 취소 → NOT_FOUND 대기열에서 빼고, 이후 순번 조회는 대기 없음으로 응답 대기 화면은 1초마다 순번을 묻고, 입장 토큰을 받으면 예매 페이지로 넘어간다.
문제 해결 과정 도착 순서대로 줄 세우기 — Redis ZSET 문제
오픈 순간 몰린 요청을 그대로 받으면 서버가 무너지고, 누가 먼저 왔는지도 지켜지지 않는다
해결 — 무엇을 바꿨나
등록 시각을 점수로 ZADD해 도착 순서를 지키고, 순번은 ZRANK로 바로 계산했다. 3초마다 ZPOPMIN으로 앞에서 5명을 꺼내 입장 허용 집합으로 옮겨, 보호된 페이지로 들어가는 양을 일정하게 제한했다
결과
입장은 분당 약 100명으로 일정하고, 나머지는 1초마다 자기 순번을 확인하며 기다린다
입장 토큰으로 예매 페이지 지키기 문제
대기열을 거치지 않고 예매 페이지 주소로 바로 들어오면 줄 세우기가 의미 없어진다
해결 — 무엇을 바꿨나
입장이 허용된 사용자에게만 5분짜리 UUID 토큰을 Redis에 발급하고, WebFilter가 예매 페이지 요청마다 토큰을 확인해 없으면 401, 만료되거나 잘못된 토큰이면 403 안내 페이지로 돌려보냈다
결과
토큰 없이 들어온 요청은 예매 페이지에 닿지 못한다
블로킹 없이 처리하고 부하로 확인 문제
대기열 서버 자체가 몰린 요청에 막히면 줄을 세울 수도 없다
해결 — 무엇을 바꿨나
등록 · 조회 · 이탈 전 과정을 ReactiveRedisTemplate과 Mono로 이어 블로킹 호출을 없앴다. k6로 50초 동안 가상 사용자를 300명부터 5,000명까지 늘리는 시나리오 네 가지에 실패율 1% 미만 · p95 200ms 미만 기준을 걸었다
결과
README의 k6 결과 — 5,000명 · 요청 121,420건에서 p95 3.25ms, 실패 0건
설계 결정 오류 응답 나누기 브라우저 요청은 303으로 오류 안내 페이지(401 · 403 · 404 · 500)로 보내고, API 요청은 JSON으로 응답한다.
대기 취소 대기를 취소하면 ZREM으로 줄에서 바로 빼, 뒤 사람들의 순번이 그만큼 당겨진다.
검증 k6 부하 테스트 10초 동안 최고 인원의 1/6 → 30초 동안 최고 인원 → 10초 동안 0명으로 늘렸다 줄이는 시나리오를 300 · 1,000 · 3,000 · 5,000명으로 돌렸다. 네 번 모두 실패 0건, p95 4ms 이내(5,000명: 평균 1.61ms · 최대 38.85ms · 초당 2,454건, 저장소 README 로그).
← 프로젝트 목록으로