Devin.KR

키보드와 모바일 검증

210분 안팎

학습 목표

자동 검사와 수동 탐색의 확인 범위를 구분합니다.

개념

입력 수단과 화면 조건을 바꿉니다

마우스 클릭으로 상세를 열고 돌아오는 흐름은 통과하지만 키보드 사용자는 현재 위치를 잃을 수 있습니다. 또한 데스크톱에서 보이는 버튼이 좁은 화면에서는 잘릴 수 있습니다. 이 레슨은 같은 탐색 목표를 입력 수단과 화면 조건을 바꿔 검사합니다. 자동 검사는 특정 환경에서 재현 가능한 근거를 만들고 수동 관찰은 그 밖의 실제 사용을 확인합니다.

키보드의 시작은 문서를 연 다음 첫 Tab을 누르는 순간입니다. 입력을 locator.focus로 강제하면 Tab 순서에 들어갈 수 없는 결함을 우회합니다. 제공 검사는 첫 Tab에서 검색어가 활성 요소인지 확인하고 keyboard.type으로 입력한 뒤 Enter로 제출합니다. click 없이도 조건 제출이 가능해야 하며 검색 버튼만 click 처리한 구현은 이 흐름에서 드러납니다.

Tab은 다음 조작 요소로, Shift+Tab은 이전 요소로 이동합니다. 선택 상자의 실제 조작은 기본 키보드 동작을 따르며, 이 과제의 자동 검사는 검색 이후 카드에 도달하는 경로를 중심으로 봅니다. 카드까지 Tab을 제한된 횟수로 진행한 뒤 toBeFocused를 단언합니다. 횟수 제한은 무한 반복을 막기 위한 것이고 마지막에는 정확한 대상에 도달했는지를 확인합니다.

상세 진입은 카드 링크에서 Enter를 눌러 진행합니다. 비동기 상세가 완료되면 detail-title이 포커스를 받습니다. 제목은 일반적인 Tab 순서에는 넣지 않지만 tabindex=-1로 프로그램 포커스를 허용합니다. 이 정책은 새로운 화면의 위치를 알려 주는 선택입니다. 모든 상태 변경마다 제목을 강제로 선택하면 입력 중인 사용자의 작업을 끊으므로 전환 시점에 한정합니다.

상세 제목 다음 Tab은 목록으로 돌아가기 링크로 이어집니다. Enter로 복귀한 뒤 방금 선택한 카드 링크가 포커스를 받아야 다시 탐색할 수 있습니다. 결과가 새로 렌더링되므로 이전 링크 객체를 저장해 focus하는 방법은 안전하지 않습니다. 화면에 있는 현재 링크를 식별자로 다시 찾습니다. 해당 행사가 사라진 경우에는 검색어 입력을 대체 위치로 사용합니다.

포커스 참조와 시각적 표시도 구분합니다. toBeFocused는 활성 요소를 검사하고, :focus-visible 상태의 outline 폭과 style 검사는 표시가 완전히 제거되지는 않았는지 확인합니다. 계산된 스타일이 있다고 외곽선이 배경에서 잘 보인다는 뜻은 아닙니다. 클리핑·색 대비·주변 가림은 실제 화면에서 확인하고 MANUAL.md에 관찰을 적습니다.

오류 복구도 키보드만으로 진행합니다. 500 응답을 받은 뒤 Tab으로 다시 시도 버튼에 도달하고 Enter를 누릅니다. 성공하면 버튼이 숨겨져 활성 요소가 사라질 수 있으므로 앱은 재시도 의도를 기억해 검색어로 옮깁니다. 화면이 업데이트됐다는 이유로 body에 위치가 남는 것을 정상으로 보지 않습니다. 다음 행동이 가능한 위치인지가 검사 기준입니다.

자동 검사에서 기본 동작을 확인하려면 실제 브라우저가 필요합니다. Node DOM 모델의 dispatch는 Enter가 submit을 만드는 브라우저 규칙을 구현하지 않습니다. 모델 focus는 요소 참조만 바꾸므로 tabindex 조건이나 외곽선도 판단하지 않습니다. 두 환경의 검사 이름을 구별하면 같은 성공이라는 표현이 서로 다른 의미를 갖는 혼동을 줄일 수 있습니다.

