Devin.KR

키보드와 오류 안내

100분 안팎

학습 목표

포커스 순서와 입력 오류의 안내 위치를 확인합니다.

개념

키보드 점검은 기능 점검입니다

키보드 사용은 특별한 추가 기능이 아니라 링크 이동과 검색 제출을 사용할 수 있는지 확인하는 경로입니다. 마우스로는 동작하지만 Tab으로 도달할 수 없는 검색 버튼이 있다면 사용자 흐름 일부가 끊긴 것입니다. 지역 행사 페이지를 처음 연 상태에서 시작해 검색과 상세 이동을 끝까지 시험합니다. 성공한 클릭 화면 한 장보다 이동 순서와 실패 조건을 기록한 메모가 수정 근거로 유용합니다.

문서 순서를 이동 순서로 사용합니다

기본 링크, 입력, 선택 상자, 버튼은 브라우저의 키보드 조작을 제공합니다. 이 페이지에서 Tab으로 먼저 소개와 목록 링크를 지나고 검색어, 지역, 검색 버튼, 상세 링크로 갑니다. 폼 안의 순서는 검색어에서 지역을 거쳐 검색 버튼입니다. Shift+Tab으로 돌아가도 같은 요소를 역방향으로 방문해야 합니다. h1과 일반 문단이 Tab 정지점이 아니라고 문제는 아닙니다. 읽기 구조 탐색과 폼 조작의 포커스 이동은 구별합니다.

양수 tabindex로 검색어를 맨 먼저 만들면 뒤에 추가된 메뉴나 다른 입력과 순서가 엇갈릴 수 있습니다. 이 프로젝트에서는 DOM 순서를 사용자 흐름과 맞추고 양수 tabindex를 쓰지 않습니다. tabindex=-1은 스크립트로 포커스할 대상을 만들 때 사용할 수 있지만 지금은 필요하지 않습니다. 모든 문단에 tabindex=0을 넣으면 조작할 필요 없는 내용 때문에 Tab 이동이 길어집니다. 기본 요소의 동작을 보존하는 것이 출발점입니다.

현재 위치를 볼 수 있어야 합니다

Tab을 눌렀을 때 무엇이 선택됐는지 눈으로 확인합니다. 제공된 CSS의 :focus-visible은 브라우저가 포커스 표시가 필요하다고 판단하는 상황에 외곽선을 줍니다. 브라우저의 기본 포커스 표시도 유용합니다. outline을 없애고 다른 표시를 제공하지 않으면 사용자가 위치를 잃습니다. outline-offset은 외곽선과 요소 사이 간격을 줍니다. 이 단계의 파란 외곽선은 시작 예시이며 다양한 배경에서의 시인성과 대비는 다음 모듈과 품질 검증 단계에서 다시 확인합니다.

테스트가 outline 문자열을 찾았다고 화면에서 표시가 보인다는 보장은 없습니다. 다른 CSS가 덮어쓰거나 잘릴 수 있습니다. 실제 브라우저에서 메뉴, 검색 입력, 지역, 버튼의 포커스를 차례로 확인합니다. 선택 상자 조작은 운영체제와 브라우저에 따라 열기 키가 다를 수 있습니다. Tab으로 지역에 도달하고 해당 환경의 키로 선택을 바꾼 뒤 다음 요소로 이동할 수 있는지를 기록합니다. 특정 환경에서 관찰하지 않은 키 동작을 모든 환경의 결과로 적지 않습니다.

오류는 이름과 설명에 이어집니다

빈 검색어를 제출하면 required가 기본 제약 검증을 작동시킵니다. 브라우저가 표시하는 문구와 형태는 언어 설정과 제품에 따라 다르므로 동일한 문장을 예상 출력으로 고정하지 않습니다. 검색어의 label은 무엇을 입력하는지, query-help 설명은 한 글자 이상이라는 조건과 예시를 제공합니다. input의 aria-describedby는 설명의 id를 참조합니다. 설명이 화면에 있다는 사실만으로 프로그램이 입력과의 관계를 알 수 있는 것은 아니므로 참조도 검사합니다.

이 모듈은 별도 사용자 정의 오류 메시지를 생성하지 않습니다. 실패 전에도 보이는 연결된 안내와 브라우저의 기본 검증을 함께 사용합니다. aria-invalid=true를 처음부터 붙이면 정상 입력도 오류처럼 전달될 수 있습니다. 실제 실패 상태를 관리하지 않는 정적 문서에서는 붙이지 않습니다. 나중에 사용자 정의 오류를 만들 때는 오류를 지우는 시점과 포커스 처리도 설계해야 합니다. 현재 화면에서 검증되지 않은 동적 오류 기능을 구현했다고 주장하지 않습니다.

