반응형
"저장" 버튼을 못 찾는다
FAILED - playwright._impl._errors.TimeoutError:
Timeout 30000ms exceeded.
Locator: get_by_role("button", name="저장")
로케이터가 틀렸나? 텍스트가 바뀌었나? 아니면 버튼이 아예 안 나오나?
에러 로그만 보고는 모른다
에러 로그만 보고 추측하면 생기는 일
에러: "저장" 버튼 Timeout
↓
추측: "텍스트가 바뀌었겠지" → name="Save" 로 변경
↓
실행 → 또 실패
↓
추측: "버튼이 아니라 link 겠지" → get_by_role("link") 로 변경
↓
실행 → 또 실패
↓
추측: "로딩이 느린가봐" → timeout 60초로 증가
↓
실행 → 또 실패 (60초 기다린 후)
↓
30분 소모
결국 browser_snapshot 찍어보니 모달이 버튼을 가리고 있었다
처음부터 스냅샷을 봤으면 3분이면 끝났다
browser_snapshot 먼저, 코드 수정은 나중에
디버깅 워크플로우 (4단계)
Step 1: 실패 재현
↓
Step 2: browser_snapshot → 실제 UI 상태 확인
↓
Step 3: "기대 상태" vs "실제 상태" 비교
↓
Step 4: 차이점 기반으로 코드 수정
Step 1: 실패 재현
pytest tests/step_definitions/test_bench_steps.py -k "저장" --headed -x
--headed 로 브라우저를 보면서, -x 로 첫 실패에서 멈춘다
Step 2: browser_snapshot
Claude Code 에 Playwright MCP 가 연결되어 있으면 실제 페이지를 볼 수 있다
> 지금 페이지 상태 확인해줘
Claude 가 browser_snapshot 을 호출하면 접근성 트리가 나온다
[dialog] 모달 제목
[button] "닫기"
[button] "확인"
[main]
[button] "저장" (뒤에 가려져 있음)
여기서 바로 보인다 — dialog 가 main 위에 떠 있다
Step 3: 기대 vs 실제 비교
| 기대 상태 | 실제 상태 | |
|---|---|---|
| 모달 | 없음 | Feature Release 모달 떠 있음 |
| "저장" 버튼 | 보임 + 선택 가능 | DOM 에 있지만 모달에 가려짐 |
| Timeout 원인 | 버튼이 없어서 | 버튼은 있는데 선택 불가 |
이걸 먼저 정리해야 수정 방향이 나온다
Step 4: 코드 수정
# 원인: 모달이 버튼을 가림
# 수정: 저장 전에 모달 닫기
def click_save(self):
self.dismiss_feature_release_modal() # 모달 닫기 추가
self.page.get_by_role("button", name="저장").click()
로케이터를 바꾸는 게 아니라 사전 조건을 추가하는 거였다
에러 로그만 보면 로케이터 문제로 착각한다
실제 사례 3가지
사례 1: 드롭다운이 안 열림
에러: get_by_role("option", name="Korean") Timeout
추측: option 텍스트가 바뀌었나?
snapshot 결과: 드롭다운이 열리지 않은 상태
option 이 없는 게 아니라 드롭다운을 먼저 열어야 했다
사례 2: 체크박스를 못 찾음
에러: get_by_role("checkbox") Timeout
추측: checkbox 가 아니라 switch 인가?
snapshot 결과: 체크박스가 스크롤 아래에 있어서 뷰포트 밖
scroll_into_view() 를 추가해서 해결
사례 3: 버튼이 비활성화
에러: click 후 아무 반응 없음 (Timeout 아님, 동작 안 함)
추측: 로케이터가 틀렸나?
snapshot 결과: 버튼이 disabled 상태
이전 Step 에서 필수 입력을 빠뜨려서 버튼이 활성화되지 않았다
스냅샷 vs 스크린샷
| browser_snapshot | browser_take_screenshot | |
|---|---|---|
| 형태 | 접근성 트리 (텍스트) | 이미지 (PNG) |
| 정보량 | 요소 role, name, state | 시각적 레이아웃 |
| 용도 | 로케이터 확인, 구조 파악 | 시각적 상태 확인 |
| 속도 | 빠름 | 느림 |
디버깅은 snapshot 으로 시작한다
스크린샷은 "눈으로 봐야 아는 것" (색상, 위치, 애니메이션) 에만 쓴다
MCP 연결 방법
# Playwright MCP 추가
claude mcp add --scope project playwright -- npx @anthropic-ai/mcp-server-playwright
연결하면 쓸 수 있는 도구들:
| 도구 | 용도 |
|---|---|
browser_navigate |
URL 로 이동 |
browser_snapshot |
접근성 트리 확인 |
browser_click |
요소 선택 |
browser_take_screenshot |
스크린샷 저장 |
디버깅에서 가장 많이 쓰는 건 browser_snapshot 이다
사용법
# MCP 연결 확인
claude mcp list
# 디버깅 시 Claude Code 에서
> 검증 실패했어. 페이지 상태 확인해줘
# → Claude 가 browser_snapshot 호출
# → 접근성 트리 분석
# → 원인 파악 후 수정 제안
주의점
browser_snapshot은 접근성 트리 기반이다 —aria-hidden="true"인 요소는 안 보인다- 확인 끝나면 바로
browser_close해야 한다 — 열어둔 채로 코드 수정하면 세션이 꼬인다 pkill -f "Google Chrome"은 절대 쓰면 안 된다 — 사용자의 일반 Chrome 까지 전부 죽는다
반응형
'TIL > Claude Code' 카테고리의 다른 글
| [TIL][Playwright] 모달이 버튼 인덱스를 밀어낸다 — nth() 로케이터의 함정 (0) | 2026.04.04 |
|---|---|
| [TIL][Claude Code] "이게 최선이야?" 를 자동으로 묻게 만들기 (0) | 2026.03.30 |
| [TIL][Claude Code] CLAUDE.md 가 500줄이 된 이유 (0) | 2026.03.24 |
| [TIL][Claude Code] Claude Code 초기 세팅 #3 - 숨겨진 단축키와 기능 (0) | 2026.02.18 |
| [TIL][Claude Code] Claude Code 초기 세팅 #2 - MCP, Hooks, Skills 로 워크플로우 자동화 (0) | 2026.02.17 |