모바일 검사는 320·390·1280px의 viewport 너비를 사용합니다. 320은 좁은 경계, 390은 일상적인 작은 화면 예시, 1280은 넓은 화면 예시입니다. 이 숫자가 모든 기기를 대표하는 것은 아닙니다. audit-plan.mjs의 widths를 채워 같은 fixture를 세 조건에서 실행합니다. 검사 계획 자체의 누락도 Node 테스트가 발견하므로 넓은 화면 한 개만 남기는 실수를 막습니다.

짧은 제목만 사용하면 긴 텍스트가 카드 최소 너비를 밀어내는 문제를 놓칩니다. 제공 fixture는 공백 없는 긴 제목을 반복해 목록과 상세에서 줄바꿈을 압박합니다. 각 화면에서 documentElement의 scrollWidth와 clientWidth 차이를 계산하고 1px 이하인지 확인합니다. 작은 반올림 오차는 허용하되 큰 가로 스크롤은 허용하지 않는 기준입니다.

넘침 숫자만 통과시키려고 overflow-x:hidden을 붙이면 내용과 버튼을 잘라 숨길 수 있습니다. 카드의 min-width와 grid 열과 텍스트 줄바꿈 원인을 먼저 찾습니다. 상세의 복귀 링크도 화면에 보여야 합니다. 가로 스크롤이 없다는 단언은 겹침·잘림·읽기 편함까지 증명하지 않으므로 좁은 화면의 실제 화면 캡처나 직접 관찰로 보완합니다.

viewport 크기 변경과 실제 모바일은 같은 검사가 아닙니다. 작은 너비의 데스크톱 context는 화면 공간을 줄이지만 터치·가상 키보드·주소창·OS 글자 크기를 모두 재현하지 않습니다. 실제 기기에서는 검색어 입력 시 버튼과 안내가 가려지지 않는지, 상세에서 복귀 링크를 손가락으로 조작할 수 있는지 확인합니다. 기기와 브라우저를 기록해야 결과를 해석할 수 있습니다.

확대는 브라우저 메뉴로 200%와 400%를 적용해 확인합니다. 단순 CSS zoom 주입이나 viewport 축소를 확대 완료의 증거로 쓰지 않습니다. 글이 늘어난 뒤 검색어 이름과 입력과 결과 안내와 복귀 행동이 읽히는지 관찰합니다. 확대율과 창 크기를 같이 적으면 재현이 가능합니다. 겹치는 위치가 있으면 어느 조작을 막는지 구체적으로 기록합니다.

한국어 입력은 조합 중간과 확정 후의 동작을 실제 IME로 확인합니다. keyboard.type이나 composition 이벤트 주입은 실제 입력기의 완전한 대체가 아닙니다. 조합 중 요청이 발생하는지와 확정 후 마지막 조건이 유지되는지를 Network에서 살펴봅니다. 이를 자동 검사 통과에서 추론하지 않고 수동 확인 항목으로 남깁니다. 앞 모듈의 가짜 clock 검사는 요청 예약 정책을 따로 보호합니다.

흔한 실패는 키보드로 링크에 도달하기 전에 숨겨진 조작 요소에 멈추거나, 상세 제목을 활성화하지 못하거나, 복귀에서 새 카드가 아닌 검색어로 이동하는 경우입니다. toBeFocused의 실제 대상과 기대 대상을 읽어 어느 전환에서 위치를 잃었는지 찾습니다. 넘침 실패에는 검사 이름의 폭과 목록·상세 구분이 함께 나오므로 그 조건으로 화면을 줄여 원인을 관찰합니다.

