C.W.K.
Stream
Lesson 06 of 07 · published

Lighthouse + 접근성 바닥

~12 min · lighthouse, accessibility, a11y, performance

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
Lighthouse 가 넷 측정: 성능, 접근성, best practice, SEO. Vite SPA 엔 접근성이 무시할 변명 가장 적은 거 — 대부분 올바른 HTML 사용의 규율.

Lighthouse 돌리기

Chrome DevTools → Lighthouse 탭 → 카테고리 (performance, accessibility, best practices, SEO) 선택 → 프로덕션 모드 (dev server 아닌 npm run preview 로 시작) 에 돌림. 카테고리당 점수 + 문서 링크 가진 이슈 리스트 받음.

성능 점수

메트릭 구동: LCP (largest contentful paint), TBT (total blocking time), CLS (cumulative layout shift). SPA 엔 보통 LCP 와 TBT 가 dominant. 승리: code splitting (Track 8 lesson 2), below-the-fold 이미지 lazy-load, font-display: swap, <head> 의 render-blocking JS 제거.

접근성 바닥 (모든 SPA 가 필요한 것)

  1. Semantic HTML<div onClick> 아닌 <button>, 구조에 <nav> / <main> / <article>, 모든 input 에 <label>.
  2. 키보드 네비게이션 — 모든 interactive element 가 Tab 으로 도달 가능, 보이는 focus 링 (대체 없이 outline: none 마).
  3. 색 대비 — 텍스트가 WCAG AA 통과 (보통 텍스트 4.5:1, 큰 텍스트 3:1). Lighthouse 가 특정 element 와 함께 실패 플래그.
  4. HTML 부족할 때 ARIA — 아이콘 전용 버튼에 aria-label, 에러 toast 에 role="alert", 스트리밍 컨텐츠에 aria-live.
  5. 페이지 타이틀 & meta — 모든 라우트가 document.title 설정 (또는 react-helmet-async 같은 라이브러리).

Best-practices 바닥

프로덕션에 HTTPS. Console 에러 없음. Deprecated API 없음. 에러 추적용 source map 가능. 프로덕션에 CSP 헤더 (Content Security Policy). Lighthouse 가 놓친 거 플래그.

접근성이 디폴트, feature 아님. 처음부터 semantic HTML 작성 + 키보드 네비게이션 존중하면 시도 없이 Lighthouse 의 대부분 a11y 체크 통과. '나중에 추가할 거' 로 취급하면 몇 주 동안 div 를 button 으로 리팩토링. 처음에 올바르게 하는 게 싸.

Code

Lighthouse audit 플래그하는 흔한 a11y 고침·tsx
// ANTI — div 를 button 으로 (키보드 없음, role 없음, announcement 없음)
<div onClick={save} className="p-2 bg-brand">Save</div>

// CORRECT — semantic button (키보드 + 스크린 리더 + focus 링)
<button
  onClick={save}
  className="p-2 bg-brand focus-visible:ring-2 focus-visible:ring-fg"
>
  Save
</button>

// ANTI — 설명 없는 이미지 아이콘
<img src="/trash.svg" onClick={delete} />

// CORRECT — 접근 가능한 아이콘 버튼
<button onClick={delete} aria-label="Delete conversation">
  <img src="/trash.svg" alt="" />
  {/* 버튼 label 이 의미 담으니까 빈 alt */}
</button>

// ANTI — label 없는 form input
<input name="email" />

// CORRECT — input 과 연관된 label
<label className="block">
  <span>Email</span>
  <input name="email" type="email" required />
</label>
라우트별 페이지 타이틀 설정·tsx
import { useEffect } from "react";

function useDocumentTitle(title: string) {
  useEffect(() => {
    const previous = document.title;
    document.title = title;
    return () => { document.title = previous; };
  }, [title]);
}

function SettingsPage() {
  useDocumentTitle("Settings — My App");
  return <h1>Settings</h1>;
}

External links

Exercise

프로젝트 빌드 (npm run build && npm run preview). Chrome DevTools → Lighthouse → 프리뷰 URL 에 돌림. 상위 접근성 이슈 셋 고침. 다시 돌림. 90+ 칠 때까지 반복. 참고: 100 필수 아님; 90+ 가 본인이 기본 처리했다 보여줌.
Hint
흔한 첫 승리: 모든 input 에 <label> 추가, 클릭 가능한 div 를 button 으로 교체, 아이콘 전용 버튼에 aria-label 추가, body 텍스트가 contrast 통과 보장 (Tailwind 의 text-fg on bg-bg 통과되게 튠).

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.