JUNSEOK
05 · 심화 FE·18분·4개 레슨

기타 꼭 알아야 할 것들

접근성, i18n, PWA, Service Worker.

학습 목표

  • WebAssembly가 무엇이고 언제 쓰는지 이해한다
  • Web Components와 Shadow DOM의 원리를 학습한다
  • 접근성(a11y)을 WAI-ARIA와 키보드·스크린리더 관점에서 이해한다
  • PWA / Service Worker / Offline 패턴을 익힌다
  • i18n, 타입 안전 라우팅 등 실무에서 자주 마주치는 보조 주제를 정리한다

0. 이 장은 왜 "기타"인가 — 초심자용

0-1. 각자 중요하지만 단독 챕터로 하기엔 작은 주제들

5단계의 다른 장들이 "모든 FE가 매일 쓰는" 주제(JS 엔진·번들러·렌더링)였다면, 이 장은 "특정 상황에서 결정적으로 필요한" 주제들을 모은 것.

주제언제 필요한가
WebAssemblyCPU 집약적 작업을 브라우저에서 할 때 (이미지 편집, 게임)
Web Components프레임워크 독립적인 UI 컴포넌트 배포 시
접근성(a11y)모든 서비스가 갖춰야 할 기본 (법적 의무인 경우도)
PWA오프라인 지원, 설치 가능한 웹앱 만들 때
i18n다국어 서비스
타입 안전 라우팅대형 프로젝트에서 URL 버그 방지

0-2. "기타"지만 이거 하나는 꼭 — 접근성

위 주제 중 a11y (접근성) 만큼은 "언젠가 필요한" 게 아니라 지금 필요한 기본기.

  • 한국: 공공기관 웹은 법적 의무 (장애인차별금지법)
  • 해외: 미국 ADA, 유럽 EAA — 소송 사례 존재
  • 키보드만 쓰는 사용자, 스크린리더 사용자 가 실제로 많음
  • 많은 접근성 개선이 일반 UX도 향상시킴 (focus 관리, 시맨틱 HTML)

"시간 남으면 하자" 로 미루면 나중에 전체 UI 뜯어고쳐야 함. 처음부터 신경 써야 한다.

0-3. WebAssembly의 의의

"JS가 아닌 언어로 브라우저에서 네이티브 급 속도 실행" 이 가능해진 것.

실제 사례:

  • Figma: C++ 로 작성된 렌더링 엔진을 Wasm으로 → 브라우저에서 데스크톱급 속도
  • Google Earth: Unity/C++ → Wasm
  • Adobe Photoshop Web: Wasm

"JS만 알면 된다" 시대에서 "C/Rust 같은 언어로 웹에 배포 가능" 시대로.

0-4. PWA와 Service Worker

Progressive Web App = 웹인데 네이티브 앱처럼:

  • 홈 화면에 설치 가능
  • 오프라인에서도 일부 기능 동작
  • 푸시 알림
  • 백그라운드 동기화

핵심은 Service Worker — 브라우저와 네트워크 사이에 끼어들어 요청을 가로채는 JS 스크립트. 2-9 캐싱에서 본 캐시 전략을 클라이언트에서 직접 구현 할 수 있게 해줌.

0-5. Web Components는 왜 안 뜨나

표준 기술로 컴포넌트를 만들 수 있는 방법. 그런데 실무에선 React/Vue가 압도적. 이유:

  • 상태 관리, 라우팅 등 생태계 부재
  • 서버 렌더링이 까다로움
  • 프레임워크만큼 DX가 매끄럽지 않음

하지만 디자인 시스템을 여러 프레임워크에 배포해야 하는 대형 조직(Adobe Spectrum, Shoelace 등)에서는 꾸준히 쓰임.

0-6. 이 장을 다 읽은 뒤 할 수 있는 것

  • "이 작업은 Wasm이 필요한가 JS로 충분한가" 판단
  • 접근성 기본기로 기존 컴포넌트 개선
  • PWA 기능을 점진적으로 추가
  • i18n 전략 설계 (URL 구조, 폴백, 번들 분할)

"꼭 몰라도 되는 것들" 이 아니라 "특정 상황에서 모르면 끝장나는 것들" 의 모음이다.


1. WebAssembly (Wasm)

1-1. 정의

