C.W.K.
Stream
Lesson 02 of 03 · published

JavaScript Bridge: 행동이 시작되는 자리

~10 min · javascript, behavior, progressive-enhancement

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"CSS 가 옛날보다 훨씬 많이 함. JavaScript 가 계산, 네트워크, 저장, 동적 상태 시작하는 자리에서 시작."

모던 경계

CSS/JavaScript 선이 지난 5 년 동안 CSS 쪽으로 극적으로 이동. 2020 년에 JS 필요했던 게 2026 년에 순수 CSS:

  • 테마 토글 — prefers-color-scheme + custom property.
  • 검증 기반 show/hide — :has(:invalid).
  • Sticky header — position: sticky.
  • 부드러운 스크롤 — scroll-behavior: smooth.
  • 반응형 컨테이너 적응 — container query.
  • Native dialog 열기/닫기 — <dialog> + showModal()/close() (열기/닫기 자체는 JS 한 줄, 하지만 상태, focus trap, Esc 닫기는 native).
  • 폼 검증 — HTML5 속성 + :invalid.

2026 년에 여전히 JavaScript 의 일:

  • DOM 속성으로 surface 안 되는 상태. 사용자의 쇼핑 카트, SPA 의 현재 페이지, 다중 선택의 선택된 항목. 상태가 어딘가 살아야 함; 보통 JS 에.
  • 네트워크 요청. 데이터 fetch, AJAX 통한 폼 제출, WebSocket, Server-Sent Event.
  • 계산. 리스트 정렬, 표 필터링, JSON 파싱, 요약 계산, 알고리즘 실행.
  • 저장. localStorage, IndexedDB, 쿠키. 사용자 데이터 읽기/쓰기.
  • 스케줄링. Timeout, interval, 입력 debouncing, polling.
  • On-the-fly 콘텐츠 생성. 데이터에서 DOM 노드 만들기, React/Vue/Svelte component 렌더.
  • OS/브라우저 API 통합. Geolocation, 알림, 클립보드, 파일 시스템.
  • 물리나 스크롤 위치로 구동되는 애니메이션. Spring 애니메이션, Web Animations API 또는 서드파티 라이브러리 통한 scrubbing 애니메이션.

점진적 향상: JS 없이 작동하는 거에서 시작

웹의 가장 회복력 있는 디자인 패턴: JavaScript 0 으로 작동하는 페이지 짓고, JS 가 가치 더하는 자리에 향상.

  1. 서버 렌더된 HTML — JS 로드 실패 (느린 네트워크, ad blocker, JS 에러) 해도 페이지가 맞게 렌더.
  2. Semantic, 접근 가능 markup — screen reader, 검색 엔진, reader mode 에서 작동.
  3. 표현과 반응 상태에 CSS — 맞게 보임, 사용자 선호에 응답.
  4. JavaScript 가 동적성 추가 — 즉시 피드백, 전체 페이지 reload 없음, 풍부한 인터랙션.

모던 framework (Next.js, Remix, SvelteKit) 가 점진적 향상 쉽게: markup 서버 렌더, 클라이언트 사이드 hydrate, 하지만 모든 단계에서 작동.

최소-JS 훈련

모든 인터랙션에 물어 봐: CSS 가 할 수 있나? 종종 답이 yes:

  • 클릭에 열리는 드롭다운 → <details>/<summary>, native HTML, JS 0.
  • 탭 컨트롤 → radio 버튼과 :checked + sibling selector 가진 CSS-only. (접근성 위해 JS 가 더 낫지만 CSS-only 도 작동.)
  • Modal → native <dialog> element, showModal() 위한 JS 한 줄.
  • 아코디언 → custom 스타일링 가진 <details>.
  • 앵커로 부드러운 스크롤 → scroll-behavior: smooth + <a href="#section">.

JS 적게 = 빠른 페이지, 버그 적게, 더 접근 가능. 원칙이 "절대 JavaScript 안 됨" 이 아냐; "문제 해결하는 가장 단순 layer 써".

JavaScript 진짜로 필요할 때 뭐 잡아?

2026 년 대부분 앱에:

  • Vanilla JS 가 작은 인터랙션 (클립보드 복사, 테마 토글, 폼 헬퍼).
  • React 가 중-대 인터랙티브 UI (지배적 선택; React Quest 가 이거 커버).
  • Next.js 가 라우팅, 데이터 fetching, SSR 가진 풀-스택 웹 앱 (Next.js Quest).
  • Web Components 가 framework-agnostic 재사용 component.
  • Svelte / Vue / Solid 가 React 대안 — 각자 trade-off; React 의 생태계가 가장 universal.
