1️⃣ 문제 상황
Spring Boot + Redis 기반 선착순 강의 신청 시스템을 구현하던 중,
다음과 같은 문제가 발생했다.
❗ 문제 증상
- 사용자가 이미 강의를 신청한 상태
- 다시 상세 페이지(/lectures/{id})에 진입
- "신청하기" 버튼 클릭
👉 결과:
- ❌ "대기열 정보가 없습니다. 다시 신청하기 버튼을 눌러주세요."
- ❌ 다시 대기열 진입 가능
- ❌ 결제 페이지까지 다시 이동 가능
👉 정상이라면?
- "이미 신청한 강의입니다."
- 버튼은 "신청 완료" 상태여야 함
2️⃣ 원인 분석
1. 백엔드는 이미 정상
Postman으로 확인:
GET /api/lectures/1/enroll/me
응답:
{
"status":"ENROLLED",
"lectureId":1,
"lectureTitle":"Redis 기반 대규모 선착순 수강신청 실전"
}
👉 즉, DB 기준으로는 이미 신청된 상태
2. 문제는 프론트(detail.html)
프론트에서는 /queue/me 응답을 이렇게 처리하고 있었다:
if (data.status==="QUEUED") {
...
}else if (data.status==="SUCCESS") {
...
}else {
// ❌ 여기에 걸림
"대기열 정보가 없습니다..."
}
👉 그런데 서버는 ENROLLED를 내려주고 있었음
👉 프론트는 ENROLLED를 모름 → 무조건 else로 떨어짐
💥 핵심 원인
👉 상태값(status)을 프론트에서 처리하지 않아서 발생한 문제
- 백엔드: ENROLLED 정상 반환
- 프론트: ENROLLED를 모름 ❌
3️⃣ 해결 방법
✅ 1. ENROLLED 상태를 프론트에서 처리
공통 처리 함수 추가
function renderEnrolled(message) {
stopPolling();
spinner.classList.add("hidden");
resultArea.classList.remove("hidden");
resultText.innerHTML = `
<div class="mb-2">✅</div>
<span class="text-gray-900 font-bold text-lg">신청 완료</span><br>
<span class="text-xs text-gray-500">${message || "이미 신청한 강의입니다."}</span>
`;
applyBtn.textContent = "신청 완료";
applyBtn.disabled = true;
}
✅ 2. 폴링 함수에 ENROLLED 처리 추가
if (data.status === "ENROLLED") {
renderEnrolled(data.message);
return;
}
✅ 3. 신청하기 버튼 클릭 시에도 처리
const data = await Token.apiFetch(`/api/lectures/${lectureId}/queue`, { method: "POST" });
if (data.status === "ENROLLED") {
renderEnrolled(data.message);
return;
}
✅ 4. 페이지 진입 시에도 체크
window.addEventListener("load", async () => {
const data = await fetchMyQueueStatus();
if (data.status === "ENROLLED") {
renderEnrolled(data.message);
}
});
4️⃣ 결과
이제 동작 흐름은 다음과 같다:
| 상태 | 결과 |
| NOT_ENROLLED | 신청하기 버튼 |
| QUEUED | 대기열 표시 |
| SUCCESS | 결제 페이지 이동 |
| ✅ ENROLLED | 신청 완료 표시 + 버튼 비활성화 |
5️⃣ 배운 점
1. 상태(status)는 반드시 프론트/백엔드 모두 정의해야 한다
- 백엔드만 정의하면 의미 없음
- 프론트도 반드시 처리해야 함
2. “마지막 else”는 항상 의심해야 한다
else {
"대기열 정보 없음"
}
👉 여기에 숨겨진 버그가 많다
3. API 테스트(Postman)는 필수
- DB 상태 vs 화면 상태가 다를 때 👉 Postman으로 확인하면 바로 원인 분리 가능
4. UX는 “데이터”보다 “상태 표현”이 중요하다
- 이미 신청했는데 다시 신청 가능하면 UX 망가짐
- 상태 기반 UI 설계가 핵심
'프로젝트 > 스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글
| ⏰💡 오픈 시간(openAt) 전에는 신청 버튼 막기 + 카운트다운 표시 (0) | 2026.03.07 |
|---|---|
| ⏰🚨 “입장권이 있는데도 apply 페이지에서 입장권 없음 오류 발생” (0) | 2026.03.06 |
| ⏰💡 선착순 강의 Redis 대기열(ZSET) + 입장권 원자화 + 소비(consume) 적용하기 (Lua 1개로 끝) (0) | 2026.02.26 |
| ⏰🚨 선착순 강의 신청 페이지에서 버튼이 동작하지 않던 문제 해결 (0) | 2026.02.23 |
| ⏰💡 소셜 로그인 + 선착순 강의 신청 시스템 통합 (JWT 기반 userId 전환) (1) | 2026.02.23 |