← 프로젝트 목록
운영 · 총학생회 개발팀2026.03 — 2026.05

한국항공대 축제 선착순 티케팅

서비스 제안 · 서버·인프라 설계 · 부하 테스트 · 운영

현장 팔찌 배부를 온라인 선착순 발급으로 바꾼 서비스입니다. 요청은 안내 사이트였지만 티케팅을 직접 제안해 서버와 인프라를 만들고 축제 기간 운영했습니다.

Redis (Lua · Streams)MySQL (RDS)AWS EC2ALBTerraformk6
약 1,300명실사용
0건900매 초과·중복 발급
501msp95 (1,000명 동시 접속)
0건부하 테스트 크래시·타임아웃

프로젝트 목적

축제 팔찌를 현장에서 줄을 세워 나눠 주던 방식을 온라인 선착순 발급으로 바꾸는 것이 목표였습니다. 한정 수량 900매를 정확히 나누는 것과, 오픈 직후 몰리는 요청을 버티는 것을 꼭 지켜야 할 조건으로 정했습니다.

기간2026.03 — 2026.05
소속총학생회 개발팀

담당 역할

  • 서비스 제안과 구조 비교(서버리스 대 Redis)
  • VPC·ALB·EC2·RDS 인프라 설계와 Terraform 코드화
  • Redis Lua 발급 로직과 Streams 기반 보관 구조
  • 배포 전 k6 부하 테스트와 축제 기간 운영

시스템 구조

티케팅 Redis 아키텍처
ALB → 앱 서버 2대 → Redis 발급 → Stream → Archiver → RDS 보관원본 보기 ↗
이렇게 설계한 이유서버리스(API Gateway → Lambda → SQS FIFO → DynamoDB) 구조를 먼저 만들어 부하 테스트하자 3,000건 중 1,389건이 Lambda 동시성 한도(429)로 실패했습니다. 큐를 거치는 구조라 사용자가 발급 결과를 바로 알 수 없었고, 모든 발급이 카운터 하나에 몰렸습니다. 중복 확인·수량 확인·발급을 하나의 원자적 연산으로 묶고 결과를 요청 안에서 바로 돌려줄 수 있는 Redis 구조로 바꿨습니다.

주요 구현

1. JWT·시간대 검증→2. Lua 원자 발급→3. 즉시 응답→4. Stream 보관
  • 앱 서버가 JWT와 학적별 신청 시간대를 확인한 뒤, Redis에 Lua 스크립트를 한 번 호출합니다(1 RTT).
  • 스크립트는 중복 신청 확인 → 잔여 수량 확인 → 발급(순번 증가, 발급 기록, Stream 기록)을 한 번에 실행하고 ISSUED / ALREADY_ISSUED / SOLD_OUT 중 하나를 돌려줍니다.
  • 애플리케이션·Redis·RDS는 Private Subnet에 두고 보안 그룹으로 접근 범위를 나눴습니다.
-- issue.lua  KEYS: count, issued, log / ARGV: user_id, total, requested_at
local prev = redis.call('HGET', KEYS[2], ARGV[1])
if prev then return {2, prev} end                 -- 이미 발급됨
local cnt = tonumber(redis.call('GET', KEYS[1]) or '0')
if cnt >= tonumber(ARGV[2]) then return {0, ''} end  -- 매진
local rank = redis.call('INCR', KEYS[1])
local v = rank .. '|' .. ARGV[3]
redis.call('HSET', KEYS[2], ARGV[1], v)
redis.call('XADD', KEYS[3], '*', 'user_id', ARGV[1], 'rank', rank)
return {1, v}                                     -- 발급 성공

해결 과제

1. 확인과 발급 사이의 틈

Redis Lua · 원자적 연산

과제
오픈 직후 수백 명이 동시에 신청하면 중복 확인, 잔여 수량 확인, 발급이 따로 실행되는 사이에 정원 초과나 중복 발급이 생길 수 있었습니다.
접근
세 단계를 Lua 스크립트 하나로 Redis에서 실행했습니다. Redis는 스크립트를 한 번에 하나씩 실행하므로, 앱 서버가 몇 대든 분산 락 없이 순서와 수량이 지켜집니다.

900매를 초과·중복 발급 없이 마감

2. 발급마다 DB 쓰기를 기다리는 문제

Redis Streams · Archiver

과제
요청마다 발급 내역을 RDS에 바로 쓰면, 요청이 몰리는 순간 DB 쓰기 지연이 그대로 응답 시간에 더해집니다.
접근
발급 결과는 Redis Stream에 기록하고, 별도 Archiver가 읽어 RDS에 저장하게 했습니다. 정확한 발급은 Redis가, 장기 보관은 응답 경로 밖에서 처리합니다.

배포 전 EC2·k6 1,000명 동시 접속에서 p95 501ms, 크래시·타임아웃 0건

남은 한계

  • Redis를 EC2 한 대에 두어, Redis가 멈추면 발급도 멈춥니다. 다시 한다면 복제본과 장애 전환을 두겠습니다.