기본 QR 출결 뒤에는 “스캔됐다”와 “출석이 저장됐다”를 분명히 구분하는 데 집중했다. 입구에서는 여러 회원이 연달아 태블릿을 사용하므로, 한 사람의 상태가 다음 사람의 흐름을 방해하지 않아야 했다.
스캔 상태를 나눴다
스캔 화면을 시작 중, 스캔 중, 일시 정지, 오류 상태로 나눴다. QR을 읽으면 즉시 성공 화면을 띄우지 않고 스캐너를 멈춘 뒤 서버 응답을 기다린다. 저장 성공 응답을 받은 뒤에만 이름, 체크 표시, 완료음과 진동을 보여 준다.
네트워크 오류나 토큰 만료가 있을 때 QR이 카메라에 잡혔다는 사실만으로 성공처럼 보이면 안 된다. 실패한 경우에는 연결 확인과 재시도라는 다음 행동을 보여 주고, PIN 출석으로 전환할 수 있게 했다. 처리 후 약 1.4초 뒤 스캐너를 재시작하며, 중복 요청 방지 플래그로 스캔 콜백이 겹치지 않게 했다.
회원의 확인 흐름
QR만 보여 주면 회원은 출석됐는지 알 수 없다. 회원 대시보드와 QR 화면에 오늘 출석 완료 여부와 저장 시각을 함께 표시했다.
- 처음 사용하는 회원은 본인 PIN을 설정한다.
- 이후 PIN으로 로그인해 자신의 이름을 확인한다.
- 내 QR을 열고 태블릿에 보여 준다.
- 태블릿 처리 뒤 회원 화면에서 오늘 상태와 시간을 확인한다.
관리자는 회원별 PIN을 설정·갱신할 수 있다. 운영자가 해결할 수 있는 경로는 남기되, PIN을 평문으로 관리하는 방식은 사용하지 않았다.
태블릿에서 오류를 행동으로 바꿨다
입구 태블릿에는 스캔 영역을 중심으로 두고 카메라 전환·전체 화면·PIN 입력만 가까운 곳에 배치했다. 권한 거부, 장치 없음, 다른 앱의 카메라 점유는 원인이 다르므로 안내도 분리했다.
- 권한 거부: 브라우저 사이트 설정에서 카메라 허용 후 재시도
- 장치 없음: 연결 확인 또는 PIN 출석
- 다른 앱 점유: 해당 앱을 종료한 뒤 재시도
후면 카메라가 열리지 않는 기기에서는 전면 카메라도 시도한다. PIN 입력창이 열린 동안에는 QR 콜백이 출석 요청을 보내지 않게 해 두 입력 경로가 충돌하지 않도록 했다.
PWA와 데이터 갱신
웹 앱 매니페스트, 아이콘, 서비스 워커를 추가해 설치형 앱처럼 열 수 있게 했다. 정적 자산은 먼저 확보하고, 출석 현황처럼 변하는 데이터는 오래된 캐시가 남지 않게 했다.
출석 처리 뒤에는 리더보드와 관리자 현황이 갱신돼야 한다. 그래서 API 변경 직후 관련 데이터를 재검증하고, 월간 리더보드에는 짧은 캐시를 적용했다. 매 요청마다 무거운 집계를 하지 않으면서도 태블릿과 관리자 화면의 차이가 오래 남지 않게 하는 절충이다.
브랜드 시작 화면도 추가했지만, 매 페이지 이동마다 재생하면 입구에서는 기다림이 된다. 한 앱 세션에 한 번만 재생하고 시간을 2.5초 수준으로 줄였다. 세로형 휴대폰에서는 영상이 잘리지 않도록 비율도 조정했다.
이 날짜에는 QR 저장 완료 뒤에만 성공이 보이는지, PIN과 QR이 같은 출석 규칙을 따르는지, 카메라 오류에서 다음 행동이 분명한지, 설치형 앱의 시작과 갱신이 과도하게 느리지 않은지를 확인했다.