Learn Next.js_CH14. Improving Accessibility

HyeKong·2024년 6월 22일

Learn Next.js

목록 보기
12/14

폼 유효화가 필요하다.
어떻게 서버액션으로 서버 사이드 검증를 하는지와, 리액트의 useActionState 훅을 통해 에러를 보여주는지 배워보자

In this chapter

접근성 향상을 위해 Next.js와 eslint-plugin-jsx-a11y 를 사용하는 법
서버 사이드 폼 유효성 검사를 하는 법
폼 에러를 처리하고 사용자에게 보여주기 위해 리액트 useActionState 훅을 쓰는 법

What is accessibility?

모두가 사용할 수 있는 웹 디자인 및 구현을 가리킴
키보드 네비게이션, 시멘틱 태그, 이미지 등 다양한 주제를 내포함

이번 챕터에선 Next.js에서 가능한 접근성의 특징을 배우고 몇 실습을 한다.

Using the ESLint accessibility plugin in Next.js

디폴트로 Next.js는 eslint-plugin-jsx-a11y 플러그인을 갖고 있어 법근성 이슈를 잡는 걸 도운다.
예시로, 이 플로그인은 이미지를 alt 없이 입력 시, aria-* and role 를 잘못되게 사용한다.

"lint": "next lint",

를 패키지.json에 추가한다.

그리고 pnpm lint를 실행한다. (내 경우에는 npm run lit)

다음 메시지를 볼 수 있다.

그러나 이미지를 alt 없이 넣는다면?
/app/ui/invoices/table.tsx로 가서 alt를 지운다.
lint 재실행 시 다음과 같은 문구가 뜬다.

만약 vercel로 앱 배포 시, 경고는 빌드 로그에서 뜰 것이다.
이것은 next lint가 빌드 과정의 일부분이기 때문이다.
따라서 배포 전에 접근성 이슈를 잡기 위해 lint를 실행할 수 있다.

Improving form accessibility

접근성 향상을 위해 이미 하고 있는 것들

  • 시맨틱 html : div 대신 시맨틱 요소 사용하는 것. AT가 인풋 요소에 집중되게 하고 적절한 문먁 정보를 제공하며 폼 항해, 이해를 돕는다
  • 라벨링: htmlFor 속성과 label 태그를 추가하는 것은 묘사적이다. 이것은 문맥 제공을 통해 AT 향상을 돕고 사용자가 일치하는 인풋 필드에 포커스 될 수 있도록 클릭하게 함으로써 사용성을 향상한다.
  • Focus Outline: 필드는 포커스 시 아웃라인을 보여줘야 한다. 이것은 시각적으로 진행중 요소를 나타내고 키보드와 스크린 리더 사용자가 어디 폼에 있는지 알 수 있게 하기 때문이다. tab을 통해 점검할 수 있다.

위 사례들은 접근성 향상에 좋지만, 폼 유효성 검사와 에러는 안 다룬다.

Form validation

http://localhost:3000/dashboard/invoices/create 에서 빈 폼을 제출한다면?
=> 에러가 난다. 서버 액션에 빈 폼 값을 보내기 때문이다. 이것을 클라이언트 혹은 서버에서 검증을 통해 방지할 수 있다.

Client-Side validation

몇 가지 방법 중 가장 간단한 것은 인풋과 select 요소에 required를 추가하는 것이다.

<input
  id="amount"
  name="amount"
  type="number"
  placeholder="Enter USD amount"
  className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
  required
/>

다시 폼 제출 시, 경고를 볼 수 있다.
이 접근 역시 AT가 브라우저 유효성 검사를 하므로 괜찮다.

클라이언트 사이드 검증의 대안은 서버 사이드 검증이다.
이제 다시 required를 지운다.

Server-Side validation

서버에서 검증 하기 위해 다음이 필요하다.

  • 데이터가 DB에 보내지기 전 기대한 포맷인지 확실하게 한다.
  • 클라이언트 사이드 검증을 못된 사용자가 통과하는 것의 위험을 줄인다.
  • 유효한 데이터로 간주되는 한 truth를 가진다.

