미지원 기기/브라우저 접속 제한 안내 (PPI-1085)

마지막 업데이트 2026-07-22

43623131 모카 · 2026-06-29 Feature 9 files +584 −11

미지원 기기/브라우저로 수업에 입장하려는 아동에게 이동 전 확인 모달을 띄워, 보호자가 STT·오디오 불안정 가능성을 미리 인지하게 하는 기능. 기존 language-guard-settings 서브피처들과 완전히 동일한 draft/save 패턴을 재사용했고 enabled: false 기본값이라 즉시 영향은 없다. 부수적으로 parseBrowser의 죽은 코드(Samsung/Opera가 항상 Chrome으로 잡히던 순서 버그)를 고쳤는데, 이 함수는 진행자 모니터도 공유해서 써서 리뷰 시 가장 먼저 볼 지점은 이 유틸 수정이 이번 기능과 무관하게 기존 모니터 표시에도 영향을 준다는 것.

무엇이, 왜 바뀌었나

치료사가 매 수업마다 구두로 "이 기기/브라우저는 지원 안 됩니다"를 안내해야 했던 부담을 줄이기 위해, 게스트가 입장 버튼을 누르는 순간 서버에 저장된 규칙과 매칭되면 세션 이동 전에 확인 모달을 띄운다.

흐름 ① — 설정 저장 경로 (관리자 UI → PUT → DynamoDB → GET → 훅)

1

메시지 탭 — ON/OFF · 규칙 · 모델 키워드 편집

components/sections/access-restriction-section.tsx

2

draft 버퍼만 변경 (저장 전 미반영)

setDraftAccessRestrictionEnabled/Rules/DeviceModels

3

PUT — 방어적 파싱 후 머지 저장

PUT /api/language-guard-settings

4

DynamoDB — 로드 시 DEFAULTS 머지

lib/db-queries.ts · getLanguageGuardSettings()

5

게스트 최초 로드 시 자동 반영

hydrateFromServer() (기존 module-level 훅 재사용)

흐름 ② — 런타임 판정 경로 (입장 클릭 → 매칭 → 모달)

1

게스트 기기정보 계산

app/client-guest/page.tsx · parseUserAgent + getDeviceModel()

2

매칭 판정

accessMatchKinddeviceModels 부분일치 / rules OS·browser 일치

3

입장 클릭 시 네비게이션 가로채기

navigateToSession · isAccessRestricted && !compatConfirmedRef.current

4

확인 모달 — 문구 3분기(device/browser/both)

components/ui/access-restriction-confirm-modal.tsx

5

"확인하고 입장" → 재호출(이번엔 통과)

compatConfirmedRef.current = true

코드로 보는 핵심 지점

parseBrowser 순서 교정 — Samsung/Opera가 죽은 코드였다

lib/user-agent-utils.ts const samsungMatch = userAgent.match(/SamsungBrowser\/([\d.]+)/); if (samsungMatch) return `Samsung Internet ${samsungMatch[1]}`; const operaMatch = userAgent.match(/OPR\/([\d.]+)/); if (operaMatch) return `Opera ${operaMatch[1]}`; // ↑ 이 두 체크가 기존엔 Chrome 판별 뒤에 있어서 도달 불가능했음 - // (기존 위치: 아래 Chrome 블록 이후) const chromeMatch = userAgent.match(/Chrome\/([\d.]+)/); if (chromeMatch && !userAgent.includes("Edg")) return `Chrome ${chromeMatch[1]}`;

Chrome 정규식은 !userAgent.includes("Edg")만 걸러내고 SamsungBrowser/OPR 토큰은 걸러내지 않아, 이전 순서에서는 삼성 인터넷·오페라가 항상 "Chrome"으로 오인식됐다. 이 함수는 진행자 모니터의 기기 표시도 공유해서 쓴다.

세션 재확인 안 함 — 사실은 "컴포넌트 생존 기간" 단위

app/client-guest/page.tsx const compatConfirmedRef = useRef(false); // ... if (isAccessRestricted && !compatConfirmedRef.current) { setPendingCompatNav({ roomId, lessonIndex, activityIndex, stepIndex }); setShowCompatModal(true); return; }

