Claude Code의 화면 확인을 스크린샷에서 문자로 바꾼 방법
요약
AI에게 웹 화면 확인을 요청할 때 스크린샷 방식은 토큰 소모와 여러 번의 도구 호출이 필요해 비효율적입니다. 이를 해결하기 위해, 개발자가 직접 만든 `page-digest.mjs` 스크립트는 격리된 브라우저에서 실행되어 오류, 가로 넘침 여부, 이름 없는 조작 부품 등 구조적인 정보를 문자로 요약하여 반환합니다.
핵심 포인트
- 스크린샷 대신 문자 기반 보고서가 효율적입니다.
- 오류(console.error), 가로 넘침, 이름 없는 요소 등을 판별 가능하게 추출합니다.
- Playwright와 Node.js를 사용하여 격리된 환경에서 작동합니다.
- AI는 종료 코드만으로도 문제 유무를 즉시 판단할 수 있습니다.
AI에게 '개발 중인 화면을 봐달라'고 요청하면, 스크린샷을 찍어 보게 하는 방식이 되기 쉽습니다. 하지만 제 환경에서 측정해 보니, 한 번 확인할 때마다 '열기・누르기・읽기・콘솔 보기・통신 보기' 등 약 5번 정도 도구를 호출해야 했으며, 스크린샷은 1장당 약 400~1,600 토큰(AI가 처리하는 정보량의 단위. 둘 다 추정치)이 소요되었습니다.
그래서 화면 상태를 수십 줄의 문자로 요약하여 반환하는 작은 스크립트 page-digest.mjs를 만들었습니다. 이 글에서는 무엇을 보고하게 하는지, 외부 통신을 어떻게 중단시키는지, 그리고 리뷰 과정에서 '중단했다고 생각했지만 실제로 중단되지 않았던' 경위를 소개합니다. AI에게 스크린샷이 아닌 문자로 화면 확인을 시키고 싶은 분들이 바로 사용해 볼 수 있도록 만들었습니다. 대상은 AI에게 화면 확인을 맡기는 웹 개발자입니다.
전제: 어떤 환경에 대한 이야기인가요
저는 Claude Code와 Codex(각각 Anthropic과 OpenAI의 개발 지원 AI)를 번갈아 가며 개인 개발을 하고 있으며, 두 가지 공통 규칙과 보조 스크립트를 agent-toolkit이라는 리포지토리에 모아두었습니다. page-digest.mjs도 그중 하나로, Node.js와 Playwright(브라우저를 조작하는 라이브러리)를 사용하여 격리된 Chromium에서 작동합니다.
참고로, 이 글에 적힌 수치는 제 환경에서 2026-09-27에 측정한 것입니다. 조건이 동일한 비교는 아니므로 '제 환경에서도 똑같을 것'이라고 말씀드릴 수는 없습니다.
어려웠던 점: 기존 방식은 놓치거나, 너무 많았습니다
같은 화면(직접 구동하던 웹 앱)을 Chrome을 통해 읽어보니 다음과 같은 상태였습니다.
| 방법 | 실측으로 알게 된 것 |
|---|---|
| 본문 텍스트 가져오기 | 본문의 대부분을 놓침 |
| ... | |
| 본문 텍스트는 빠지고, 구조 전체를 읽으면 불필요한 내용까지 포함되며, 스크린샷은 토큰을 소모합니다. 게다가 한 번 확인할 때 약 5번 정도 호출했기 때문에 왕복 시간도 무거웠습니다. |
보고 싶은 것은 시각적인 인상이 아니라, '에러가 없는지', '로딩 실패는 없는지', '스마트폰 너비에서 가로로 넘치지는 않는지', '이름 없는 조작 부품은 없는지'와 같이 명확하게 판정할 수 있는 항목입니다. 판별 가능한 항목이라면, 이미지를 보는 것보다 처음부터 문자로 답변하는 구조가 더 적합하다고 생각했습니다.
만든 것: 한 번 호출하면, 판정이 포함된 보고서가 돌아옵니다
page-digest.mjs는 URL 또는 정적 파일 폴더(--serve)를 전달하면, 격리된 브라우저에서 열고 다음 항목들을 보고합니다.
- 예외 및
console.error(오류) - 로딩 실패와 상태 400 이상의 통신(통신 실패) - 가로 넘침 (기본 너비 390px 기준, 페이지의 가로 폭이 화면 너비를 초과하는지. 원인 후보 요소명 포함)
- 이름 없는 조작 부품 (버튼・링크・입력란 등에서 읽어낼 수 있는 이름이 없는 것)
- 화면의 골격 (보이는 요소만, 읽어내기와 같은 이름으로. 기본 최대 80줄)
마지막에는 '판정: 문제 없음' 또는 '판정: 문제 있음'을 내리고, 종료 코드도 0(문제 없음)・1(문제 있음)・2(사용법 오류)로 구분합니다. AI는 문장을 읽지 않아도, 종료 코드로 합불을 알 수 있습니다.
# 정적 파일의 화면을 열고 요약하기
node bin/page-digest.mjs --serve <폴더>
# 연 후에 버튼을 누르고, 입력한 후 요약하기
...
조작은 --do로 여러 번 추가할 수 있습니다. 대상은 '역할:이름'으로 지정하며, 화면에 보이는 이름과 완전히 일치하는 것만 누릅니다. 이는 스크린샷상의 좌표가 아니라, 읽어내기와 같은 이름으로 부품을 지칭한다는 설계입니다. 이미지가 필요할 때만 --shot로 저장하고, 저장 위치만 반환합니다(볼 필요가 있을 때 AI가 열게 됩니다).
누가(무엇)가 무엇을 하는지
이 글의 메커니즘은 담당자가 다음과 같이 나뉘어 있습니다.
담당자 | 할 일
|---|---|
| 확정된 프로그램 (AI 미사용) | page-digest.mjs가 화면을 열고, 예외/통신/넘침/이름 유무를 기계적으로 계산하여 판정과 종료 코드를 출력함 |
| 나 | '효율화와 토큰 절약을 위해, 화면 확인을 개선해 달라'고 방향을 지시함 |
| AI (Claude Code) | 보고서를 읽고, 코드를 고치고, 다시 실행함. 끝내기 전에 합격/불합격을 알 수 있는 확인 과정을 스스로 돌림 (전체 지시에 작성되어 있음). |
| 다른 AI (Codex) | page-digest.mjs 자체를 리뷰함 (리뷰는 읽기 전용으로 부탁하는 운영 방식). |
판정 그 자체에는 AI의 추측이 들어가 있지 않습니다. '가로 넘침 여부'는 요소의 오른쪽 끝 좌표와 화면 너비 비교입니다. '이름 유무'는 속성과 텍스트의 유무에서 보는 간이 판정으로, 읽어주는 소프트웨어가 실제로 읽는 이름을 엄밀하게 확인하는 것은 아닙니다 (참조처 ID가 잘못된 경우 등은 놓칠 수 있습니다). AI가 담당하는 것은 그 보고서를 읽고 다음에 무엇을 고칠지 판단하는 것입니다.
외부 통신을 멈췄다고 생각했지만, 멈추지 않았다
화면 확인 스크립트가 개발 중인 앱을 건드릴 때 무서운 것은 실제 프로덕션 데이터베이스나 분석 서비스로 통신이 나가는 것입니다. 그래서 기본적으로 로컬( localhost
・127.0.0.1
・*.localhost
) 이외의 통신을 멈추도록 했습니다. 외부 사이트를 보고 싶을 때만 --net를 붙입니다.
처음 구현은 브라우저의 route라는 메커니즘으로, 로컬 외 요청을 중단하는 것이었습니다. 그런데 Codex에게 리뷰를 받자 다음 문제가 발견되었습니다.
- 로컬 서버가 외부 주소로 포워딩(302 등)할 경우, 포워딩 전송지는
route를 우회하여 실제로 외부로 나감 - 2026-09-27 실측으로 재현 가능
조사해 보니 원인은 처음 요청만 보고, 전송지는 보지 않았기 때문이었습니다. 그래서 브라우저 자체의 중계처(프록시) 설정을 존재하지 않는 주소로 가게 했습니다. 로컬 외는 모두 일률적으로 실패하므로, 전송지가 외부여도 나갈 수 없습니다. 이렇게 하면, 전송지를 포함하여 로컬 외로는 나갈 수 없게 됩니다. 코드에서는 다음 형태입니다.
const browser = await chromium.launch(
opt.net ? {} : { proxy: { server: 'http://127.0.0.1:9',
bypass: 'localhost,127.0.0.1,[::1],*.localhost' } });
route는 '막은 호스트를 보고서에 적기 위해' 남겨두었습니다. 막는 본체는 중계처 설정이고, 기록은 route라는 역할 분담입니다.
같은 생각으로, 밤에 돌리는 품질 체크 화면 확인도 고치고 있습니다.
그 이후의 지적과 채택하지 않은 것
리뷰를 총 3번 진행했습니다.
| 회차 | 지적 사항 | 결과 |
|---|---|
| 1회차 | 고2・중3 | 4건을 채택. 전송 구멍 외에, 폴더 외부를 가리키는 링크와, 부모가 투명한 컴포넌트 처리도 수정함 |
| 2회차 | 고2・중1 | *.test를 로컬로 간주하지 않음 (hosts 설정에 따라 외부를 가리킬 수 있으므로), Chrome을 열지 못하면 알림, '야간 체크에서 다른 로컬 서비스로 전송'은 해당 서버가 정적 배포만으로는 전송을 반환하지 않아 미채택 |
| 3회차 | 지적 사항 없음 | 여기서 중단함 |
기록에는 '지적이 끝으로 옮겨갔으므로 여기서 중단'이라고 되어 있습니다. 모든 지적을 채택하는 것이 아니라, '이 구성에서는 일어나지 않는다'라는 지적은 이유와 함께 보류했습니다.
테스트가 공회전하고 있지 않은지 확인하기
테스트는 Test-PageDigest.ps1에 12건 있으며, 툴 그룹의 자체 점검에 포함되어 있습니다. 여기서 포인트는, 일부러 고장 난 버전으로 제대로 떨어지는 것을 보고 있다는 것입니다. 중계처 설정을 제외한 버전과, 넘침 감지를 제외한 버전에서 각각 테스트가 실패했습니다. 통과만 하는 테스트는 검사가 공회전하고 있어도 통과해 버리기 때문입니다.
실제 성과와 아직 말할 수 없는 것
- 로컬에서 구동하던 Web 앱의 화면이 404를 반환하는 원인 (
/api/meta, 즉 백그라운드 서버가 필요함)까지, 한 번의 호출로 보고서에 나왔습니다 - 같은 화면에서 Chrome 경유보다 출력 글자 수는 약 절반, 호출은 5회에서 1회로 줄었습니다 (둘 다 추정).
한편으로는, 다음 것은 아직 말할 수 없습니다.
외관의 좋고 나쁨(여백이나 색상 배합)은 판별할 수 없습니다. 필요하다면 --shot으로 저장한 스크린샷을 별도로 봅니다 - 로그인해야 하는 화면은 처리할 수 없습니다. 격리된 브라우저에서 열기 때문에 평소 Chrome의 로그인 상태를 사용하지 않기 때문입니다(그 경우에는 Chrome 확장 프로그램으로 보는 방식으로 운영합니다). - 일본어 음성 읽기에서의 자연스러움 등, 이름의 '내용의 좋고 나쁨'까지는 보고 있지 않습니다. 이름이 비어있지한지만 보고 있습니다
가져가기 (Takeaway)
자신의 환경에 응용하려면 다음 3가지가 핵심입니다.
- 화면 확인 출력은 스크린샷이 아니라 '판단이 포함된 짧은 문자'로 하고, 종료 코드로 합불을 반환한다.
- 조작하는 부품은 좌표가 아니라, 음성 읽기와 같은 '역할: 이름'으로 지정한다. 이름이 없는 부품은 그 자체가 보고 대상이 된다.
- 통신 차단은 리퀘스트 단위의 중지에만 의존하지 않고, 브라우저의 중계지 설정에서 일괄적으로 멈춘다. 전송지 또한 멈춘다.
막았다고 생각하는 대책은 다른 AI에게 '정말로 멈춰 있는지'를 확인해 달라고 부탁하면, 비슷한 빠진 부분이 발견하기 쉬워집니다. 화면 확인을 문자로 하니, 제 환경에서는 출력도 호출도 줄어들고(추정), 합불을 종료 코드로 받을 수 있었습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기