create-form.tsx 컴포넌트에서, useActionState를 불러온다.
useActionState가 훅이기에, 폼을 'use client';를 통해 클라이언트 컴포넌트로 바꾼다.

폼 컴포넌트 안에서 useActionState 훅은 :

  • 두 argu를 가진다.(action, initialState)
  • 두 값을 반환한다. : state, formAction : 폼 제출 시 호출되는 폼 state와 함수

useActionState에 argu로서 createInvoice 를 넘기고
폼 액션 어트리뷰트에서 formAction을 호출한다.

initialState는 정의하는 모든 것이 될 수 있다.
이 경우에는, 두개의 빈 키: message, errors로 객체를 생성하고 actions.ts에서 State를 불러온다.

action.ts 에서, 검증을 위해 Zod를 쓸 수 있다.
FormSchema를 다음과 같이 고친다.

  • customerId : Zod는 String 타입을 예상하기에 해당 필드가 비면 이미 에러를 던진다. 하지만 친근한 메시지를 추가한다.
  • amount: 타입을 문자열 => 숫자로 바꾸기 때문에 문자열이 비면 디폴트로 0이 될 것이다. Zod에 항상 0이상을 원한다고 .gt( )를 통해 말한다.
  • status: Zod는 pending | paid 상태를 기대하기에 status 필드가 비면 이미 에러를 던진다. 친근한 메시지를 추가하자.

이제 createInvoice 액션이 두 개의 파라미터를 받도록 갱신하자 : prevState, formData

  • prevState: useActionState 훅에서 전달되는 state 포함. 필수적이다.

그 후, Zod parse( )를 safeParse()로 고친다. 이것은 success | error 필드를 포함하는 객체이다.

DB에 정보 전송 전에, 폼 필드가 검증됐는지를 조건문으로 확인하자

 if (!validatedFields.success) {
    return {
      errors: validatedFields.error.flatten().fieldErrors,
      message: 'Missing Fields. Failed to Create Invoice.',
    };
  }

만약 validatedFields가 성공적이 아니라면, Zod로 만든 에러 메시지를 반환하는 함수를 만난다.

마침내, 유효성을 try/catch 블럭 밖에서 구분지어 핸들링 했으므로, 어느 db 에러던 특정 메시지를 추가할 수 있다.

이제 에러를 폼 컴포넌트에 추가하자
create-form.tsx로 들어가 폼 state를 통해 에러에 접근할 수 있다.

특정 각 에러를 검사하는 삼항 연산자를 추가하자.

<div id="customer-error" aria-live="polite" aria-atomic="true">
        {state.errors?.customerId &&
          state.errors.customerId.map((error: string) => (
            <p className="mt-2 text-sm text-red-500" key={error}>
              {error}
            </p>
          ))}
      </div>
  • aria-describedby="customer-error": select 요소와 에러 메시지 컨테이너 간 관계 형성. id="customer-error"를 가진 컨테이너가 select 요소를 포함함을 표현. 스크린 리더는 이 구문을 사용자가 select 박스와 상호작용시 에러 공지 위해 사용
  • id="customer-error" : id 속성은 select 인풋에 대해 에러메시지를 가진 html 요소를 유니크하게 식별. aria-describedby의 관계 형성에 필수적
  • aria-live="polite": 스크린 리더는 사용자에게 갱신 시 div 내 에러를 알려줘야 한다. 컨텐트 변경 시, 스크린 리더는 이 변경을 알리지만 사용자는 방해하지 않을 것

Practice: Adding aria labels

폼이 비었을 때 밑에 에러 메시지를 나타내자

  • edit-form.tsx 에 useActionState를 추가해야함
  • updateInvoice를 Zod의 에러 핸들링으로 수정해야 함
  • 컴포넌트에 에러를 추가하고 aria 라벨을 추가해야 함 .

0개의 댓글