Devin.KR

사용자 관점의 요소 찾기

120분 안팎

학습 목표

역할·레이블 기반으로 가입 폼 요소를 찾습니다.

개념

DOM 순서 대신 사용자의 단서를 씁니다

가입 폼 앞에 안내 입력창이 하나 추가되었다고 테스트가 다른 칸에 이메일을 넣으면 선택자가 화면의 목적을 표현하지 못한 것입니다. input의 두 번째 위치나 긴 CSS 경로는 현재 구조를 찾지만 사용자가 어떤 정보를 입력하는지 설명하지 않습니다. 이번 레슨은 역할과 접근 가능한 이름, 연결된 레이블로 동아리 가입·로그인 폼을 구별하고 실제 브라우저 행동을 검증합니다.

locator는 요소를 찾는 방법을 표현한 객체입니다. getByRole은 button·form 등의 역할과 name 조건을 묶고 getByLabel은 폼 컨트롤에 연결된 레이블을 찾습니다. 버튼의 가입하기라는 이름, 입력칸의 이메일 레이블이 사용자 행동과 테스트를 연결합니다. 문법은 Playwright locator 공식 문서에서 확인할 수 있습니다. 접근 가능한 이름 전체 계산이나 DOM 생성 방법은 이 레슨의 구현 범위와 구분합니다.

가입과 로그인에 같은 레이블이 있습니다

제공 ui.html에는 가입과 로그인 두 폼이 모두 이메일·비밀번호 레이블을 가집니다. page.getByLabel(이메일)처럼 페이지 전체를 대상으로 입력하면 후보가 둘이라 엄격한 선택에서 실패할 수 있습니다. 먼저 이름이 가입인 form 역할을 찾고 그 locator에서 이메일을 찾습니다. 로그인은 이름이 로그인인 form부터 좁힙니다. 범위와 이름을 결합하면 다른 폼의 동일 문구를 구별합니다.

form(page, 이름)은 page.getByRole에 form 역할, name과 exact:true를 전달하는 제공 함수입니다. const f=form(page, 가입)으로 범위를 고른 뒤 f.getByLabel에 이메일과 exact:true를 넣습니다. exact는 가입과 가입하기처럼 부분 문자열이 비슷한 이름을 구별하는 데 사용합니다. 화면 이름 자체가 바뀌면 요구 변경 여부를 검토해야 하며 정확 일치가 모든 변경을 자동 해결하지는 않습니다.

비밀번호 입력은 label로 찾습니다. 비밀번호 형태의 input을 일반 textbox 역할로 찾겠다고 가정하면 브라우저 접근성 의미와 맞지 않을 수 있습니다. 역할을 외우기보다 제공 HTML의 label for와 input id 연결을 확인하고 대응하는 사용자 단서를 선택합니다. placeholder만 있는 새 입력칸을 발견하면 선택자 우회와 별개로 사용성 문제를 팀에 질문합니다.

찾기와 업무 결과를 분리합니다

actions.cjs의 signup은 이메일·비밀번호·닉네임을 채우고 가입하기를 클릭한 다음 status의 가입 완료를 단언합니다. login은 다른 폼을 채우고 내 프로필 영역, 표시된 자기 이메일, 로그인 완료를 확인합니다. 가입 성공 메시지를 로그인 성공으로 재사용하지 않습니다. 화면에 보이는 영역이 자기 계정인지까지 확인하면 앞 실행의 다른 사용자를 표시하는 결함을 더 잘 찾습니다.

프로필 화면의 닉네임에는 사용자에게 별도 레이블이 없으므로 제공된 안정적 id를 계약으로 사용합니다. 역할·레이블을 우선한다는 원칙이 모든 요소에 같은 방법을 강제하는 규칙은 아닙니다. 입력은 레이블, 버튼은 역할과 이름, 단순 출력은 합의한 식별자를 선택합니다. 선택자가 어느 계약을 표현하는지 설명하고 HTML 구현의 우연한 중첩에 기대지 않습니다.

수정은 새 닉네임을 입력하고 저장하기를 누른 뒤 실제 표시값을 비교합니다. 이어 동일 세션의 API로 nickname이 수정회원인지 읽습니다. 둘 중 어느 단언이 실패했는지로 UI 반영과 저장 효과를 나눠 조사합니다. 마지막 로그아웃에서는 영역 숨김과 API 401을 함께 관찰하여 화면만 지운 상태와 서버 세션 종료를 구별합니다.

starter를 완성하는 순서입니다

qa-ui-locators의 starter에서 actions.cjs를 열면 signup의 f가 page로 지정되어 있습니다. TODO를 form(page, 가입)을 호출하는 형태로 고칩니다. 레이블과 버튼의 exact 조건은 유지합니다. 제공 테스트를 실행하면 가입 여정, 중복 이메일, 키보드 이동, 잘못된 비밀번호 네 사례를 검사하도록 구성되어 있습니다. 이 네 사례의 실제 렌더링 결과는 외부 브라우저 검증에서 확인합니다.