브라우저에서 네이티브에 가까운 속도로 실행되는, 스택 머신 기반의 바이너리 명령 포맷.

  • C/C++/Rust/Go/AssemblyScript 등에서 컴파일
  • JS와 같은 가상 머신에서 실행되지만 파싱·최적화 단계가 거의 불필요 → 빠른 startup
  • 샌드박스에서 실행되어 안전

1-2. 언제 쓰는가

사용 사례예시
CPU 집약적 계산이미지/비디오 처리, 게임 엔진, 물리 시뮬레이션
기존 C/C++ 라이브러리 재사용FFmpeg, SQLite, OpenCV
보안이 중요한 로직암호화, DRM
결정론적 실행블록체인 스마트 컨트랙트

대표 사례: Figma (C++ → Wasm), Google Earth (Unity → Wasm), Photoshop Web (C++ → Wasm), AutoCAD Web.

1-3. 기본 사용법

// Rust → wasm-bindgen
import init, { fibonacci } from './pkg/my_lib.js';

await init();
console.log(fibonacci(40)); // Wasm 함수 호출

1-4. Wasm이 만능은 아니다

  • DOM 직접 접근 불가 (JS를 거쳐야 함) → 가벼운 UI 조작엔 오버헤드
  • JS ↔ Wasm 경계 비용 — 데이터 복사·변환 필요
  • 번들 크기가 클 수 있음
  • 디버깅 도구가 JS만큼 성숙하지 않음

⚠️ 간단한 숫자 계산 정도는 오히려 JS가 빠를 수 있다. 크로스 오버가 있는 경계에서만 이득. 프로파일링 필수.

1-5. WASI

브라우저 밖(서버, CLI)에서도 Wasm을 실행하는 표준 인터페이스. Cloudflare Workers, Fastly Compute@Edge 등이 Wasm을 서버리스 런타임으로 사용.


2. Web Components

2-1. 정의

브라우저 표준 기반의 커스텀 재사용 컴포넌트. 프레임워크 독립적. 3가지 기술로 구성.

  1. Custom Elements — 새 HTML 태그 정의
  2. Shadow DOM — 스타일·마크업 캡슐화
  3. HTML Templates<template>, <slot>

2-2. Custom Element

