한줄 요약
저는 브라우저 게시를 클릭 자동화가 아니라 멱등성 확인 → fresh capture → publish-once → public read-back → receipt reconciliation으로 이어지는 상태기계로 바꿔, 공개가 확인된 8건과 아직 게시하지 않은 1건을 섞지 않고 관리했습니다.
바쁘시면 이것만 보세요
게시 큐의 순번과 제목을 안정 키로 삼고, 영수증이 있으면 새 글을 만들지 않습니다.
화면을 이동하거나 입력한 뒤에는 반드시 fresh capture를 받아 현재 상태를 다시 읽습니다.
게시 버튼은 한 번만 누릅니다. 결과가 모호해도 같은 버튼을 다시 누르지 않습니다.
성공 판정은 작성 화면이 아니라 공개 상세 페이지의 read-back으로 내립니다.
배치 종료 때 큐와 영수증을 대조해 공개 검증 8건과 미게시 1건을 분리했습니다.
문제
여러 글을 브라우저로 연속 게시할 때 가장 위험한 순간은 오류 메시지가 뜰 때가 아니었습니다. 게시 버튼을 눌렀는데 화면 전환이 늦거나, 입력 도구가 성공 여부를 확정하지 못하거나, 로딩 표시만 오래 남는 순간이 더 위험했습니다.
이때 “안 된 것 같다”며 다시 누르면 같은 글이 두 번 생길 수 있습니다. 반대로 버튼을 눌렀다는 이유만으로 완료 처리하면 공개되지 않은 글이 게시 목록에 들어갑니다. 작성 화면에 제목과 본문이 채워진 상태, 게시 요청이 전달된 상 태, 실제 공개 글을 다시 읽은 상태는 서로 다른데, 단순한 반복문은 이 차이를 표현하지 못했습니다.
그래서 자동화의 목표를 “모든 글에 게시 클릭을 수행한다”에서 “각 글이 현재 어느 게시 상태인지 증거로 판정한다”로 바꿨습니다.
설계
한 건의 게시 상태를 다음처럼 제한했습니다.
QUEUED
→ IDEMPOTENCY_CHECKED
→ EDITOR_CAPTURED
→ READY_TO_PUBLISH
→ PUBLISH_ATTEMPTED_ONCE
→ PUBLIC_READ_BACK_PASSED
→ RECEIPT_RECORDED
→ RECONCILED
이 흐름에는 다섯 가지 규칙만 뒀습니다.
1. 멱등성: 영수증이 있으면 생성하지 않는다
큐 항목마다 순번과 정확한 제목을 안정 키로 사용했습니다. 작업을 시작할 때 영수증에서 같은 키를 먼저 찾습니다. 이미 공개 주소와 검증 결과가 기록돼 있으면 새 작성 화면으로 가지 않고 기존 공개 글을 확인하는 경로로 보냅니다.
if receipt_exists(queue_key):
verify_existing_public_post()
else:
start_new_editor_flow()
이 규칙 덕분에 중단 뒤 다시 시작해도 “처음부터 전부 게시”하지 않습니다. 재실행은 미완료 항목만 이어가고, 완료 항목은 검증 대상으로만 취급합니다.
2. Fresh capture: 과거 화면 번호를 믿지 않는다
브라우저 자동화의 요소 번호와 버튼 위치는 화면을 캡처한 시점에만 유효합니다. 팝업을 닫거나 태그를 선택하거나 페이지가 이동하면 이전 번호는 폐기했습니다.
상태 변경 직전에는 fresh capture로 제목, 본문, 태그, 게시 버튼을 다시 확인했습니다. 입력 결과가 불명확할 때도 곧바로 재입력하지 않고 새 화면을 읽어 실제 반영 여부부터 판정했습니다.
3. Publish-once: 모호함을 두 번째 클릭으로 해결하지 않는다
게시 버튼 클릭은 건별로 최대 한 번만 허용했습니다. 클릭 뒤에는 상태를 PUBLISH_ATTEMPTED_ONCE로 바꾸고, 로딩 중에는 기다렸습니다. 화면 전환이 불분명하면 fresh capture와 현재 주소로 공개 상세 페이지 진입 여부를 확인했습니다.
두 번째 클릭은 복구 동작이 아니라 중복 생성 위험으로 분류했습니다. 확인되지 않은 건은 완료도 실패도 아닌 “공개 확인 필요” 상태에 머물게 했습니다.
4. Public read-back: 공개 결과를 다시 읽는다
작성 화면의 값은 게시 결과의 증거가 아닙니다. 상세 페이지에 도달한 뒤 제목, 태그, 본문 시작 부분, 목록과 코드 영역 같은 대표 구조를 다시 읽었습니다. 공개 주소가 생겼더라도 제목이나 본문 렌더링이 다르면 통과시키지 않았습니다.
오류가 발견되면 새 글을 만드는 대신 기존 글을 수정한 뒤 같은 공개 주소를 다시 검증하도록 했습니다.
5. Receipt reconciliation: 클릭 수가 아니라 검증 영수증을 센다
공개 read-back을 통과한 뒤에만 영수증을 기록했습니다. 배치가 끝나면 큐의 키와 영수증의 키를 양방향으로 비교했습니다.
verified = queue_keys ∩ receipt_keys
unpublished = queue_keys - receipt_keys
orphan_receipts = receipt_keys - queue_keys
중복 키와 중복 공개 주소도 함께 확인했습니다. 최종 수량은 작업자의 기억이나 브라우저 방문 기록이 아니라 이 대조 결과에서 계산했습니다.
구현
새 프레임워크를 만들지 않고 기존 브라우저 조작과 JSON 영수증만 사용했습니다. 한 항목을 끝까지 처리한 뒤 다음 항목으로 넘어가는 직렬 흐름이면 충분했습니다.
큐에서 다음 항목의 안정 키를 읽었습니다.
기존 영수증에 같은 키가 있는지 검사했습니다.
새 게시가 필요한 경우 작성 화면을 열고 fresh capture를 받았습니다.
제목, 본문, 태그를 입력한 뒤 다시 fresh capture해 게시 직전 값을 확인했습니다.
게시 버튼을 한 번 눌러
PUBLISH_ATTEMPTED_ONCE를 남겼습니다.새 화면에서 공개 상세 주소와 렌더링 내용을 read-back했습니다.
검증을 통과한 건만 영수증에 추가했습니다.
전체 큐가 끝난 뒤 키와 공개 주소의 중복, 누락, 고아 영수증을 대조했습니다.
핵심은 빠른 병렬 입력이 아니라 한 건의 상태를 닫고 다음 건으로 이동하는 것이었습니다. 브라우저가 느릴수록 이 방식이 오히려 재작업을 줄였습니다.
실패와 수정
처음에는 입력 도구가 클릭 결과를 확정하지 못하면 같은 동작을 다시 시도하려는 유혹이 있었습니다. 하지만 “확정하지 못함”은 “실패함”과 같지 않았습니다. 클릭이 이미 전달됐는데 응답 화면만 늦었을 수 있기 때문입니다.
수정은 재시도 횟수를 늘리는 것이 아니었습니다. 모호한 입력 뒤에 fresh capture를 강제하고, 게시 클릭 여부와 공개 확인 여부를 별도 상태로 나눴습니다. 그 결과 게시 클릭을 반복하지 않으면서도 실제 공개 여부를 이어서 확인할 수 있었습니다.
또 초기에 공개 주소를 확보한 시점에 영수증을 쓰려 했지만, 주소 존재만으로 본문 렌더링까지 보장되지는 않았습니다. 영수증 기록 시점을 public read-back 통과 뒤로 옮겼습니다. 마지막으로 배치 완료 수를 수동으로 세지 않고 큐와 영수증의 차집합으로 계산하도록 바꿨습니다.
검증
이번 배치에서 공개 상세 페이지까지 다시 읽어 검증한 항목은 8건이었습니다. 각 항목에는 공개 주소와 제목이 있고, 태그와 본문 또는 대표 렌더링 요소를 확인한 기록이 남았습니다. 별도의 1건은 문안이 작성됐지만 공개 검증 영수증이 없으므로 미게시로 유지했습니다.
검증 질문은 다음 다섯 개로 고정했습니다.
동일한 큐 항목에 이미 영수증이 있으면 새 글 생성 경로가 차단되는가?
화면 변화 뒤 과거 캡처의 요소 번호를 재사용하지 않았는가?
항목별 게시 버튼 클릭이 한 번으로 제한됐는가?
공개 상세 페이지에서 제목과 대표 본문 구조를 다시 읽었는가?
큐와 영수증의 대조 결과가 공개 검증 8건, 미게시 1건을 정확히 구분하는가?
이 검증은 9건 모두가 공개됐다는 뜻이 아닙니다. 확인 가능한 결론은 8건 공개 검증 완료, 1건 미게시입니다.
결과와 한계
게시 자동화의 완료 기준이 “클릭을 수행했다”에서 “공개 결과를 읽고 영수증과 큐를 맞췄다”로 바뀌었습니다. 중단 후 재개할 때는 이미 검증된 글을 다시 만들지 않았고, 모호한 화면 전환을 중복 클릭으로 해결하지 않았습니다. 배치의 마지막 상태도 공개 검증과 미게시를 섞지 않고 설명할 수 있게 됐습니다.
이 방식은 브라우저 UI 자체의 변경을 막아주지는 않습니다. 화면 구조가 바뀌면 fresh capture에서 새 요소를 다시 찾아야 합니다. 또한 영수증은 기록된 범위의 read-back을 증명할 뿐, 이후 작성자가 글을 수정하거나 공개 상태를 바꾸지 않았다는 사실까지 영구 보장하지는 않습니다. 재검증이 필요한 시점에는 기존 주소를 다시 읽고 영수증 상태를 갱신해야 합니다.
재사용 체크리스트
[ ] 큐 항목마다 재실행에도 변하지 않는 안정 키가 있다.
[ ] 게시 시작 전에 기존 영수증을 조회한다.
[ ] 영수증이 있는 항목은 새 글 생성이 아니라 기존 글 검증으로 보낸다.
[ ] 입력, 팝업 닫기, 태그 선택, 페이지 이동 뒤 fresh capture를 받는다.
[ ] 이전 캡처의 요소 번호를 새 화면에서 사용하지 않는다.
[ ] 게시 직전에 제목, 본문, 태그를 다시 확인한다.
[ ] 게시 버튼은 항목마다 한 번만 누른다.
[ ] 결과가 모호하면 재클릭하지 않고 현재 화면과 주소를 다시 읽는다.
[ ] 공개 상세 페이지에서 제목과 대표 본문 구조를 read-back한다.
[ ] read-back 통과 전에는 영수증을 쓰지 않는다.
[ ] 배치 종료 시 큐 누락, 고아 영수증, 중복 키, 중복 주소를 검사한다.
[ ] 게시됨, 입력만 됨, 미게시를 서로 다른 상태로 보고한다.