실습 starter의 audit-plan은 넓은 화면과 확대 한 항목만 있습니다. 제공한 테스트의 검사 의도를 읽고 경계 너비와 수동 조건을 보완합니다. 코드를 고치는 것 외에도 MANUAL.md의 실제 관찰 칸을 채우는 일이 제출물입니다. 실행하지 않은 칸에는 미확인을 유지합니다. 테스트 수가 늘었더라도 실제 모바일·확대·스크린리더를 모두 검증했다고 쓰지 않습니다.

접근성 완료라는 넓은 주장보다 구체적인 흐름과 환경을 설명합니다. “Chromium의 Tab·Enter 검색과 상세 복귀를 검사했고 실제 확대는 미확인” 같은 기록은 다음 검토자가 후속 검사를 정할 수 있게 합니다. 더 읽기의 HTML 검색 폼 장에서 이름·제출 관계를 다시 확인하고, 다음 레슨에서는 여기서 찾은 포커스 회귀를 실패 테스트로 고정합니다.

따라하기

자동 모델과 수동 범위를 분류합니다

검사 기록을 정렬해 서로 다른 근거가 합쳐지지 않게 합니다.

const checks=[{name:'활성 요소',kind:'browser'},{name:'실제 확대',kind:'manual'},{name:'모델 참조',kind:'node'}];for(const x of checks)console.log(x.kind+': '+x.name);

실행 결과

browser: 활성 요소
manual: 실제 확대
node: 모델 참조

빠진 경계 폭을 채웁니다

audit-plan.mjs에 320·390·1280과 수동 관찰 항목을 작성합니다. starter의 계획 누락 assertion이 실패하는지 먼저 보고 수정합니다.

node --test flow-test.mjs

넘침 수치의 의미를 읽습니다

accessibility.spec.cjs의 긴 제목 fixture에서 아래 흐름을 찾습니다. 한 건을 반환하는 모의를 먼저 설치한 test 안에서 실행하는 코드입니다. difference는 목록 완료 뒤 문서 가로 넘침이며, 내용 가림이나 실제 확대 판정을 대신하지 않습니다. 이 환경의 브라우저 실행 결과는 PENDING입니다.

// e2e/accessibility.spec.cjs의 test 내부 검사입니다.
await page.setViewportSize({width:320,height:900});
await page.goto(origin + '/results.html');
await expect(page.locator('#result-summary')).toHaveText('예시 행사 1건입니다.');
const difference = await page.evaluate(() =>
  document.documentElement.scrollWidth - document.documentElement.clientWidth);
expect(difference).toBeLessThanOrEqual(1);

키보드와 긴 제목을 검사합니다

외부 브라우저 실행 환경에서 Tab·Enter 경로와 폭별 목록·상세 넘침을 확인합니다. 실제 실행 여부를 MANUAL.md에 적습니다.

npm run test:e2e -- --grep "Tab|키보드|320|검사 계획"

수동 확대와 실제 입력을 기록합니다

서버를 열고 README 주소로 접속합니다. 브라우저 메뉴에서 200%·400%를 설정해 검색과 복귀를 관찰하고 실제 IME와 터치도 기록합니다. 실행 터미널의 Ctrl+C로 서버 close를 요청합니다.

npm start

확인 문제

실습

audit-plan.mjs의 검사 폭과 수동 관찰 범위를 완성합니다. accessibility.spec.cjs의 Tab·Enter·재시도·넘침 검사를 실행하고 MANUAL.md의 실제 확대·IME·터치 관찰을 작성합니다. npm test로 모델을 먼저 검사합니다. 브라우저 설치 후 bash check.sh는 모델과 Playwright를 모두 실행합니다. 서버는 harness가 열고 close로 닫으며 이 샌드박스의 브라우저 판정은 PENDING입니다.

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

실행 명령

bash check.sh

기대 결과

npm test의 의도된 starter assertion 실패·solution 모델 전부 통과; 설치 후 Playwright starter 실패·solution 전부 통과는 외부 검증 PENDING

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

더 읽기

면접 질문

  • 키보드만으로 폼을 사용하는 흐름을 설명합니다.
  • 좁은 화면에서 레이아웃이 깨지는 문제의 확인 방법을 설명합니다.