구조에 HTML, 표현에 CSS, 행동에 JavaScript. 경계가 흐려져 — 모던 CSS 가 반응 feature 가짐, HTML 이 폼 검증 가짐 — 하지만 원칙 유지. 일 하는 가장 단순 layer 잡아. JavaScript 가 capability 에서 이김; HTML/CSS 가 회복력에서 이김.

피파의 노트

피파의 WebUI 가 React 앱, 하지만 UI 의 놀라운 양이 HTML + CSS. 채팅 메시지 렌더링이 React (데이터 → JSX → DOM), 하지만 모든 메시지 풍선이 안에 HTML5 element 가진 semantic <article>. 테마 토글이 <html>data-theme 설정하는 JS 한 줄 — 모든 시각 변화가 var() 읽는 CSS. Brain selector 메뉴가 custom 드롭다운 (키보드 네비와 검색 필요해서), 하지만 아바타 감정 picker 가 JS 없는 그냥 <details>/<summary>. 맞는 도구, layer by layer.

Code

라이브러리 필요했던 HTML element·html
<!-- Native disclosure: 아코디언용 <details>, JS 0 -->
<details>
  <summary>Track 1 에 뭐 있어?</summary>
  <p>HTML foundation — semantic markup, document 구조,
     텍스트 element, link 과 이미지, 그리고 왜 <code>&lt;div&gt;</code>
     가 last resort 인지.</p>
</details>

<!-- 열기 위한 JS 한 줄 가진 native dialog -->
<button onclick="document.getElementById('confirm').showModal()">
  Delete account
</button>
<dialog id="confirm">
  <p>확실해? 되돌릴 수 없어.</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm" autofocus>Delete</button>
  </form>
</dialog>
JS 가 제 값 하는 자리·javascript
// JavaScript 가 빛나는 자리: 상태, 네트워크, 계산

// 1. 테마 토글 — 최소 JS, 최대 CSS
function toggleTheme() {
  const isDark = document.documentElement.dataset.theme === 'dark';
  document.documentElement.dataset.theme = isDark ? 'light' : 'dark';
  localStorage.setItem('theme', isDark ? 'light' : 'dark');
}
// 모든 시각 변화가 [data-theme="dark"] / [data-theme="light"]
// 에서 상속하는 CSS custom property 통해 발생.

// 2. Fetch + 데이터 렌더
async function loadConversations() {
  const res = await fetch('/api/conversations');
  const conversations = await res.json();
  document.getElementById('list').innerHTML = conversations
    .map(c => `<li><a href="/c/${c.id}">${c.title}</a></li>`)
    .join('');
}

// 3. 정렬 / 필터링
function sortByDate(items) {
  return [...items].sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
}

// 4. 저장 / 선호
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.dataset.theme = saved;
12 줄로 점진적 향상·html
<!-- 점진적 향상 — JS 없이 작동, JS 있으면 더 좋음 -->

<!-- 폼이 JS 없이 작동: 전체 페이지 네비게이션 수행. -->
<form action="/api/search" method="get">
  <label for="q">Search</label>
  <input id="q" name="q" type="search" required />
  <button type="submit">Search</button>
</form>

<script>
  // JS 향상: 인터셉트하고 즉시 결과에 fetch 사용
  document.querySelector('form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const q = new FormData(e.target).get('q');
    const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`);
    const html = await res.text();
    document.getElementById('results').innerHTML = html;
  });
</script>

<div id="results"></div>

External links

Exercise

JavaScript 쓰는 기존 페이지 가져와서 HTML/CSS 대신 가능한 행동 셋 식별: custom 아코디언 (→ <details>), custom modal (→ <dialog>), custom 테마 토글 (→ CSS custom property + JS 한 줄). 각자 교체. JavaScript 줄 수 감소 측정. 사용자 향 행동 동일한지 확인.
Hint
변환하기 너무 복잡하게 느껴지면 그게 종종 옳은 신호 — JS 로 유지. 목표가 '없는 JavaScript' 가 아냐; '원하는 행동에 최소 JavaScript'. 아코디언, modal, 테마 토글, 부드러운 스크롤: 플랫폼이 처리.

Progress

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

댓글 0

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

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