테스트 파일은 같은 기대값으로 starter와 solution을 검사합니다. 중복 사례는 자기 이메일을 API로 먼저 준비한 뒤 UI에서 같은 이메일로 재가입합니다. 알림이 이미 가입한 이메일입니다로 보이고 기준 행 목록과 기존 닉네임이 유지되어야 합니다. 매번 랜덤 이메일로 바꾸면 이 사전 조건이 사라지므로 실패를 피하려고 데이터를 바꾸지 않습니다.

키보드 행동은 실제 포커스를 봅니다

keyboardSignup은 가입 이메일에 초점을 놓고 키보드로 글자를 입력한 뒤 Tab을 누릅니다. 비밀번호, 닉네임, 가입하기 순서로 toBeFocused를 비교하고 Enter로 제출합니다. 버튼을 프로그램으로 클릭한 결과만으로 키보드 접근을 증명할 수 없습니다. 초점은 실제 브라우저가 이동시킨 결과를 관찰합니다. DOM 문자열 순서를 읽는 빠른 검사는 이 증거를 대신하지 않습니다.

첫 입력에 focus를 호출한 것은 시작점을 명시한 것입니다. 페이지 진입부터 모든 탐색 경로를 평가한 것으로 보고하지 않습니다. Shift+Tab, 화면 읽기 프로그램, 다양한 확대 비율은 별도 범위입니다. Tab 순서 검사가 실패하면 실제 활성 요소와 disabled·hidden 속성, 의도치 않은 tabindex 변경을 확인하고 사용자 기대와 HTML 순서가 맞는지 검토합니다.

선택 실패 메시지를 해석합니다

strict mode violation은 동작할 요소가 하나로 정해지지 않았다는 신호입니다. 일치 후보의 개수와 각각 속한 폼을 보고 범위를 좁힙니다. first나 nth로 하나를 고르면 우연히 통과할 수 있지만 가입과 로그인 구별이 없어집니다. 중복 요소가 제품 결함인지 테스트 선택자 누락인지 화면과 이름 계약을 함께 확인한 뒤 수정합니다.

Timeout 메시지에서 locator를 기다렸다고 나오면 요소가 없는 경우, 이름이 다른 경우, 요소는 있어도 행동 조건이 안 된 경우를 구별합니다. 제공 앱의 해당 폼이 숨겨져 있거나 로그인 사전 조건이 실패했는지 먼저 읽습니다. 모든 실패를 선택자 오타로 단정하지 않습니다. 테스트 이름, 마지막 요청 상태, 선택 대상과 실제 화면을 연결하면 다음 조사를 구체화할 수 있습니다.

요소 계약의 변경을 리뷰합니다

새 화면에서 label과 입력 연결이 빠지면 CSS 선택자로 테스트만 통과시켜도 사용자의 레이블 클릭과 보조 기술 이용 문제는 남습니다. 자동화를 유지하는 수정과 제품 접근성 결함 수정의 책임을 분리해 기록합니다. locator가 정상 선택되었다는 사실은 전체 접근성 검사 결과가 아니며 사용자 단서를 드러내는 작은 피드백입니다.

레슨 제출에는 수정한 signup 함수와 네 UI 사례의 결과, 실패가 있었다면 폼 범위를 결정한 근거를 남깁니다. 실제 브라우저 실행이 어려운 환경에서는 PENDING과 기동 오류를 적으며 core 검사만으로 UI 통과를 쓰지 않습니다. 일반 DOM 선택·생성·textContent의 의미는 더 읽기에서 이어가고 여기서는 실제 가입 폼의 사용자 행동과 단언을 완성합니다.

따라하기

두 폼의 같은 레이블을 확인합니다

app/src/main/resources/static/ui.html에서 가입·로그인 폼과 label 연결을 확인합니다. 이 발췌는 HTML 설명이며 실행 결과가 아닙니다.

<form aria-label="가입"><label for="se">이메일</label><input id="se"></form>
<form aria-label="로그인"><label for="le">이메일</label><input id="le"></form>

후보가 둘인 선택을 진단합니다

브라우저 DOM 대신 후보 메타데이터 두 개로 범위를 설명합니다. 단순 배열 출력은 실제 locator 실행을 증명하지 않습니다.

const candidates=[{form:'가입',label:'이메일'},{form:'로그인',label:'이메일'}];console.log('전체',candidates.length);console.log('가입',candidates.filter(x=>x.form==='가입').length);

실행 결과

전체 2
가입 1

signup의 범위를 고칩니다

app/ui/actions.cjs에서 const f=page를 아래 줄로 교체합니다. 제공 form 함수는 역할과 정확한 폼 이름으로 범위를 만듭니다.

const f=form(page,'가입');

네 브라우저 사례를 실행합니다

압축 루트에서 실행합니다. flow·duplicate·keyboard·badlogin의 결과와 종료 코드를 확인합니다. 오류에는 후보 폼·단언 위치를 기록합니다. 샌드박스에서는 이 UI 실행이 PENDING입니다.

bash check.sh

확인 문제

실습

actions.cjs의 signup TODO에서 가입 폼 범위를 완성합니다. 네 실제 UI 사례와 기존 API 검사 기대값을 유지합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

기존 API 검사와 브라우저 4사례 실패 0

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • UI 자동화에서 고정 시간 대기를 줄이는 방법을 설명해 주시면 됩니다.