class MyButton extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        button { padding: 8px 16px; background: #06f; color: white; }
      </style>
      <button><slot></slot></button>
    `;
  }

  connectedCallback() { /* DOM에 부착되었을 때 */ }
  disconnectedCallback() { /* 제거되었을 때 */ }
  attributeChangedCallback(name, oldVal, newVal) { /* 속성 변경 */ }

  static get observedAttributes() { return ['disabled']; }
}

customElements.define('my-button', MyButton);
<my-button>Click</my-button>

2-3. Shadow DOM

캡슐화된 DOM 서브트리. 외부 CSS가 뚫고 들어오지 않고, 내부 스타일이 밖으로 새지 않는다.

this.attachShadow({ mode: 'open' }); // 외부 JS에서 shadowRoot 접근 가능
// 'closed' → 접근 불가 (실무에선 거의 안 씀)

Shadow DOM 경계:

  • 외부 querySelector는 Shadow 내부에 접근 불가 (의도적)
  • :host 선택자로 컴포넌트 자신에 스타일
  • ::part(button) → 외부에서 내부 일부에 스타일 허용
  • CSS 변수는 Shadow 경계를 넘는다 (테마 전파)

2-4. 왜 덜 쓰일까

  • 프레임워크(React/Vue)와 궁합이 미묘함 — form·이벤트 연계
  • SSR 지원 부족 (Declarative Shadow DOM으로 개선 중)
  • 접근성 구현이 수작업 (스크린리더가 Shadow 경계를 잘 타는지 테스트 필요)

2-5. 실무 활용

  • 디자인 시스템 배포 — 여러 프레임워크 환경에 뿌리는 공용 컴포넌트 (Ionic, Shoelace, Adobe Spectrum)
  • 마이크로 프론트엔드 — 서로 다른 팀의 앱을 안전하게 조합
  • 제품 임베드 — 고객 사이트에 삽입해도 스타일 충돌 없음 (위젯, 챗봇)

3. 접근성 (Accessibility, a11y)

3-1. 왜 중요한가

  • 법적 요구 — 미국 ADA, EU EAA, 한국 웹접근성 표준
  • 사용자 경험 — 키보드/스크린리더 사용자, 저시력, 색맹, 운동장애
  • SEO — 시맨틱 HTML은 검색엔진에도 이득

3-2. 시맨틱 HTML이 기본

<!-- ❌ -->
<div onclick="submit()">제출</div>

<!-- ✅ -->
<button type="submit">제출</button>

<button>기본적으로:

  • 키보드 포커스 가능
  • Enter/Space로 활성화
  • 스크린리더에 "버튼"으로 읽힘
  • 비활성 상태 기본 제공

3-3. WAI-ARIA

시맨틱 HTML로 표현할 수 없을 때 보조 속성.

<div role="button" tabindex="0" aria-pressed="false">토글</div>

<div role="alert" aria-live="assertive">저장 실패!</div>

<button aria-expanded="true" aria-controls="menu">메뉴</button>
<ul id="menu" hidden>...</ul>

ARIA의 철칙 (No ARIA is better than Bad ARIA):

  1. 가능하면 네이티브 HTML 요소 사용
  2. 네이티브 시맨틱을 덮어쓰지 말 것 (<h1 role="button"> 금지)
  3. 모든 인터랙티브 요소는 키보드로 조작 가능해야 함
  4. 포커스 가능한 요소에 접근 가능한 이름(aria-label 등) 제공
  5. 숨긴 콘텐츠(display: none)는 스크린리더도 못 읽음 — 의도한 대로인지 확인

3-4. 키보드 접근성

  • Tab: 포커스 이동 (tabindex 순서)
  • Shift+Tab: 역방향
  • Enter/Space: 활성화
  • Esc: 모달/메뉴 닫기
  • 화살표: 라디오·탭 패널·메뉴 내부 이동

Focus Trap (모달에서 포커스가 밖으로 나가지 않게):

// focus-trap 라이브러리 추천, 직접 구현 시 Tab/Shift+Tab 이벤트로 순환

⚠️ outline: none 금지. 포커스 링이 없으면 키보드 사용자가 "지금 어디 있는지" 알 수 없다. 제거하려면 대체 스타일 필수(:focus-visible).

button:focus { outline: none; } /* ❌ */
button:focus-visible { outline: 2px solid #06f; outline-offset: 2px; } /* ✅ */

3-5. 이미지 대체 텍스트

<img src="chart.png" alt="2024년 분기별 매출: 1분기 100, 2분기 120 ...">

<!-- 장식용 이미지 -->
<img src="decoration.svg" alt="" role="presentation">

3-6. 폼 레이블

<!-- ✅ -->
<label for="email">이메일</label>
<input id="email" type="email">

<!-- 또는 -->
<label>
  이메일
  <input type="email">
</label>

<!-- placeholder는 레이블이 아님 -->
<input placeholder="이메일"> <!-- ❌ 단독 사용 -->

3-7. 색 대비 (Color Contrast)

  • WCAG AA: 본문 4.5:1, 큰 글자 3:1
  • WCAG AAA: 본문 7:1, 큰 글자 4.5:1

하나만으로 정보를 전달하지 말 것 (색맹 고려). 아이콘·텍스트·패턴 병행.

3-8. 스크린리더 테스트

  • macOS: VoiceOver (Cmd+F5)
  • Windows: NVDA (무료), JAWS (상용)
  • iOS: VoiceOver
  • Android: TalkBack

3-9. 자동화 도구

  • axe DevTools — Chrome 확장, 80% 이슈 자동 감지
  • Lighthouse Accessibility
  • eslint-plugin-jsx-a11y — 컴파일 타임 검사

한계: 자동화로 잡을 수 있는 건 약 30-40%. 나머지는 수동 테스트(키보드 only 주행, 스크린리더 리딩)로 검증.


4. PWA (Progressive Web App)

4-1. 정의

웹을 앱처럼 만드는 기술 조합. 단일 기술이 아니라 패턴.

  • Installable (홈 화면 추가) — Web App Manifest
  • Offline — Service Worker
  • Reliable — 캐시 전략
  • Engageable — Push Notification (iOS도 16.4부터 지원)

4-2. Web App Manifest

{
  "name": "My App",
  "short_name": "MyApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#06f",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

4-3. Service Worker

브라우저와 네트워크 사이의 프로그래머블 프록시.

// sw.js
self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open('v1').then(cache => cache.addAll(['/', '/style.css', '/app.js']))
  );
});

self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(res => res || fetch(e.request))
  );
});

중요 특성:

  • HTTPS 필수 (localhost 예외)
  • 별도 스레드 (UI 블로킹 없음)
  • DOM 접근 불가
  • 라이프사이클: install → activate → fetch/message 대기
  • 업데이트skipWaiting + clients.claim 조합으로 제어

4-4. 캐시 전략

  1. Cache First — 캐시 우선, 없으면 네트워크. 정적 자산에.
  2. Network First — 네트워크 우선, 실패 시 캐시. API 응답에.
  3. Stale While Revalidate — 캐시 즉시 반환 + 백그라운드 업데이트. 반 동적 콘텐츠에.
  4. Network Only / Cache Only — 상황별

Workbox 라이브러리로 이 전략들을 쉽게 적용 가능.

4-5. iOS의 한계와 변화

  • 예전: 푸시/백그라운드/웹뷰 강제 등 제약
  • iOS 16.4+: Web Push Notification 지원, PWA 설치 시 홈 화면 추가
  • 여전히 일부 API는 Safari에서 제한적 — 실무에서는 크로스 브라우저 매트릭스로 검증

5. 국제화 (i18n) / 지역화 (l10n)

5-1. i18n의 실제 난이도

  • 단순 번역 != i18n — 복수형, 성별, 시간/숫자/통화 포맷, RTL(오른쪽→왼쪽), 날짜 체계
  • 텍스트 확장: 독일어는 영어보다 길어지기 쉬움 → 레이아웃 망가짐

5-2. 도구

  • ICU MessageFormat — 복수형/성별 등 표현
    {count, plural, =0 {No items} one {# item} other {# items}}
  • Intl API (네이티브):
    new Intl.NumberFormat('ko-KR', { style: 'currency', currency: 'KRW' }).format(12345);
    // "₩12,345"
    
    new Intl.DateTimeFormat('en-US', { dateStyle: 'long' }).format(new Date());
    // "April 19, 2026"
    
    new Intl.RelativeTimeFormat('ko').format(-1, 'day');
    // "1일 전"
    
    new Intl.PluralRules('ko').select(1); // "other"
    new Intl.PluralRules('en').select(1); // "one"
  • 라이브러리: react-intl (FormatJS), next-intl, i18next, vue-i18n

5-3. RTL 지원

아랍어·히브리어는 오른쪽→왼쪽.

<html dir="rtl" lang="ar">

CSS Logical Properties로 대응:

/* ❌ 방향 고정 */
.box { margin-left: 8px; }

/* ✅ 방향 독립 */
.box { margin-inline-start: 8px; }

6. 타입 안전 라우팅

6-1. 왜 중요한가

  • 라우트 URL과 params를 코드 전역에서 문자열로 쓰면 오타/누락/리팩토링 지옥
  • 타입 안전 라우팅 = URL 구조를 타입 시스템이 보증

6-2. 예시 (Next.js App Router, TanStack Router 등)

// TanStack Router 예
const userRoute = new Route({
  getParentRoute: () => rootRoute,
  path: '/users/$userId',
  validateSearch: z.object({ tab: z.enum(['posts', 'likes']).optional() }),
});

// 사용
<Link to="/users/$userId" params={{ userId: '1' }} search={{ tab: 'posts' }} />
// params, search 모두 타입 체크됨

6-3. 일반 패턴 (자체 구현 시)

const ROUTES = {
  user: (id: string) => `/users/${id}` as const,
  post: (id: string) => `/posts/${id}` as const,
} as const;

router.push(ROUTES.user('123')); // 문자열 리터럴로 안전

7. 기타 알아두면 좋은 주제

7-1. CSS Container Queries

부모 크기에 반응하는 스타일 (미디어 쿼리는 뷰포트 기준).

.card { container-type: inline-size; }

@container (min-width: 400px) {
  .card-body { display: grid; grid-template-columns: 1fr 2fr; }
}

7-2. View Transitions API

SPA 전환을 네이티브 애니메이션으로.

document.startViewTransition(() => {
  updateDOM();
});

페이지 간 "같은" 요소에 view-transition-name을 부여하면 morph 애니메이션.

7-3. Popover API

<dialog>와 유사한 네이티브 팝오버.

<button popovertarget="menu">열기</button>
<div id="menu" popover>...</div>

포커스 트랩·light dismiss·ESC 닫기가 기본 제공.

7-4. WebRTC / WebSocket / Server-Sent Events

기술용도특성
WebSocket양방향 실시간채팅, 알림
SSE서버→클라 스트림단방향, HTTP 기반, 재접속 쉬움, AI 스트리밍 응답
WebRTCP2P 미디어화상통화, 파일 전송

7-5. File System Access API

사용자 허락 받아 로컬 파일 읽기/쓰기.

const [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();

VS Code Web, Figma 같은 도구가 사용.

7-6. Web Crypto API

암호화/해시를 네이티브로.

const encoder = new TextEncoder();
const data = encoder.encode('hello');
const hash = await crypto.subtle.digest('SHA-256', data);

7-7. Feature Detection

라이브러리 추가 전에 네이티브 API가 있는지 확인:

if ('share' in navigator) { await navigator.share({...}); }
if (CSS.supports('container-type: inline-size')) { /* ... */ }

8. 실무 체크리스트

  • Wasm이 필요한 영역을 프로파일링으로 식별했는가 (JS로 충분한 걸 억지로 Wasm 쓰지 않는가)
  • 키보드만으로 전체 페이지 주요 기능을 사용할 수 있는가
  • :focus-visible이 모든 인터랙티브 요소에 적용되는가
  • 색 대비 WCAG AA 이상인가 (axe로 검사)
  • 폼의 모든 입력에 연결된 <label>이 있는가
  • Service Worker가 배포 시 제대로 갱신되는가 (skipWaiting 정책)
  • Intl API로 날짜/숫자/통화 포맷팅하고 있는가 (문자열 직접 조립 금지)
  • RTL 대응이 필요한 언어를 지원한다면 Logical Properties를 쓰는가
  • 라우트 URL을 타입 안전하게 관리하는가

9. 연습 문제

Q1. WebAssembly가 브라우저에서 JS보다 빠를 수 있는 이유와, 실제로 느릴 수 있는 상황을 각각 설명하라.

정답

빠른 이유:

  • 바이너리 포맷이라 파싱·구문 분석이 거의 없음 → startup 빠름
  • 정적 타입 + 미리 정의된 명령 집합 → JIT 최적화 초기부터 가능, deopt 위험 적음
  • 메모리 레이아웃을 제어할 수 있어 SIMD/멀티스레드(Worker + SharedArrayBuffer) 활용 용이

느릴 수 있는 상황:

  • JS ↔ Wasm 경계 비용 — 데이터 복사/변환이 잦으면 오히려 손해
  • DOM 접근은 JS 브릿지 필요 → UI 조작 위주라면 메리트 없음
  • 작은 계산은 V8 JIT 최적화된 JS가 비등하거나 빠름

Q2. Shadow DOM의 캡슐화가 제공하는 실용적 이점을 2가지 설명하라.

정답
  1. 스타일 충돌 방지: 외부 CSS가 Shadow 내부에 영향을 주지 않고, 내부 스타일도 밖으로 새지 않는다. 여러 팀이 만든 컴포넌트를 한 페이지에 조합할 때 CSS 충돌을 걱정할 필요가 없다. 마이크로프론트엔드·임베드 위젯에 매우 유용.
  2. 마크업 캡슐화: 외부 document.querySelector가 내부 구조에 의존하지 못하게 해, 내부 구현을 자유롭게 리팩토링 가능. 공용 컴포넌트 라이브러리의 API 안정성 확보.

Q3. 다음 코드의 접근성 문제를 모두 지적하라.

<div onclick="submit()">제출</div>
<input placeholder="이메일">
<img src="chart.png">
정답
  1. <div onclick> 대신 <button> 필요: 키보드 포커스 안 됨, Enter/Space 안 됨, 스크린리더에 버튼으로 안 읽힘.
  2. <input>에 레이블 없음: placeholder는 레이블 아님. 포커스되면 사라지고 스크린리더가 name을 못 찾음. <label for> 또는 감싼 <label> 필요.
  3. <img>에 alt 없음: 스크린리더가 파일명을 읽거나 무시. 내용이면 alt="2024년 분기별 매출 차트", 장식이면 alt="".

Q4. Service Worker의 "Cache First"와 "Network First" 전략을 각각 어떤 자원에 쓰는 게 적절한지 설명하라.

정답
  • Cache First: 변경이 거의 없는 정적 자산(해시 붙은 JS/CSS 번들, 폰트, 아이콘). 오프라인·빠른 로딩이 중요하고 stale 데이터 걱정 없음.
  • Network First: 실시간성이 중요한 API 응답(뉴스, 피드, 가격). 네트워크에서 최신 데이터를 받고, 오프라인일 때만 캐시로 폴백.

추가: 반 동적 콘텐츠(블로그 글, 사용자 프로필)에는 Stale While Revalidate가 적합.

Q5. Intl.NumberFormatIntl.DateTimeFormat을 써서 다음을 출력하는 코드를 작성하라.

  • 12345.67을 미국 달러로 (예: $12,345.67)
  • 현재 날짜를 한국어 긴 포맷으로 (예: 2026년 4월 19일 일요일)
정답
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(12345.67);
// "$12,345.67"

new Intl.DateTimeFormat('ko-KR', { dateStyle: 'full' }).format(new Date());
// "2026년 4월 19일 일요일"

Q6. WebSocket과 SSE(Server-Sent Events)의 차이를 설명하고, AI 챗봇의 "답변 스트리밍"에는 어느 것이 더 적합한지 이유를 들어라.

정답
  • WebSocket: 양방향, 바이너리/텍스트, 별도 프로토콜(ws://), HTTP/2 multiplex 못 탐.
  • SSE: 단방향(서버→클라), 텍스트 전용, HTTP 기반, 자동 재접속 내장, 방화벽/프록시 친화적.

AI 챗봇은 클라→서버는 한 번 요청이면 충분하고(프롬프트 전송), 서버→클라는 토큰 스트림을 길게 흘리면 된다. 단방향 스트림에 SSE가 더 단순·안정적이며 구현 복잡도가 낮다. 실제로 OpenAI, Anthropic 등 대부분의 LLM API가 SSE 기반 스트리밍을 사용한다.

Q7. PWA에서 Service Worker를 배포했는데, 사용자 브라우저에 구버전 SW가 계속 살아있어 새 코드가 반영되지 않는다. 어떻게 해결하는가?

정답

Service Worker는 기본적으로 기존 탭이 모두 닫힌 후 다음 방문에서 활성화된다. 강제 업데이트는:

  1. install 이벤트에서 self.skipWaiting() 호출 → 대기 없이 즉시 활성화
  2. activate 이벤트에서 self.clients.claim() → 열려 있는 모든 페이지를 새 SW가 즉시 제어
  3. 클라이언트 코드에서 registration.waiting을 감지해 사용자에게 "새 버전이 있습니다, 새로고침" 배너 노출 (Workbox workbox-window 제공)
  4. 캐시 이름에 버전 문자열 포함 → activate에서 구 캐시 삭제

주의: 무조건 skipWaiting페이지 간 자산 버전 불일치를 만들 수 있어, 상황에 따라 사용자 동의 후 갱신이 안전하다.


10. 정리

  • WebAssembly는 CPU 집약 작업과 레거시 C/C++ 재사용에 강하지만, DOM·JS 경계 비용을 고려해야 한다.
  • Web Components는 프레임워크 독립적 캡슐화 단위로, 디자인 시스템·마이크로 프론트엔드에 유용하다.
  • 접근성은 시맨틱 HTML이 기본. ARIA는 보조. 키보드/스크린리더 테스트가 필수다.
  • PWA는 Manifest + Service Worker + 캐시 전략의 조합이다.
  • Intl API로 i18n의 숫자·날짜·복수형을 표준적으로 처리한다.
  • Container Queries, View Transitions, Popover, File System Access 등 최신 플랫폼 API는 라이브러리로 해오던 일을 네이티브로 대체하고 있다. 항상 Feature Detection 먼저.

← 5-7. 프레임워크 내부 동작 | 6-1. 디자인 패턴 →

진도 체크시작 전
NEXT

이 단계의 마지막 챕터예요

다음 단계는 아직 준비 중이에요. 1단계 챕터를 복습해보세요.

단계 목록으로 돌아가기