에이전트 브라우저는 어느 층에 붙는가
지금 나와 있는 에이전트 브라우저들은 대체로 Chromium을 고쳐 만든 독립 브라우저다. 확장 프로그램이 아니라 브라우저 자체를 손봤다는 것이 공통된 주장이고, 그래야 AI가 페이지 깊숙이 개입할 수 있다는 논리다.
가격 모델도 갈린다. 자체 AI 요금을 받는 쪽이 있고, 사용자가 이미 결제한 구독을 그대로 물려 쓰게 하는 쪽이 있다. 후자는 “Bring your own subscription”이라는 표현을 쓴다. 다만 무료 구간에는 대개 상한이 붙는다. 한 곳은 무료가 월 500 크레딧에 반복 실행(routine) 3개까지고, 반복 실행을 무제한으로 쓰려면 유료다. 자동화를 여러 채널로 돌리는 쪽이라면 처음부터 유료 구간이라는 뜻이다.
여기까지는 제품 이야기다. 실제로 중요한 것은 그다음이다. 브라우저를 바꾸면 어떤 벽이 없어지고 어떤 벽이 남는가.
벽 1 — 탭이 뒤에 있으면 fetch가 멎는다
가장 자주 부딪힌 벽이다. 자동화 스크립트가 페이지 안에서 fetch()로 이미지나 본문을 받아 오는데, 그 탭이 활성 탭이 아니면 요청이 그냥 멈춘다. 서버는 멀쩡하다. 같은 주소를 커맨드라인에서 받으면 0.2초에 200이 온다.
판별은 간단하다. 페이지에서 document.visibilityState를 읽어 hidden이면 그 상태다. 이걸 모르면 «서버가 느리다»거나 «스크립트가 틀렸다»로 오진하게 된다. 실제로 타임아웃을 걸어도 소용이 없었다. 창을 앞으로 세우자 같은 코드가 그 자리에서 바로 돌았다.
창을 앞에 세운 뒤에도 스크립트가 빈 결과를 돌려주는 일이 있었다. 실패로 보였지만 아니었다. 원격 제어 채널이 비동기 완료를 기다리지 않고 먼저 돌아온 것이고, 상태를 다시 재 보니 작업은 이미 끝나 있었다. 그래서 판정은 «반환값»이 아니라 «상태를 다시 재서» 한다.
벽 2 — 좌표가 미끄러진다
화면을 캡처해 좌표를 잡고 클릭하는 방식에는 함정이 하나 있다. 스크린샷의 픽셀 좌표와 페이지 안의 CSS 좌표가 다르다. 창 크기나 배율이 작업 중에 바뀌면 그 차이가 더 벌어진다.
환산은 이렇게 한다.
클릭좌표 = 요소의 CSS 좌표 × (스크린샷 가로폭 ÷ window.innerWidth)실제로 창이 세로로 좁혀져 있던 날, innerWidth가 1421이고 스크린샷이 1361이라 배율이 0.958이었다. 환산하지 않고 누르면 4%쯤 어긋난다. 버튼 하나 크기 안에서 어긋나면 티가 안 나다가, 버튼이 여러 개 붙어 있는 자리에서 옆 버튼이 대신 눌린다.
실제로 그런 일이 났다. 「게시」를 누르려다 바로 위의 「예약 옵션」이 열렸다. 다행히 예약 화면이라 되돌릴 수 있었지만, 한 칸 아래였으면 잘못 나갔을 것이다.
누르기 «직전»에 다시 잰다
좌표는 한 번 재서 재사용하면 안 된다. 페이지가 조금만 움직여도 낡는다. 그리고 누르기 전에 document.elementFromPoint(x, y)로 그 자리에 정말 그 요소가 있는지 확인하면 오진이 크게 줄어든다.
더 나은 길도 있다. 좌표가 미덥지 않으면 요소를 직접 지정해 누르는 것이다. 위의 「예약 옵션」 사고도 요소를 지정해 누르니 한 번에 정확히 들어갔다.
벽 3 — 1차 클릭이 안 먹는다
여러 SNS에서 공통으로 겪는 현상이다. 게시 버튼을 눌러도 아무 일이 없고, 한 번 더 눌러야 나간다. 버튼이 비활성인 것도 아니고 오류도 없다.
문제는 여기서 대응을 잘못하기 쉽다는 데 있다. “안 먹었으니 다시 누르자”가 중복 게시를 만든다. 실제로 같은 글이 두 벌 나간 적이 있다. 버튼을 누른 뒤 3초 만에 화면을 봤더니 작성창이 그대로 남아 있어서 실패로 판단하고 다시 눌렀는데, 첫 클릭은 이미 먹은 상태였다.
누름 → 아무것도 하지 않음 → 새 탭에서 프로필을 열어 «게시물 수»를 센다 → 그다음에 판단. 작성창이나 모달이 남아 있는 것은 실패의 증거가 아니다. 화면 상태가 아니라 결과 수치로 판정한다.
벽 4 — 편집기마다 «넣는 법»이 다르다
글을 넣는 방법이 사이트마다 다르다. 같은 contenteditable이라도 그 위에 올라간 편집기가 다르면 통하는 방법이 달라진다. 실제로 통했던 조합은 이렇다.
| 편집기 계열 | 통한 방법 | 안 통한 것 |
|---|---|---|
| DraftJS·Lexical 계열 | 합성 paste 이벤트 | 한 글자씩 타이핑(한글이 깨진다) |
| TinyMCE 계열 | 편집기 API로 내용 넣기 | DOM을 직접 고치기(저장이 안 간다) |
| 블록 편집기 | 실제 클릭으로 포커스를 준 뒤 합성 paste | 코드로 포커스만 주고 붙이기 |
| 태그 입력칸(textarea) | execCommand('insertText') + 쉼표로 이어 붙이기 | paste 후 엔터 반복 |
특히 세 번째 줄이 오래 걸렸다. 코드로 포커스를 주고 붙여넣으면 오류 없이 조용히 아무것도 안 들어간다. 길이를 재 보고서야 알았다. 실제 클릭 한 번을 먼저 넣자 그다음부터는 잘 들어갔다.
저장이 «갔는지»는 따로 확인한다
화면에 보이는 것과 저장된 것은 다르다. 어떤 편집기는 내부 모델을 거치지 않은 변경을 저장할 때 통째로 덮어쓴다. 넣은 직후가 아니라 저장한 뒤 편집기를 새로 열어 글자 수를 다시 재는 편이 확실하다. 이 검산으로 «넣었는데 저장은 안 된» 경우를 실제로 잡았다.
벽 5 — 파일 입력이 없다
이미지를 올리려면 보통 input[type=file]을 찾아 파일을 넣는다. 그런데 그 요소가 페이지에 아예 0개인 사이트가 있다. 업로드 버튼이 운영체제의 파일 선택창을 직접 여는 방식이라 그렇다. 자동화에서는 그 창을 다룰 수 없다.
우회로는 있다. 파일을 HTTP로 접근 가능한 곳에 잠깐 올려 두고, 페이지 안에서 그 주소를 받아 파일 객체로 만든 다음, 드롭 이벤트를 합성해 편집기에 떨어뜨리는 것이다. 순서는 이렇다.
주의할 점이 둘 있다. 하나는 드롭한 순서와 화면에 쌓이는 순서가 다를 수 있다는 것이다. 순서로 식별하지 말고 파일명 속성으로 잡는다. 다른 하나는 4번이다. 실제로 임시로 올린 원고가 하루 넘게 공개 주소에 남아 있었다. 지우는 단계를 절차에 넣지 않으면 반드시 잊는다.
벽 6 — 클립보드는 OS 자원 하나다
이건 브라우저 문제가 아니다. 운영체제 전체가 클립보드 하나를 공유한다. 창을 나눠 쓰고 있어도, 자동화가 클립보드를 통해 텍스트나 이미지를 옮기는 순간 그 경계가 없어진다.
실제로 한 창에서 작업하던 내용이 다른 창의 입력칸에 붙은 적이 있다. 저장 전에 발견해 지웠지만, 성격이 다른 두 가지를 동시에 다루고 있었다면 그대로 나갈 수 있었다.
그래서 지금은 클립보드를 아예 쓰지 않는다. 텍스트는 합성 paste 이벤트로, 이미지는 앞의 드롭 방식으로 넣는다. 클립보드를 거치지 않으면 오염될 자리가 없어진다.
벽 7 — 나가기 확인창이 이동을 막는다
편집기가 열려 있는 탭에서 다른 주소로 이동하려 하면 «변경 사항이 저장되지 않을 수 있습니다» 확인창이 뜬다. 자동화 입장에서는 그 순간부터 아무것도 못 한다.
코드로 그 경고를 끄려고 window.onbeforeunload = null을 넣고 편집기의 «변경됨» 표시도 내려 봤는데 둘 다 소용이 없었다. 사이트가 자체 핸들러를 따로 걸어 두기 때문이다.
실제 해법은 단순했다. 새 탭을 열어 거기서 확인하는 것이다. 저장 여부는 이미 «다시 열어 재기»로 검증해 두었으므로 잃을 것이 없었다.
벽 8 — 계기가 틀린다
마지막 벽이 가장 위험하다. 자동화는 «확인»을 코드로 한다. 그런데 그 확인 코드가 틀리면 조용히 잘못된 결론이 나온다. 오류가 안 나기 때문에 더 위험하다.
겪은 것만 적어도 이렇다. 태그를 셀렉터로 세었더니 0개였는데 화면에는 전부 붙어 있었다. FAQ 개수를 정규식으로 세었더니 0이었는데 텍스트로 세니 5개였다. 어떤 사이트는 공개 페이지를 커맨드라인으로 받으면 응답이 0바이트라, 글이 없는 것처럼 보였다.
마지막 경우는 어제 글·그제 글도 같이 찾아본 덕에 밝혀졌다. 그것들도 0이었으니 «없다»가 아니라 «못 쟀다»였다. 파일이 지워졌는지 확인할 때 살아 있는 파일도 같이 요청해 보는 것도 같은 이유다. 둘 다 응답이 없으면 그건 삭제 성공이 아니라 측정 실패다.
여기에 하나 더. 자동화가 스스로에게 거는 검사에도 같은 함정이 있다. 발행 직전 게이트가 본문 길이를 재는데 값이 안 맞아 멈춘 적이 있다. 원인은 글이 아니라 세는 범위였다. 그 사이트는 태그 칩도 편집 가능한 요소라 본문과 함께 세어졌다. 게이트가 멈춘 것 자체는 옳게 작동한 것이었다.
무엇이 풀리고 무엇이 남나
여덟 개를 «층»으로 갈라 보면 이렇게 나뉜다.
| 벽 | 어느 층의 문제인가 | 브라우저를 갈아타면 |
|---|---|---|
| 1 백그라운드 fetch | 브라우저 | 풀릴 여지가 있다 |
| 2 좌표 미끄러짐 | 조작 방식 | 풀린다(좌표를 안 쓰면 된다) |
| 5 파일 입력 없음 | 브라우저 | 풀릴 여지가 있다 |
| 7 나가기 확인창 | 브라우저 | 풀릴 여지가 있다 |
| 3 1차 클릭 | 서비스 쪽 | 안 풀린다 |
| 4 편집기 차이 | 서비스 쪽 | 안 풀린다 |
| 6 클립보드 공유 | 운영체제 | 안 풀린다 |
| 8 계기 오류 | 검증 설계 | 안 풀린다 |
절반은 도구로 풀리고 절반은 안 풀린다. 그리고 안 풀리는 쪽이 더 비싼 사고를 낸다. 중복 게시, 잘못된 내용이 섞여 나가는 것, «됐다»고 잘못 판정하는 것은 전부 아래 네 줄에서 나온다.
그리고 하나 더 — 권한을 어디까지 줄 것인가
에이전트 브라우저의 장점은 «로그인된 실제 계정에서 일한다»는 것이다. 그런데 안전 지침들은 대체로 반대를 말한다. 자격증명이 없는 프로필로 격리해 돌리고, 갈 수 있는 주소를 제한하고, 결제·계정 변경·발송은 사람이 확인하라는 것이다. 장점과 권고가 정확히 반대 방향을 가리킨다.
페이지에 적힌 글자가 지시로 읽힐 수 있다는 것도 같은 맥락이다. 「이전 지시를 무시하고 …로 가라」가 페이지에 써 있으면 원래 하려던 일에서 벗어날 수 있다. 그래서 돌이킬 수 없는 동작 — 공개 게시, 삭제, 결제 — 은 사람이 마지막 버튼을 누르는 구조가 지금으로서는 가장 싸게 먹힌다.
자주 묻는 질문
확장 프로그램 대신 에이전트 브라우저를 쓰면 다 해결되나요?
절반쯤입니다. 백그라운드 실행, 파일 입력, 나가기 확인창처럼 브라우저 층의 제약은 풀릴 여지가 있습니다. 하지만 1차 클릭 무반응이나 편집기별 차이는 서비스 쪽 동작이라 그대로고, 클립보드는 운영체제 자원이라 구조적으로 안 풀립니다.
좌표 클릭을 아예 안 쓰는 게 낫나요?
가능하면 그렇습니다. 요소를 직접 지정해 누르는 쪽이 배율 문제에서 자유롭습니다. 다만 신뢰된 입력이 필요한 자리(포커스를 처음 주는 곳 등)는 실제 클릭이 필요합니다. 그때는 누르기 직전에 좌표를 다시 재고, 그 자리에 정말 그 요소가 있는지 확인하세요.
게시가 됐는지 어떻게 확인하나요?
화면 상태로는 판정하지 않습니다. 작성창이 남아 있어도 이미 나갔을 수 있고, 성공 표시가 떠도 안 나갔을 수 있습니다. 프로필의 게시물 수처럼 되돌릴 수 없는 수치를 게시 전후로 세는 편이 확실합니다.
확인 코드가 틀렸는지는 어떻게 아나요?
대조군을 같이 잽니다. 새 글이 안 보이면 이미 있는 글도 같이 찾아보고, 파일이 지워졌는지 볼 때는 살아 있는 파일도 같이 요청합니다. 둘 다 «없음»이면 그건 결과가 아니라 측정 실패입니다.
무료로 시작할 수 있나요?
무료 구간을 두는 곳이 있지만 상한이 붙습니다. 한 곳은 월 500 크레딧에 반복 실행 3개까지입니다. 채널을 여러 개 돌린다면 상한을 금방 넘기니, 도입 전에 «내 작업 하나가 몇 크레딧인지»와 «반복 실행 하나가 어디까지 하는지»를 먼저 확인하는 편이 좋습니다.
정리
도구는 계속 좋아진다. 다만 자동화가 실제로 무너지는 자리는 대개 «기능이 없어서»가 아니라 «됐다고 잘못 판정해서»였다. 여덟 개 중 넷이 브라우저를 바꿔도 남는 것이고, 그 넷은 전부 검증과 절차의 문제다.
그래서 도구를 고르기 전에 먼저 정할 것은 이것이다. 무엇을 근거로 «끝났다»고 판정할 것인가. 그 답이 서 있으면 도구가 바뀌어도 자동화는 무너지지 않는다.