콘텐츠로 이동

브라우저 자동화가 어긋날 때

무엇이 보이는가. 브라우저 자동화 작업인데 세션 기반 도구와 Playwright 중 무엇을 써야 할지 판단이 안 섭니다.

왜 그런가. 두 도구는 성격이 다릅니다. 사용자의 로그인 세션·쿠키를 그대로 써야 하는 판독 작업은 브라우저 세션 도구(같은 프로필을 공유하는 브라우저 REPL) 몫입니다. 영상 녹화, trace, 디바이스 에뮬레이션, 격리된 프로필이 필요한 작업은 Playwright 몫입니다.

어떻게 확인하는가. “로그인된 상태가 필요한가”와 “녹화·trace·에뮬레이션·격리가 필요한가”를 각각 물어보십시오. 둘 다 아니면 어느 쪽을 써도 됩니다. 둘 다 해당하면 작업을 나눕니다.

어떻게 고치는가. 로그인 세션이 필요하면 세션 기반 도구를 쓰십시오. 녹화, trace, 디바이스 에뮬레이션, 격리 프로필이 필요하면 Playwright를 쓰십시오. 애매하면 임의로 고르지 말고 확인하십시오.

innerHTML 해시 검증이 이유 없이 실패합니다

섹션 제목: “innerHTML 해시 검증이 이유 없이 실패합니다”

무엇이 보이는가. DOM 무결성을 innerHTML의 해시로 비교하는 검증이, 아무것도 바꾸지 않았는데 실패합니다.

왜 그런가. 스크린샷을 찍는 동작 자체가 DOM을 변형시킬 수 있습니다. 예를 들어 캐럿을 숨기는 처리 과정에서 폼 요소에 빈 style 속성이 남는 식입니다. 이런 변경은 CDP(Chrome DevTools Protocol) 레벨에서 일어나 MutationObserver로는 잡히지 않습니다.

어떻게 확인하는가. 검증 대상을 제거한 대조군에서 스크린샷만 두 번 찍어 해시를 비교해 보십시오. 아무것도 바뀌지 않았어야 할 상황에서 해시가 달라진다면 스크린샷 자체가 원인입니다.

어떻게 고치는가. 해시 비교와 스크린샷 촬영을 같은 실행 안에서 순서대로 하지 말고, 서로 다른 실행으로 분리하십시오. 검증 도구가 검증 대상을 오염시킬 수 있다는 것을 전제하십시오.

rAF 루프를 외부에서 조작하면 시뮬레이션 시간이 느리게 흐릅니다

섹션 제목: “rAF 루프를 외부에서 조작하면 시뮬레이션 시간이 느리게 흐릅니다”

무엇이 보이는가. requestAnimationFrame 기반 루프(게임, 애니메이션)를 page.evaluate를 반복 호출해 밖에서 조작하면, 많은 횟수를 시도해도 시뮬레이션 안의 경과 시간이 거의 늘지 않습니다.

왜 그런가. 루프 내부의 델타 타임(dt)이 클램프돼 있으면, 호출 사이에 벌어진 벽시계 시간의 격차가 진행으로 환산되지 않고 버려집니다. 외부에서 상태를 강제로 밀어 넣어도 다음 프레임에서 그 파생 상태는 지워집니다.

어떻게 확인하는가. 같은 조작을 반복 횟수만 늘려 가며 실행해 보십시오. 횟수를 늘려도 시뮬레이션 경과 시간이 비례해서 늘지 않는다면 dt 클램프를 의심하십시오.

어떻게 고치는가. 자동주행 로직 전체를 하나의 page.evaluate 호출 안에 넣어 브라우저 안에서 루프를 완결시키십시오. 밖에서는 입력이나 누적 상태처럼 다음 프레임에서도 유지되는 값만 밀어 넣고, 파생 상태를 직접 밀어 넣지 마십시오.

page.evaluate 안에서 ReferenceError: __name is not defined가 납니다

