Daeseon Yoo
Back to project
·UX retro·2 min

AI 403을 초대제 안내와 무료 연습 복귀로 바꾸기

AI 사용 권한이 없는 계정에서 서버 403 원문 대신 초대제 설명을 보여 주고, 비용 없는 Practice 흐름으로 돌아갈 수 있게 했다.

문제

AI 기능은 deny-by-default인데, 권한이 없는 사용자가 스파링을 누르면 제품 맥락보다 서버 오류가 먼저 보였다. 실제 client는 이미 HTTP status와 error code를 가진 ApiError를 제공하므로 문자열을 해석할 필요는 없었다.

구현

스파링 시작에서 403 AI_NOT_ALLOWED403 SPARRING_NOT_ALLOWED만 초대제 상태로 분기한다. 작문 체크는 403 AI_NOT_ALLOWED만 같은 상태로 분기하고, 다른 오류는 지역화된 일반 실패 문구로 처리한다. 서버 원문은 스파링 개발 로그에만 남기고 화면에는 한국어·영어 안내와 무료 연습 계속하기 버튼을 보여 준다. 버튼은 /practice로 이동해 쉐도잉·드릴·SRS 같은 비용 없는 기능을 계속 쓸 수 있게 한다. 저장소에 실제 waitlist route/API가 없어서 동작하지 않는 대기 신청은 만들지 않았다.

공용 ApiError가 이미 필요한 statuscode를 export하므로 Track A 소유의 packages/core/src/api/client.ts도 수정하지 않았다.

검증 범위

local mock이 raw marker를 포함한 두 403을 각각 반환하게 하고 iPhone 17 Pro(iOS 26.5)에서 검증했다.

SPARRING_NOT_ALLOWED: invite panel visible; RAW_SPARRING_NOT_ALLOWED_DO_NOT_SHOW hidden; COMPLETED
AI_NOT_ALLOWED: invite panel visible; RAW_AI_NOT_ALLOWED_DO_NOT_SHOW hidden; COMPLETED
compose L3 source assertions passed
$ cd mobile && npx tsc --noEmit
(no stdout; exit 0)

스파링의 무료 연습 버튼은 Practice 화면으로 이동했다. compose 분기는 소스 assertion과 타입 검사까지 확인했다. compose iOS 흐름은 개발 앱이 Metro URL을 잃어 No script URL provided로 제품 분기 전에 중단됐으므로 통과했다고 기록하지 않는다. Android UI도 실행하지 않았다.

구현 커밋: 스파링 f9ef2c4, compose 90810d3.