⏰🚨 선착순 강의 신청 페이지에서 버튼이 동작하지 않던 문제 해결

2026. 2. 23. 23:38·프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트

1️⃣ 문제 상황

소셜 로그인 + JWT 기반 인증을 적용한 후,

선착순 강의 시스템의 강의 상세 페이지(/lectures/{id}) 에서

“신청하기” 버튼을 눌러도 아무 반응이 없는 문제가 발생했다.

정상 동작 흐름 (의도한 동작)

  1. 로그인 후 강의 상세 페이지 진입
  2. “신청하기” 버튼 클릭
  3. 대기열 등록 API 호출
  4. 폴링으로 순번 확인
  5. 입장 가능(SUCCESS) 시 결제 페이지 이동

하지만 실제로는:

❌ 버튼 클릭 시 화면 변화 없음

❌ 네트워크 요청 없음

❌ 오류 메시지도 없음


2️⃣ 원인 분석

문제의 핵심은 버튼이 아니라 JavaScript 실행 자체가 중단된 것이었다.

✔️ 핵심 원인 1 : Token is not defined 에러

브라우저 콘솔에 다음 오류가 발생했다 :

Uncaught ReferenceError: Token is not defined

상세 페이지에서는 다음과 같은 코드가 실행되고 있었다 :

Token.saveTokenFromUrl();
Token.getToken();
Token.apiFetch(...);

그러나 token.js 파일에는 Token 객체가 정의되어 있지 않았고,

단순히 토큰을 저장하는 코드만 존재했다.

기존 token.js

const token = searchParam('token')
if (token)localStorage.setItem("access_token",token)

즉, Token이라는 전역 객체가 존재하지 않아

스크립트 실행 중 바로 에러가 발생했고,

그 이후 코드가 실행되지 않았다.

✔️ 핵심 원인 2 : 이벤트 등록 전에 스크립트 실행 중단

버튼 클릭 이벤트는 아래처럼 스크립트 하단에서 등록된다.

applyBtn.addEventListener("click", () => {
	enqueueNew();
});

하지만 위쪽에서 에러가 발생하면:

  • 스크립트 실행 중단
  • 이벤트 등록 코드 실행되지 않음
  • 버튼 클릭 시 아무 반응 없음

👉 즉, “버튼 문제”가 아니라 “이벤트 자체가 존재하지 않는 상태”였다.

✔️ 핵심 원인 3 : th:inline = "javascript" + 템플릿 문자열 충돌

초기 구현에서는 Thymeleaf의 JS 인라인 기능을 사용했다.

<script th:inline="javascript">
	const lectureId= [[${lecture.id}]];
	resultText.innerHTML=`
    <span>...</span>
  `;
</script>

이 구조에서 문제가 발생할 수 있다.

이유

th : inline = "javascript" 는 내부 문자열을 변환하면서 :

  • 따옴표 처리
  • 이스케이프
  • 템플릿 치환

을 수행한다.

