게스트 페이지 브라우저 자동 번역 차단 (PPI-1186)

마지막 업데이트 2026-08-10

c8831da7 charles-na · 2026-08-10 Fix 2 files +15 −3

게스트(아동) 접속 화면에서 브라우저 자동 번역이 개입해 안내 Alert 문구가 의도와 다르게 바뀌던 문제를 차단한다. 근본 원인은 root layout이 한국어 콘텐츠를 <html lang="en">으로 선언하고 있던 것이고, 이번 PR은 ① lang 값을 ko로 정정하고 ② client-guest 서브트리에 번역 제외 표시를 추가하는 2단 방어다.

리뷰 시 가장 먼저 볼 지점은 apps/web/app/layout.tsxlang 변경이 앱 전역(admin 포함)에 적용된다는 점, 그리고 google: "notranslate" 메타가 Chrome/Google 계열 전용 규약이라 iPad Safari 커버를 translate="no"에 의존한다는 점이다.

무엇이, 왜 바뀌었나

PPI 게스트 화면의 모든 텍스트는 한국어인데, root layout은 Next.js 기본 스캐폴드 값인 lang="en"을 그대로 유지하고 있었다. 브라우저는 이 선언을 보고 "사용자 언어와 다른 페이지"로 판정해 번역 배너를 띄우거나, 자동 번역 설정이 켜진 기기에서는 조용히 번역해 버린다. 그 결과 기획된 안내 문구(FormAlert 등)가 재번역된 문장으로 바뀌어 아동에게 잘못된 안내가 전달될 수 있었다.

흐름 — 브라우저가 번역을 결정하는 경로

1

페이지 언어 선언 확인

apps/web/app/layout.tsx

<html lang>이 사용자 브라우저 언어와 다르면 번역 후보로 판정. enko로 정정해 이 단계에서 탈락시킨다.

2

문서 단위 번역 제외 메타

apps/web/app/client-guest/layout.tsxmetadata.other

<meta name="google" content="notranslate">가 head에 렌더. Chrome/Google 번역이 문서 전체를 대상에서 제외.

3

엘리먼트 단위 번역 제외 속성

client-guest 서브트리 래퍼translate="no"

HTML 표준 속성이라 Google 규약을 따르지 않는 브라우저(Safari 등)에도 신호가 전달된다. 게스트 화면 텍스트 전체가 대상.

코드로 보는 핵심 지점

① 근본 원인 — root layout의 lang 값

이 한 줄이 번역 유발의 시작점이다. 나머지 변경은 이 위에 얹는 보강이다.

apps/web/app/layout.tsx export default function RootLayout({ children }: { children: ReactNode }) { return ( - <html lang="en"> + <html lang="ko"> <head>

② 세그먼트 metadata — Chrome 계열 번역 제외

Next.js Metadata API에 번역 제어 전용 필드가 없어 other로 임의 메타를 주입한다. client-guest/layout.tsx"use client"가 없는 서버 컴포넌트라 metadata export가 유효하고, 하위 page.tsx 2개는 모두 client component라 metadata override 충돌도 없다.

apps/web/app/client-guest/layout.tsx -import type { Viewport } from "next"; +import type { Metadata, Viewport } from "next"; import KakaoRedirectGuard from "./kakao-redirect-guard"; +export const metadata: Metadata = { + other: { + google: "notranslate", + }, +};

③ 엘리먼트 단위 번역 제외 — 크로스 브라우저 보강

display: contents가 핵심이다. 이게 없으면 게스트 화면의 flex/grid 레이아웃 사이에 익명 블록이 끼어들어 기존 스타일이 깨질 수 있다.

apps/web/app/client-guest/layout.tsx <KakaoRedirectGuard> <PreventPinchZoom /> <LogRocketGuard /> - {children} + <div + translate="no" + className="notranslate" + style={{ display: "contents" }} + > + {children} + </div> </KakaoRedirectGuard>

레이어별 변경 요약

레이어파일핵심 변경
앱 전역 레이아웃 apps/web/app/layout.tsx <html lang="en"><html lang="ko">. 번역 유발 근본 원인 제거 (+1/−1)
게스트 세그먼트 레이아웃 apps/web/app/client-guest/layout.tsx metadata.other.google = "notranslate" 추가 + 하위 트리를 translate="no"/notranslate 래퍼로 감쌈 (+14/−2)

리뷰 관전 포인트

동작 확인

iPad Safari 실기기 검증이 필수. <meta name="google" content="notranslate">는 Google이 정의한 규약이라 Safari 번역 기능이 이를 존중한다는 보장이 없다. 아동측 주 사용 기기가 iPad인 만큼, Safari에서의 실효는 lang="ko" + translate="no" 조합에 달려 있다. 자동번역 ON 상태로 /client-guest에 접속해 안내 Alert 원문이 유지되는지 직접 확인해야 한다.

영향 범위

lang 변경은 게스트 화면뿐 아니라 admin(/main/*) 전체에 적용된다. 렌더링 로직에는 관여하지 않지만, 브라우저 기본 폰트 폴백·하이픈네이션·IME 힌트·스크린리더 발음이 영향을 받는다. 모든 화면이 한국어이므로 의미상 정확한 값이라 되돌릴 이유는 없으나, 배포 후 admin 화면도 한 번 훑어보는 편이 안전하다.

구조

방어가 게스트 세그먼트에만 걸려 있다. 진행자 모니터(/main/meet, monitor-dashboard)는 번역 제외 표시가 없어 lang="ko" 정정에만 의존한다. 진행자는 성인이고 번역을 수동으로 끌 수 있으므로 현재 범위 판단은 합리적이지만, 동일 증상이 모니터에서 보고되면 같은 래퍼를 재사용할 수 있도록 컴포넌트화 여지를 남겨둘 만하다.

저위험

롤백이 단순하다. 두 변경 모두 부수효과 없는 선언적 속성이고, 상태·네트워크·미디어 경로를 건드리지 않는다. display: contents 덕분에 레이아웃 영향도 없어 문제 시 커밋 revert만으로 원복된다. tsc --noEmit 통과 확인됨.

잔여 리스크

translate="no"번역 실행을 막는 신호일 뿐 강제 차단이 아니다. 사용자가 명시적으로 "이 페이지 번역"을 실행하는 경로까지 100% 막지는 못하는 브라우저가 있을 수 있다. 아동이 직접 그 조작을 할 가능성은 낮지만, 보호자 기기 설정에 따라 재발 신고가 들어올 여지는 남는다.

검증 체크리스트

항목확인 경로상태
타입 체크tsc --noEmit통과
iPad Safari 자동번역 ON — 안내 Alert 원문 유지/client-guest미확인
Android Chrome 자동번역 ON — 동일 확인/client-guest미확인
수업 진행 중 텍스트 번역 안 됨/client-guest/session미확인
번역 배너 자체가 뜨지 않음 (lang 반영 확인)전 화면미확인
lang 전역 변경 후 admin 화면 이상 없음/main/meet미확인

관련 문서

참조

PR #976 · 브랜치 PPI-1186develop · 커밋 c8831da7