3줄 요약
- ProseMirror 같은 리치 에디터를 자동화하는 중 화면이 "서버 장애" 안내로 바뀌었는데, 실제 원인은 서버가 아니라 켜져 있던 크롬 자동번역이었다.
- 판별식은 하나다. 페이지 이동 없이 DOM만 에러 화면으로 바뀌면 서버 장애가 아니라 클라이언트 크래시다. framenavigated 이벤트로 가른다.
- notranslate 메타 태그와 --disable-features=Translate 기동 플래그는 둘 다 무효였다. 해결하려면 프로필의 Preferences 파일 자체를 고쳐야 한다.
| 질문 | 답 |
|---|---|
| "서버 장애" 화면의 정체는 무엇인가 | 크롬 자동번역이 켜진 상태에서 리치 에디터가 텍스트 노드 교체로 크래시난 것 |
| 처음에 무엇으로 오진했나 | "짧은 시간에 편집 세션을 여러 번 열어 제한에 걸렸다" — 쿨다운으로 시간을 썼다 |
| 서버 장애와 어떻게 구분하는가 | 페이지 이동 없이 DOM만 바뀌면 클라이언트 크래시다. framenavigated로 확인한다 |
| 번역이 켜졌는지 어떻게 아는가 | documentElement.className에 translated-ltr, querySelectorAll('font').length > 0 |
| notranslate 메타로 막을 수 있는가 | 안 된다. 크롬을 끈 상태에서 프로필 Preferences를 직접 고쳐야 한다 |
이 글대로 하면, 자동화가 에러 화면을 만났을 때 몇 시간을 대기하는 대신 계측 한 번으로 서버 문제인지 클라이언트 크래시인지를 가르고 프로필 설정을 고쳐 같은 날 작업을 끝낼 수 있다.
리치 에디터 자동화 중 갑자기 뜨는 "서버 장애" 화면의 정체는 무엇인가?
크롬 자동번역이 켜진 상태에서 리치 에디터가 크래시한 화면이다. ProseMirror 계열 에디터를 Playwright로 자동화하던 중, 텍스트를 선택하거나 타이핑하는 순간 화면이 서버 장애 안내로 바뀌는 일이 반복됐다. 우리 프로필에는 사이트별 "항상 번역" 설정(translate_allowlists)이 들어 있었고, 그 결과 편집 화면의 텍스트 노드가 번역된 텍스트로 교체됐다. 그러면 에디터의 문서 모델과 실제 DOM이 어긋나 선택 확장이나 타이핑 순간 위치 매핑에서 예외가 나고, 그 예외를 프레임워크의 에러 경계가 받아 화면을 장애 안내로 대체한다.
특징적인 조합이 하나 있었다. 읽기는 되는데 편집만 죽는다. DOM만 읽으면 본문 36블록이 그대로 읽혔지만, 편집을 시작하는 순간 화면이 바뀌었다. 자동화 스크립트에는 아래 형태로 기록됐다.
GATE FAIL: 선택이 어긋났다(선택된 것: '\n죄송합니다, 지금 장애가 발생하고 있습니다.\n복구 작')
왜 레이트리밋으로 오진하기 쉬운가?
정황이 그렇게 읽히기 때문이다. 아래가 실제로 밟은 오진 사슬이다.
| 단계 | 그때 한 판단 | 왜 틀렸나 |
|---|---|---|
| 1. 관찰 | 공개 페이지는 200인데 편집만 실패한다 | 관찰 자체는 사실이었다 |
| 2. 오진 | "짧은 시간에 편집 세션을 15회 열어 서비스가 편집을 제한했다" | 근거가 정황뿐이었다. 서버는 아무 요청도 거절하지 않았다 |
| 3. 처방 | 쿨다운(대기) | 클라이언트 크래시라 아무리 기다려도 화면은 바뀌지 않는다 |
| 4. 계측 | framenavigated를 걸고 단계마다 상태를 찍었다 |
여기서 결론이 뒤집혔다 |
3번이 가장 비싼 칸이다. 원인을 서버로 잘못 짚으면 대기와 재시도만 반복하고, 그 사이 상태 기록에도 틀린 원인이 남는다. 계측 한 번이 대기 몇 시간보다 싸다는 것이 이 사건에서 남은 규율이다.
서버 장애와 클라이언트 크래시를 어떻게 구분하는가?
페이지 이동 없이 DOM만 에러 화면으로 바뀌면 서버 장애가 아니라 클라이언트 크래시다. 실제 서버 장애나 리다이렉트라면 브라우저가 새 페이지로 내비게이션을 한다. 반면 번역발 크래시는 같은 페이지 안에서 DOM만 교체될 뿐 내비게이션이 없다. 그래서 page.on('framenavigated') 이벤트가 발생했는지를 보면 둘을 즉시 가를 수 있다.
계측 결과는 아래와 같았다. 각 행은 조작 한 단계이고, "editor 존재"는 에디터 DOM이 남아 있는지, "네비게이션"은 그 사이 framenavigated가 발생했는지다.
| 단계 | editor 존재 | 네비게이션 |
|---|---|---|
| 로드 직후 | 있음 | 최초 3회 |
| 본문 클릭 | 있음 | 없음 |
| 마우스 down | 있음 | 없음 |
| 드래그 up 직후 | 없음 | 없음 |
마지막 행이 결론이다. 내비게이션 없이 에디터가 사라졌으므로 서버 응답으로는 설명되지 않는다.
자동번역이 켜져 있는지는 어떻게 점검하는가?
세 값을 본다. 크롬 자동번역은 번역된 텍스트를 <font> 태그로 감싸고 문서 루트에 translated-ltr 클래스를 붙이기 때문에, 아래 한 번의 평가로 확인된다.
({
cls: document.documentElement.className, // translated-ltr 가 있으면 번역됨
font: document.querySelectorAll('font').length, // 0 보다 크면 번역됨
lang: document.documentElement.lang // 원문 언어가 아니면 번역됨
})
우리 환경에서 번역이 켜진 상태의 값은 translated-ltr 클래스가 붙어 있고 <font> 78개, lang=ko였다. 번역을 끈 뒤에는 <font> 0개, lang=ja였다. 판별식은 이렇게 정리된다. font 개수가 0이 아니면 그 페이지에서는 리치 에디터를 조작하지 않는다. 이 검사를 자동화 시작 지점의 게이트로 넣어두면 크래시가 난 뒤에 추적하는 대신 조작 전에 멈춘다.
notranslate 메타·기동 플래그는 왜 안 먹히는가?
사이트별 "항상 번역" 프로필 설정이 둘을 모두 이기기 때문이다. 실측 결과는 아래와 같았다.
| 시도 | 결과 |
|---|---|
<meta name="google" content="notranslate">를 문서 시작 전 주입 |
실패. translated-ltr 그대로, <font> 78개 |
기동 플래그 --disable-features=Translate,TranslateUI |
실패. 동일 |
크롬 종료 상태에서 프로필 Preferences를 직접 수정 |
성공. <font> 0개, lang=ja |
실제로 번역 여부를 결정하는 것은 프로필에 저장된 설정이고, 이 설정이 메타 태그나 기동 플래그보다 우선한다. 저장소의 scripts/note/disable_translate_pref.py(프로필의 자동번역 설정을 끄는 스크립트)가 하는 일은 아래 네 가지다.
sites = {"note.com": "0", "editor.note.com": "0"}
for scope in (d, d.get("account_values", {})):
if "translate_allowlists" in scope:
scope["translate_allowlists"] = {} # "항상 번역" 해제
bl = scope.get("translate_blocked_languages")
if isinstance(bl, list) and "ja" not in bl:
bl.append("ja") # 이 언어는 번역하지 않음
if "translate_site_blocklist_with_time" in scope:
scope["translate_site_blocklist_with_time"].update(sites)
d.setdefault("translate", {})["enabled"] = False
d.setdefault("account_values", {}).setdefault("translate", {})["enabled"] = False
ja와 사이트 이름은 우리 대상에 맞춘 값이므로 자기 환경의 원문 언어와 도메인으로 바꿔 쓴다. 설정은 account_values 아래에도 따로 있어서 두 곳을 함께 고쳐야 한다. 이 스크립트는 시작 시 /json/version 응답으로 크롬이 떠 있는지 검사하고 떠 있으면 GATE FAIL을 찍고 종료한다. 크롬이 살아 있는 상태에서 고치면 종료 시점에 메모리에 있던 설정으로 파일이 덮이기 때문이다. 원본은 Preferences.bak_notranslate로 백업한다.
고친 뒤 무엇을 확인하고 재개하는가?
번호대로 하면 설정 수정부터 재개까지가 한 줄로 이어진다.
- 자동화용 크롬을 CDP
Browser.close로 정상 종료한다. 이름 단위 일괄 종료는 같은 이름의 다른 작업까지 죽인다. python -X utf8 scripts/note/disable_translate_pref.py를 실행한다. 크롬이 떠 있으면 게이트에서 멈춘다.python -X utf8 scripts/note/restart_chrome_notranslate.py로 재기동한다. 이 스크립트는 창을 화면 밖(--window-position=-2400,0)에 띄워 사용자의 화면을 뺏지 않는다.- 로그인 세션이 살아 있는지 확인한다. 프로필을 그대로 쓰므로 유지되는 것이 정상이고, 재기동 스크립트는 이 확인을 통과해야 종료 코드 0을 낸다.
- 대상 페이지에서 앞의 자동번역 게이트를 돌린다.
<font>0개,lang이 원문 언어면 통과다. - 편집을 재개한다. 선택은 마우스 드래그 대신 본문 실클릭으로 포커스를 잡고, Selection API로 캐럿을 놓고, Shift와 방향키로 확장하는 순서로 바꿨다. 좌표 의존이 사라져 두 줄에 걸친 문구도 정확히 잡힌다.
6번에 붙는 부수 실측이 하나 있다. 편집 전 실클릭이 필요하다. 실클릭 없이 Selection API로만 캐럿을 옮기면 Enter와 타이핑이 전혀 먹지 않았다(블록 수 36 그대로, 삽입한 앵커 0개). 이 절차를 적용한 뒤 실패하던 교정 6건이 모두 성공했고, 로그아웃 독자 시점에서도 6편 전부 HTTP 200에 대상 링크가 문서당 1개씩 정상 앵커로 잡혔다.
이 방법이 통하지 않는 경우는 무엇인가?
이 처방은 "번역이 원인"인 경우에만 통한다. 게이트에서 <font>가 0이고 translated-ltr도 없는데 같은 크래시가 난다면 원인은 다른 곳에 있다.
| 실패 조건 | 그때 무엇을 하는가 |
|---|---|
| 게이트는 깨끗한데 편집 중 크래시가 난다 | 번역 외의 DOM 변경 요인을 찾는다. 확장 프로그램, 광고 차단기, 접근성 도구가 후보다 |
| Preferences를 고쳐도 다시 번역된다 | 크롬이 떠 있는 상태에서 고쳤을 가능성이 높다. 종료 후 다시 고치고 account_values 쪽도 확인한다 |
| 재기동 후 로그인이 풀렸다 | --user-data-dir 값이 원래 프로필과 같은지 본다 |
| 실클릭을 넣어도 타이핑이 안 먹는다 | 클릭 좌표가 본문 영역 안인지, 오버레이가 덮고 있지 않은지 확인한다 |
framenavigated가 실제로 발생했다 |
클라이언트 크래시가 아니다. 이 글의 처방 대신 응답 코드부터 본다 |
한계도 적어 둔다. 여기 적은 프로필 구성과 translate_allowlists 같은 키 이름은 우리 환경에서 확인한 것이고, 크롬 버전이나 운영체제가 다르면 Preferences의 키 구조가 달라질 수 있다. 번역이 텍스트 노드를 교체한다는 메커니즘은 리치 에디터 전반에 해당하지만 어떤 조작에서 크래시가 나는지는 구현마다 다르며, 우리가 확인한 것은 선택 확장과 타이핑 두 지점이다. 증상만 보고 원인을 짐작해 엉뚱한 해법에 시간을 쓴 구조는 connect_over_cdp가 탭 하나 때문에 멎었던 사례와 같고, 두 사례 모두 자체 콘텐츠 감사에서 확정한 원칙에 따라 틀린 진단을 먼저 적었다.
자주 묻는 질문
Q. 자동화 중 "서버 장애" 화면이 뜨면 서버 문제로 봐야 하는가?
A. 그렇게 단정할 수 없다. 페이지 이동 없이 DOM만 바뀌었다면 클라이언트 쪽 크래시일 가능성이 높다. page.on('framenavigated') 이벤트 발생 여부로 확인한다.
Q. 크롬 자동번역이 왜 리치 에디터를 크래시시키는가?
A. 자동번역이 텍스트 노드를 번역된 텍스트로 교체하는데, 그 순간 에디터가 선택을 확장하거나 입력을 처리하려 하면 문서 모델과 DOM의 위치 매핑이 어긋나면서 예외가 난다.
Q. 자동번역이 켜져 있는지 어떻게 확인하는가?
A. documentElement.className에 translated-ltr가 있는지, querySelectorAll('font').length가 0보다 큰지, documentElement.lang이 원문 언어인지를 본다. 우리 환경에서 번역된 상태의 값은 <font> 78개에 lang=ko였다.
Q. notranslate 메타 태그를 넣으면 해결되는가?
A. 우리 실측에서는 해결되지 않았다. notranslate 메타와 --disable-features=Translate,TranslateUI 기동 플래그 둘 다 무효였고, 크롬 종료 상태에서 프로필 Preferences를 직접 고쳐야 통했다.
Q. 이 문제를 겪으면 대기하는 게 맞는가?
A. 아니다. 서버 장애가 아니므로 대기해도 화면은 바뀌지 않는다. framenavigated로 원인을 확인한 뒤 자동번역을 끄는 것이 해법이고, 계측 한 번이 대기 몇 시간보다 싸다.
Comments
Comments (0)
Leave a Comment