특히 아래가 동시에 존재하면 문법 오류 가능성이 높다:

  • Thymeleaf 인라인 JS
  • 백틱(`) 템플릿 문자열
  • HTML 멀티라인 문자열

👉 브라우저에서는 SyntaxError 발생 -> 스크립트 중단

3️⃣ 진단 과정

1. 콘솔 확인 (가장 중요)

F12 -> Console 탭

  • ReferenceError
  • SyntaxError

등이 있으면 스크립트가 실행되지 않은 것이다.

2. 네트워크 요청 확인

F12 -> Network 탭

버튼 클릭 시:

  • API 요청 없음 → 이벤트 미등록 가능성 높음
  • 요청 있음 → 서버 또는 인증 문제

이번 사례는 요청 자체가 없었다.

3. 정적 파일 실제 로딩 내용 확인

브라우저에서 직접 접근 :

http://localhost:8080/js/token.js

👉 서버가 실제로 어떤 JS를 서빙하는지 확인 가능


4️⃣ 해결 방법

✔️ 해결 1: Token 모듈 구현

token.js를 전역 객체 기반 모듈로 변경했다.

window.Token= { saveTokenFromUrl, getToken, apiFetch };

주요 기능

  • URL에서 access token 추출
  • localStorage 저장
  • Authorization 헤더 자동 첨부
  • API 호출 래퍼 제공

✔️ 해결 2: Thymeleaf 인라인 JS 제거

lectureId 전달 방식을 변경했다.

기존 (문제 발생)

const lectureId= [[${lecture.id}]];

개선 (data attribute 사용)

<body data-lecture-id="[[${lecture.id}]]">
const lectureId = document.body.dataset.lectureId;

👉 템플릿 엔진과 JS 파싱 충돌 제거

✔️ 해결 3: JS 캐시 문제 방지

개발 중에는 브라우저 캐시 때문에

옛 JS 파일이 계속 로드되는 문제가 발생한다.

Thymeleaf를 이용해 캐시 무력화:

<script th:src="@{/js/token.js(v=${#dates.createNow().time})}"></script>

🎯 결과 (Result)

수정 후 정상 동작:

  • 버튼 클릭 시 API 요청 발생
  • 대기열 등록 성공
  • 폴링 시작
  • SUCCESS 시 결제 페이지 이동

🏁 회고 (Retrospective)

이번 문제는 UI나 서버 문제가 아니라

프론트 스크립트 실행 흐름을 이해하지 못하면 찾기 어려운 문제였다.

특히 다음 사실을 명확히 깨달았다:

JavaScript는 위에서 에러가 나면 아래 코드는 “존재하지 않는 것”과 같다.

이 경험을 통해:

  • 콘솔 확인의 중요성
  • 이벤트 등록 시점 이해
  • 템플릿 엔진과 JS 분리 설계의 필요성

을 체감할 수 있었다.

'프로젝트 > 스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글

⏰🚨 선착순 강의 신청에서 “이미 신청했는데 다시 신청 가능?” 문제 해결  (0) 2026.03.06
⏰💡 선착순 강의 Redis 대기열(ZSET) + 입장권 원자화 + 소비(consume) 적용하기 (Lua 1개로 끝)  (0) 2026.02.26
⏰💡 소셜 로그인 + 선착순 강의 신청 시스템 통합 (JWT 기반 userId 전환)  (1) 2026.02.23
⏰🚨 동시성 테스트 중 500 Internal Server Error 발생 원인과 해결  (1) 2026.02.23
⏰🚨 Enroll 동시성 테스트에서 신청이 거의 발생하지 않은 문제(JMeter)  (0) 2026.02.15
'프로젝트/스프링 부트 3 백엔드 개발자 되기 Blog + 선착순 강의 프로젝트' 카테고리의 다른 글
  • ⏰🚨 선착순 강의 신청에서 “이미 신청했는데 다시 신청 가능?” 문제 해결
  • ⏰💡 선착순 강의 Redis 대기열(ZSET) + 입장권 원자화 + 소비(consume) 적용하기 (Lua 1개로 끝)
  • ⏰💡 소셜 로그인 + 선착순 강의 신청 시스템 통합 (JWT 기반 userId 전환)
  • ⏰🚨 동시성 테스트 중 500 Internal Server Error 발생 원인과 해결
hak0622
hak0622
개발하면서 “이게 뭐지?”라는 순간마다 궁금한 점을 바탕으로 정리한 개발 블로그입니다.
  • hak0622
    궁금한 개발 이야기 Why?
    hak0622
  • 전체
    오늘
    어제
    • 분류 전체보기 (68)
      • 공부 (36)
        • 1. 자바 ORM 표준 JPA 프로그래밍 - 기본.. (35)
        • 시험 (1)
      • 프로젝트 (32)
        • 스프링 부트 3 백엔드 개발자 되기 Blog + .. (32)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hak0622
⏰🚨 선착순 강의 신청 페이지에서 버튼이 동작하지 않던 문제 해결
상단으로

티스토리툴바