재현 기록을 남깁니다

브라우저 이름과 OS를 적고 빈 입력, 책과 서울, 책과 전체 지역의 세 흐름을 실행합니다. 실패한 경우 마지막 포커스 위치와 주소 이동 여부를 적습니다. Enter 제출과 버튼 제출은 각각 시험합니다. 주소에 q와 region이 전달되고 결과 화면에서 돌아가기 링크가 동작하는지 확인합니다. 아직 결과 데이터가 바뀌지 않는 것은 명시된 범위입니다. 검색어를 공백만 입력하는 경계는 기본 required만으로 차단되지 않는다는 한계도 기록해 다음 작성자가 검색 규칙을 이어서 구현하게 합니다.

역방향 점검으로 빠진 구간을 찾습니다

검색 버튼에 도달한 뒤 Shift+Tab을 눌러 지역과 검색어로 돌아옵니다. 순방향에서는 지나친 포커스 표시가 역방향에서는 보이지 않는다면 표시 규칙이나 화면 위치를 살펴봅니다. 상세 문서로 이동했다가 돌아올 때 포커스 복원 방식은 브라우저와 탐색 방식에 따라 달라질 수 있으므로 관찰한 위치를 그대로 적습니다. 원하는 결과를 미리 기록하고 맞았다고 처리하지 않습니다. 브라우저 도구나 주소 표시줄로 포커스가 이동한 경우에는 문서 안의 이동과 구별해 메모합니다. 문서 조작을 가로막는 지점이 어디인지 알 수 있도록 마지막으로 성공한 요소를 남깁니다.

키보드 시험은 마우스로 버튼을 클릭한 시험과 분리합니다. 검색어에 책을 넣고 지역을 서울로 바꾼 다음 입력에서 Enter를 누르는 경로를 먼저 확인합니다. 다시 돌아와 같은 조건을 넣고 Tab으로 검색 버튼까지 간 뒤 버튼의 기본 키보드 조작으로 제출합니다. 두 경로의 주소에서 q와 region을 비교합니다. 선택 상자에서 누른 Enter는 선택 확정으로 사용될 수 있으므로 그 위치의 Enter 동작과 입력에서의 제출을 같은 시험으로 기록하지 않습니다. 어느 요소에 포커스가 있었는지가 재현의 핵심입니다.

빈 입력과 안내의 관계를 확인합니다

검색어를 완전히 비운 뒤 제출하여 페이지가 이동하지 않는지 확인합니다. 브라우저가 입력으로 포커스를 옮기는지, 어떤 안내가 나타나는지, 안내를 닫고 바로 내용을 입력할 수 있는지 관찰합니다. 이후 책을 넣고 다시 제출해 이전 실패 때문에 정상 제출까지 막히지 않는지 봅니다. required의 존재만 읽는 검사는 이 복구 흐름을 보여 주지 못합니다. 항상 보이는 설명은 사용자가 실패하기 전 조건을 알게 하는 역할이고 기본 검증 안내는 제출 시의 제약 위반을 알리는 역할입니다. 둘을 같은 메시지라고 설명하지 않습니다.

관찰 기록을 수정 요청으로 바꿉니다

KEYBOARD.md에는 환경, 시작 상태, 조작, 관찰 결과를 연결해 씁니다. 예를 들어 검색어가 빈 상태에서 검색 버튼을 키보드로 작동시켰을 때 이동이 막혔고 검색어 위치에서 안내를 확인했다는 식입니다. 포커스가 안 보인다면 어느 요소와 어떤 배경에서 사라졌는지 적습니다. 순서가 엇갈린다면 기대 순서와 실제 방문 순서를 함께 남깁니다. 확인하지 않은 스크린 리더 발화나 다른 운영체제의 키 조합은 미확인으로 두어야 기록의 신뢰도가 유지됩니다. 다음 수정자는 이 기록으로 같은 문제를 재현하고 바뀐 동작을 비교할 수 있습니다.

따라하기

실습 폴더 준비

starter 압축을 풀고 그 폴더를 편집기로 엽니다. 터미널도 package.json이 있는 폴더에서 시작합니다. 다음 명령은 외부 의존 패키지가 없는 잠금 파일을 사용합니다.

npm ci
npm test

