출석 횟수만 남으면 회원은 기록을 볼 수 있어도 수련 패턴을 발견하기 어렵다. 이 날짜에는 출석 데이터를 보상으로 해석하는 구조와 실제 운영에 필요한 보안을 함께 추가했다.
하루 한 번 출석이라는 제약을 다시 봤다
첫 구현은 같은 회원의 하루 중복 출석을 막는 구조였다. 하지만 도장에서는 같은 날 17:30, 19:00, 21:00 수업을 모두 들을 수 있고, 토요일 오픈 매트도 별도 세션이다. 하루 단위의 유일성만으로는 여러 타임 수련을 남길 수 없다.
그래서 training_sessions를 추가했다. 세션은 도장, 개최 날짜, 정규 수업 또는 오픈 매트 여부, 시작 시각, 실제 개최 여부를 가진다. 출석과 QR 토큰이 세션을 참조하도록 바꾸고, 중복 방지 규칙도 두 갈래로 나눴다.
- 세션이 있는 출석은 같은 회원·같은 세션 조합을 한 번만 허용한다.
- 세션이 없는 예외 출석은 같은 회원·같은 날짜를 한 번만 허용한다.
이 변경으로 같은 수업에 두 번 기록되는 일은 막으면서, 같은 날 여러 수업을 들은 기록은 보존할 수 있게 됐다.
보상을 데이터로 정의했다
보상 조건을 화면 컴포넌트 곳곳의 조건문으로 만들면 새 항목을 추가할 때마다 화면과 서버를 같이 수정해야 한다. 그래서 이름, 설명, 분류, 조건 유형, 조건 값, 아이콘, 애니메이션 키를 가진 데이터 행으로 보상을 정의했다.
| 분류 | 다루는 패턴 |
|---|---|
| 누적 | 첫 출석, 누적 출석 일수, 누적 수업 수 |
| 시간대 | 특정 시간대 참여와 연속 참여 |
| 오픈 매트 | 토요일 첫 참여, 누적·연속 참여 |
| 요일·루틴 | 같은 요일 또는 요일·시간대 조합의 반복 |
| 복귀 | 30일 이상 쉬었다가 다시 수련한 경우 |
| 집중도 | 주간 목표, 월간 수련일·수련 횟수 |
| 특수 패턴 | 생활 시간대 변화와 여러 시간대 참여 |
보상은 회원별로 한 번만 지급하고, 어떤 출석이 보상을 만들었는지도 남긴다. 주간 연속 달성, 월간 배지, 장착 중인 보상도 별도 테이블로 분리했다. 회원 화면에서는 긴 보상 목록보다 현재 얻은 보상과 다음 목표를 먼저 보여 준다.
데이터 변경을 검증했다
기존 출석이 있는 상태에서 세션 모델을 추가했기 때문에, 이전 기록을 깨지 않는지가 중요했다. 테스트에서는 모든 보상 정의가 등록됐는지, 기존 출석을 유지하면서 세션 단위 중복 방지가 적용되는지를 확인했다. 보상 정의와 수여 기록은 서버만 읽고 쓰며, 브라우저에서 데이터베이스를 직접 읽지 않게 했다.
관리자 인증과 키오스크 입력
관리자 PIN 뒤에 패턴 인증을 추가했다. 패턴은 해시로 저장하고 설정·검증 API를 분리했다. 로그인 요청은 IP 기준 5분 동안 최대 8회로 제한하고, 성공 시에는 시도 기록을 비운다. 운영 편의를 위해 잠시 만든 PIN 초기화 경로는 출시 전에 제거했다.
입구 태블릿은 손가락 입력을 기준으로 스캔 영역, 숫자 키패드, 패턴 입력 면적과 간격을 키웠다. 사용자가 현재 QR, PIN, 관리자 패턴 중 무엇을 입력해야 하는지 바로 알 수 있게 했다. 보상 알림은 출석 성공 뒤 보여 주되, 다음 사람의 출석을 오래 막지 않도록 닫기와 다음 보상 동작을 단순하게 유지했다.
다음에는 실제 도장 기록을 보며 NFC 키링, 회원별 주간 목표, 휴관일·수동 수정 같은 예외의 보상 반영, 태블릿 네트워크 오류 대응을 검토할 예정이다.