sessionStorage가 아니라 컴포넌트 내부 useRef라, 새로고침이나 재마운트가 일어나면 다시 모달이 뜬다. PR 설명의 "같은 세션에서 재확인 안 함"과 정확히는 다르지만 안전한 방향의 차이라 실질적 문제는 아니다.

레이어별 변경 요약

레이어파일핵심 변경
타입types/db/language-guard-settings.types.tsAccessRestrictionRule/Settings, ACCESS_RESTRICTION_ANY_OS="*", DEFAULTS(enabled:false)
유틸lib/user-agent-utils.tsparseBrowser 순서 교정, OS/BROWSER 옵션 상수, getBrowserName()
스토어stores/use-language-guard-settings-store.tsaccessRestriction 적용값 + draft 3종 액션, hydrate/payloadEquals 반영
APIapp/api/language-guard-settings/route.tsPUT에 accessRestriction 방어적 파싱 추가
DBlib/db-queries.tsgetLanguageGuardSettings() 로드 시 DEFAULTS 머지
UI(관리)components/sections/access-restriction-section.tsx (신규)ON/OFF, OS+브라우저 규칙, 기기 모델 키워드 편집
UI(게스트)components/ui/access-restriction-confirm-modal.tsx (신규)device/browser/both 3분기 문구 + 카카오톡 안내
라우팅app/client-guest/page.tsx매칭 판정 accessMatchKind, 네비게이션 가로채기, compatConfirmedRef
등록components/pages/chat-preset.tsx메시지 탭에 섹션 배치(5줄)

리뷰 관전 포인트

동작확인

parseBrowser 재정렬은 session-header.tsx· widgets/monitor/ui/system-status-panel.tsx·host.tsx가 공유 호출하는 getDeviceDisplayString을 거쳐 자동 반영된다. PR 설명의 "모니터 표시도 같이 교정됨"은 코드상 사실 — 별도 배선 없이 부수적으로 기존 화면이 바뀐다는 뜻이니 배포 후 모니터 표시가 달라진 걸 보고 놀라지 않도록 공유해둘 것.

검증없음

compatConfirmedRef는 인메모리 useRef라 새로고침/재마운트 시 초기화된다. "같은 세션 재확인 안 함"이라는 문서상 표현과 달리 실제로는 "컴포넌트 인스턴스 생존 동안만" — 안전한 방향의 차이지만 의도와 구현이 정확히 일치하진 않는다.

구조 — 이미 커버됨

작성자가 우려한 "iOS 모델 식별 불가"는 deviceModels 룰만 무력화되고, OS+브라우저 rulesparseOperatingSystem이 iOS/iPadOS를 정상 판별하므로 영향받지 않는다. 실제 갭이 아니라 이미 설계로 커버된 케이스.

영향범위

browser 규칙엔 와일드카드가 없다(ACCESS_RESTRICTION_ANY_OS는 os 필드 전용). "iOS는 브라우저 무관 전부 제한" 같은 규칙을 만들려면 브라우저 옵션을 하나씩 다 추가해야 해서 관리 UX상 번거로울 수 있음.

검증없음

API PUT 레벨에서 rules/deviceModels 중복 제거를 하지 않는다(대소문자만 다른 키워드, 동일 os+browser 조합 등). 클라이언트 UI는 대소문자 무시 중복 체크를 하지만 API를 직접 호출하면 우회 가능 — 매칭 로직 자체(.some(...))는 중복이 있어도 정확성엔 영향 없어 실질 리스크는 낮음.

관련 문서

관련 문서: 언어 가드 설정 스토어(draft/save) — 코드레벨 동작 흐름 — 이 PR의 설정 저장 경로(흐름①)가 그대로 재사용한 draft/save/hydrateFromServer 패턴의 원본 문서. 호스트 모니터링 claim / 그룹 모니터링 — 코드레벨 동작 흐름parseBrowser 교정이 부수적으로 반영되는 진행자 모니터 기기표시 로직 문서. 영상 종료 이벤트 미발생 분석 & NAVER 인앱 브라우저 대응 — 이번 기능이 대응하려는 "미지원 브라우저 환경" 문제의 선행 사례.