starter의 FAIL 항목을 먼저 읽습니다. npm 설치나 Python 실행 실패는 환경 문제이므로 HTML 수정 전에 해결합니다.

계약에 맞게 문서 수정

index.html에서 다음 구조를 기준으로 해당 레슨의 FAIL 항목을 고칩니다. 아래는 최종 문서의 참고 코드이며 그대로 저장하면 같은 파일 구성에서 사용할 수 있습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>우리 동네 행사 찾기</title>
  <link rel="stylesheet" href="assets/site.css">
</head>
<body>
  <header><p>우리 동네 행사</p><nav aria-label="주요 메뉴"><a href="#intro">소개</a> <a href="#events">행사 목록</a></nav></header>
  <main>
    <h1>우리 동네 행사 찾기</h1>
    <section id="intro" aria-labelledby="intro-title">
      <h2 id="intro-title">가까운 곳에서 함께 만나요</h2>
      <p>지역과 검색어로 관심 있는 행사를 찾아보세요. 현재는 예시 행사만 제공합니다.</p>
    </section>
    <section aria-labelledby="search-title">
      <h2 id="search-title">행사 검색</h2>
      <p id="query-help">검색어를 한 글자 이상 입력하세요. 예: 책</p>
      <form action="results.html" method="get">
        <label for="query">검색어</label>
        <input id="query" name="q" type="search" required aria-describedby="query-help">
        <label for="region">지역</label>
        <select id="region" name="region">
          <option value="all">전체 지역</option>
          <option value="seoul">서울</option>
          <option value="busan">부산</option>
        </select>
        <button type="submit">검색</button>
      </form>
    </section>
    <section id="events" aria-labelledby="events-title">
      <h2 id="events-title">예시 행사 목록</h2>
      <article><h3>동네 책 나눔</h3><p>서울 · 10월 20일 · 무료</p><a href="event.html">동네 책 나눔 상세 보기</a></article>
    </section>
  </main>
  <footer><p>교육용 행사 정보이며 실제 모집 공고가 아닙니다.</p></footer>
</body>
</html>

파일 검사 재실행

저장한 뒤 실습 폴더에서 검사합니다. 마지막 실패 수가 0인지 확인합니다. 출력은 solution을 실행한 실제 검사 결과입니다.

npm test

실행 결과

> frontend-m01-keyboard-check@1.0.0 test
> python3 check.py

PASS 한국어 문서와 제목
PASS UTF-8 및 작은 화면 설정
PASS 스타일 파일 상대 경로
PASS 목록 및 상세 파일 존재
PASS 중복 id 없음
PASS 모든 내부 링크 목적지 존재
PASS main과 h1 각각 하나
PASS 주요 메뉴와 행사 article
PASS 소개와 목록 제목 연결
PASS 행사 제목 h3
PASS GET 검색 목적지
PASS 검색어 레이블 연결
PASS 지역 레이블과 전체 옵션
PASS 명시적 제출 버튼
PASS 빈 입력 방지 및 설명 연결
PASS DOM 순서 검색어 지역 검색
PASS 양수 tabindex 없음
PASS 포커스 외곽선 존재
검사 18개, 실패 0개

브라우저에서 사용자 흐름 확인

index.html을 브라우저의 파일 열기로 엽니다. 소개와 목록 링크, 상세 페이지와 돌아가기를 확인합니다. 검색어에 책을 넣고 지역을 서울로 골라 Enter를 누릅니다. results.html 주소의 q와 region을 확인합니다. 다시 돌아와 빈 입력 제출과 Tab·Shift+Tab을 시험하고 KEYBOARD.md의 미확인을 실제 관찰로 바꿉니다. 브라우저 결과는 직접 관찰하므로 고정 출력이 없습니다.

확인 문제

실습

starter의 index.html을 수정해 이 레슨의 검사 계약을 통과시키세요. package.json 폴더에서 npm ci 후 npm test를 실행합니다. check.py는 수정하지 않습니다. 브라우저에서 링크·검색·키보드 흐름을 확인하고 KEYBOARD.md에 관찰 결과를 기록합니다. solution은 비교용이며 수동 검증 결과를 대신하지 않습니다.

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

실행 명령

npm test

기대 결과

모든 PASS 항목과 마지막 실패 0개를 확인합니다. 수동 점검 기록도 제출합니다.

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

더 읽기

면접 질문

  • 양수 tabindex보다 DOM 순서를 먼저 정리하는 이유는 무엇인가요?
  • required와 aria-describedby는 입력 안내에서 각각 어떤 역할을 하나요?