섹션 제목: “page.evaluate 안에서 ReferenceError: __name is not defined가 납니다”

무엇이 보이는가. page.evaluate에 넘긴 함수 안에서 이름 있는 화살표 함수나 일반 함수 선언을 쓰면 ReferenceError: __name is not defined가 납니다.

왜 그런가. esbuild 같은 번들러가 함수 이름을 유지하기 위해 삽입하는 __name 헬퍼가 브라우저 실행 컨텍스트에는 존재하지 않기 때문입니다. 번들러를 거친 코드가 그 헬퍼 호출을 그대로 남긴 채 브라우저로 직렬화되면 이 오류가 납니다.

어떻게 확인하는가. page.evaluate에 넘기는 함수 본문에서 이름이 붙은 함수 선언이나 화살표 함수 대입을 찾아보십시오.

어떻게 고치는가. 인라인 익명 화살표 함수만 쓰거나, 소스 문자열을 new Function(...)으로 넘기십시오. 다만 문자열과 두 번째 인자를 조합하는 방식은 오류 없이 undefined만 반환할 수 있으니 결과를 직접 확인하십시오.

evaluate → click → evaluate로 잰 타이밍이 들쭉날쭉합니다

섹션 제목: “evaluate → click → evaluate로 잰 타이밍이 들쭉날쭉합니다”

무엇이 보이는가. 클릭 전후로 evaluate를 호출해 걸린 시간을 측정했는데 매번 오차가 크게 벌어집니다.

왜 그런가. evaluate와 click은 각각 별도의 IPC 왕복을 거칩니다. 그 왕복 시간이 측정값에 섞여 실제 동작 시간을 오염시킵니다.

어떻게 확인하는가. 같은 동작을 여러 번 반복해 측정값의 편차를 보십시오. 실제 동작보다 훨씬 큰 편차가 나온다면 IPC 왕복이 섞여 있는 것입니다.

어떻게 고치는가. 측정 로직 자체를 브라우저 안에 심으십시오. capture phase 리스너를 미리 주입해 이벤트를 감지하고, 시간 측정도 페이지 안에서 완결시켜 IPC 왕복을 측정 구간 밖으로 빼내십시오.

요소 참조가 리마운트 이후 stale해집니다

섹션 제목: “요소 참조가 리마운트 이후 stale해집니다”

무엇이 보이는가. 한 번 찾아 둔 요소 참조(예: querySelector로 얻은 canvas)를 계속 쓰다가, 어느 시점부터 조작이 반영되지 않습니다.

왜 그런가. React의 key 변경 등으로 컴포넌트가 리마운트되면 이전 DOM 요소는 폐기되고 새 요소로 교체됩니다. 이전에 얻어 둔 참조는 더 이상 화면에 있는 요소를 가리키지 않습니다.

어떻게 확인하는가. 리마운트를 유발하는 조건(키 변경, 조건부 렌더링 토글) 전후로 참조가 가리키는 요소가 같은지 확인하십시오.

어떻게 고치는가. 참조를 한 번만 얻어 재사용하지 말고, 매 프레임 다시 querySelector로 조회하십시오.

녹화한 영상 파일이 비어 있습니다

섹션 제목: “녹화한 영상 파일이 비어 있습니다”

무엇이 보이는가. 녹화를 설정하고 세션을 진행했는데, 생성된 영상 파일 크기가 0이거나 재생이 안 됩니다.

왜 그런가. 녹화 데이터는 브라우저 컨텍스트가 닫힐 때 디스크에 flush됩니다. 컨텍스트를 닫기 전에는 파일이 비어 있거나 존재하지 않는 것이 정상입니다.

어떻게 확인하는가. 컨텍스트를 닫기 전에 파일을 열어 보면 항상 비어 있습니다. 이것만으로 녹화 실패를 단정할 수 없습니다.

어떻게 고치는가. 브라우저 컨텍스트를 명시적으로 닫은 뒤에 파일을 확인하십시오. 닫기 전에는 확인 자체를 미루십시오.