📰 DataTables + Scrollytelling

인터랙티브 테이블 + 스크롤 기반 데이터 기사 · 코드 그대로 복사 → 데이터만 교체

⓪ 시작하기
① DataTables
② Intersection Observer
③ Scrollama 인라인 ⭐
④ 완성 기사 (새 창)
⓪ 데이터 저널리즘 4가지 도구 — 어떤 기능을 어디에 쓰나?
이 페이지는 데이터 기사·인터랙티브 콘텐츠를 만드는 데 자주 쓰이는 4가지 패턴을 모았습니다. 스크롤리텔링은 ②→③→④ 순서로 발전합니다. ②가 원리, ③이 라이브러리, ④가 완성 기사.
  1. ① DataTables — 정렬·검색·페이지네이션 테이블 (jQuery)
  2. ② Intersection Observer — 스크롤 감지 브라우저 기본 API
  3. ③ Scrollama — IO를 한 줄로 감싼 스크롤리텔링 라이브러리
  4. ④ 완성 기사 — 모두 합쳐 만드는 풀스크린 데이터 기사
🧰 4가지 도구 — 라이브러리와 사용법
jQuery + DataTables · ① jQuery  $('#table').DataTable({...})
Intersection Observer · ② 브라우저 기본  new IntersectionObserver(callback, options)
Scrollama + Chart.js · ③④ Scrollama  scrollama().setup({step}).onStepEnter(...)
💡 학생 미션: ① 표 → 자기 데이터로 행만 교체. ③ 단계 → 자기 분석 5단계로 텍스트와 차트 교체. ④ → 새 창 코드를 통째로 복사해 별도 .html 저장 후 자기 기사로 변환.
// ── 스크롤리텔링의 핵심 패턴 ──
// 1) 차트 영역(왼쪽)을 sticky로 화면에 고정
// 2) 텍스트(오른쪽)를 스크롤하며 단계 진입 시 차트만 갱신

const scroller = scrollama();
scroller
    .setup({ step: '.scroll-step', offset: 0.5 })   // step이 화면 50% 지점에 오면 발동
    .onStepEnter(({ element, index }) => {
        document.querySelectorAll('.scroll-step')
                .forEach(s => s.classList.remove('is-active'));
        element.classList.add('is-active');
        showStage(index);                              // 차트 전환 함수
    });
window.addEventListener('resize', scroller.resize);
① DataTables.js — 정렬·검색·페이지 표 jQuery + DataTables

🎯 이 탭에서 배우는 것

"수십 행짜리 표"를 독자가 직접 정렬·검색·페이지 이동할 수 있게 만드는 방법. 기존 HTML <table>에 한 줄만 적용하면 즉시 인터랙티브 테이블로 변신합니다.

📚 DataTables란?
🎯 목적 · 차트로 보기엔 항목이 너무 많을 때(시도 17개·차종 30개) "독자가 직접 탐색하는 표"로.
✨ 특징 · 헤더 클릭 → 정렬, 검색창 → 즉시 필터, 하단 → 페이지 이동.
🚦 사례
  • 17개 시도 사고 통계 — 사망률 정렬로 위험 지역 찾기
  • 1000건 사망사고 원본 → 검색으로 특정 운전자 추적
⚠️ 주의 · 만 행 넘으면 클라이언트 처리 느려짐 → 서버 사이드 처리로 전환.
📝 핵심: HTML <table id="..."> 그대로 두고 $('#id').DataTable(...) 한 줄. 행은 <tbody>에 평범하게.
// ① CDN: jQuery + DataTables (CSS도 추가)
// <link rel="stylesheet" href="https://cdn.datatables.net/1.13.7/css/jquery.dataTables.min.css"/>
// <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
// <script src="https://cdn.datatables.net/1.13.7/js/jquery.dataTables.min.js"></script>

// ② 일반 HTML 테이블 — 행만 자기 데이터로 교체
// <table id="myTable">
//     <thead><tr><th>시도</th><th>사고건수</th><th>사망률</th></tr></thead>
//     <tbody>
//         <tr><td>서울</td><td>34215</td><td>0.74</td></tr>
//         ...
//     </tbody>
// </table>

// ③ JS 한 줄로 활성화
$(document).ready(function() {
    $('#myTable').DataTable({
        language: {                            // 한글화
            search: '검색:',
            lengthMenu: '_MENU_ 개씩 보기',
            info: '_START_–_END_ / _TOTAL_ 개',
            paginate: { previous: '이전', next: '다음' }
        },
        order: [[1, 'desc']],                  // 초기 정렬: 1번 열, 내림차순
        pageLength: 7                          // 페이지당 행 수
    });
});

// ── fetch로 데이터 동적 로드 (실무) ──
// fetch('data.json').then(r => r.json()).then(rows => {
//     const tbody = document.querySelector('#myTable tbody');
//     tbody.innerHTML = rows.map(r =>
//         `<tr><td>${r.시도}</td><td>${r.사고건수}</td><td>${r.사망률}</td></tr>`).join('');
//     $('#myTable').DataTable({...});
// });

🔍 해보기: 헤더 클릭으로 정렬, 검색창에 시도명 입력.

시도사고건수사망자부상자사망률(%)10만명당
서울34215252498210.73635.2
경기58432681872341.16543.8
부산15621178223411.13947.2
대구984398145210.99641.3
인천14523141212340.97148.1
광주723467108210.92650.3
대전812383123411.02255.7
울산52345278230.99445.2
세종18931828340.95152.4
강원7891156102341.97751.6
충북8234143112341.73752.3
충남11234198152341.76353.5
전북9123145122341.59051.2
전남10234187132341.82856.1
경북13234243172341.83650.8
경남17234254242341.47451.7
제주48235268231.07872.1
② Intersection Observer — "스크롤 감지"의 원리 브라우저 기본 API

🎯 이 탭에서 배우는 것

스크롤 기반 시각화의 기초 원리를 직접 눈으로 확인. 이 기술 위에 ③ Scrollama가 만들어졌고, ④ 완성 기사가 동작합니다.

📚 Intersection Observer란?
🎯 목적 · 화면 어딘가에 어떤 요소가 들어왔는지 감지하는 브라우저 기본 API. 옛날 scroll 이벤트보다 성능이 좋아 표준화됨.
✨ 특징 · "이 요소가 50% 이상 보이면 콜백" 같은 식. 무한 스크롤·이미지 지연로드·스크롤 애니메이션의 기반.
🚦 사례
  • 아래 7개 박스를 스크롤하면 보이는 박스가 보라색으로 활성
  • 독자가 통계 섹션에 도달하면 차트 자동 그리기
  • 긴 인포그래픽의 어느 위치인지 우측 진행바에 표시
⚠️ 주의 · 컨테이너(root)가 자체 스크롤되면 root: containerEl 명시 필요.
📝 3요소: ① root (감지 기준 컨테이너, null=뷰포트) ② threshold (몇 % 보일 때 발동) ③ 콜백.
// ① CDN 불필요 ─── 브라우저 기본 API
// ② 감시할 요소들과 컨테이너 준비
// <div id="container">
//     <div class="item">...</div> (여러 개)
// </div>

// ③ Observer 생성
const observer = new IntersectionObserver(
    entries => {                                 // 콜백: entries는 변화 있는 요소들
        entries.forEach(e => {
            e.target.classList.toggle('visible', e.isIntersecting);
            // e.isIntersecting === true → 화면에 들어옴
        });
    },
    {
        root: document.getElementById('container'),  // null이면 브라우저 뷰포트
        threshold: 0.5                            // 50% 이상 보일 때 발동
    }
);

// ④ 감시할 요소들 등록
document.querySelectorAll('.item').forEach(el => observer.observe(el));

▼ 아래 박스를 스크롤해 보세요. 화면 안에 들어오면 보라색으로 변합니다.

① 첫 섹션 — 스크롤하면 활성화
② 두 번째 — 화면에 들어오면 보라색
③ 세 번째 — IntersectionObserver 감지
④ 네 번째 — threshold: 50%
⑤ 다섯 번째 — Scrollama의 기반 기술
⑥ 여섯 번째 — 추가 확인용
⑦ 일곱 번째 — 끝
스크롤하면 감지 상태가 표시됩니다
③ ⭐ Scrollama 인라인 — 스크롤로 차트가 바뀐다 Scrollama + Chart.js

🎯 이 탭에서 배우는 것

NYT·워싱턴포스트·SBS·한겨레 등이 쓰는 "스크롤리텔링"의 핵심 패턴.
왼쪽 차트를 화면에 고정(sticky)해 두고, 오른쪽 텍스트를 스크롤하면 단계마다 차트가 바뀝니다.

📚 Scrollama 패턴이란?
🎯 목적 · "독자 스크롤 = 이야기 + 시각화 동시 진행". 데이터 저널리즘 표준 형식.
✨ 특징 · IntersectionObserver를 한 줄로 감싼 라이브러리. onStepEnter 콜백에서 차트만 바꿈.
🚦 사례 — 5단계 시나리오
  • SECTION 01: 8년 추세 선그래프
  • SECTION 02: 특정 한 해만 빨간 막대로 강조
  • SECTION 03: 도넛으로 사고유형 비율
  • SECTION 04: 시도별 가로막대로 지역 격차
  • SECTION 05: 거대한 숫자 한 개로 핵심 메시지
⚠️ 주의 · 작은 컨테이너 안에서 동작시키면 IO가 어색해짐 → 페이지 자체를 스크롤하는 게 가장 안정적.
📝 패턴: HTML에 sticky 차트 영역 + 여러 step div 만들고, JS에서 scrollama().setup({step}) + onStepEnter에서 차트 갱신 함수 호출.
// ① CDN: Scrollama + Chart.js
// <script src="https://cdn.jsdelivr.net/npm/scrollama@3.2.0/build/scrollama.min.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>

// ② HTML 구조 ─── sticky 차트 + 텍스트 step들
// <div class="scroll-demo">
//     <div class="sticky-stage"><canvas id="stage-canvas"></canvas></div>
//     <div class="scroll-steps">
//         <div class="scroll-step">...텍스트 1...</div>
//         <div class="scroll-step">...텍스트 2...</div>
//     </div>
// </div>

// ③ 데이터 + 단계별 차트 빌더 함수
const years    = [2016,2017,2018,2019,2020,2021,2022,2023];
const 사고건수 = [220917,216335,217148,229600,209654,203130,196836,191418];
const 유형     = ['차대차','차대사람','차량단독','기타'];
const 유형건수 = [142350, 42580, 6380, 108];

let chart = null;
function showStage(idx) {
    if (chart) chart.destroy();                  // 매번 destroy → 재생성
    const canvas = document.getElementById('stage-canvas');

    if (idx === 0) {                              // 단계별로 다른 차트
        chart = new Chart(canvas, { type:'line', data:{ labels: years,
            datasets:[{ label:'사고건수', data: 사고건수, borderColor:'#7c3aed' }] } });
    } else if (idx === 1) {
        const minIdx = 사고건수.indexOf(Math.min(...사고건수.slice(3,6)));
        const colors = 사고건수.map((_,i) => i===minIdx ? '#ef4444' : 'rgba(124,58,237,0.35)');
        chart = new Chart(canvas, { type:'bar',
            data:{ labels: years, datasets:[{ data: 사고건수, backgroundColor: colors }] } });
    } else if (idx === 2) {
        chart = new Chart(canvas, { type:'doughnut',
            data:{ labels: 유형, datasets:[{ data: 유형건수 }] } });
    }
    // ... 단계만큼 추가
}
showStage(0);                                     // 첫 진입 시

// ④ Scrollama 연결 ─── step이 화면 50% 지점에 오면 발동
const scroller = scrollama();
scroller
    .setup({ step: '.scroll-step', offset: 0.5 })
    .onStepEnter(({ element, index }) => {
        document.querySelectorAll('.scroll-step')
                .forEach(s => s.classList.remove('is-active'));
        element.classList.add('is-active');
        showStage(index);                          // 차트 전환
    });
window.addEventListener('resize', scroller.resize);
📌 아래 영역을 천천히 위/아래로 스크롤해 보세요. 왼쪽 차트가 단계마다 바뀝니다.
SECTION 01 · 연도별 추이
SECTION 01

📊 연도별 사고 추이

한 해 정점(약 23만 건)을 찍은 뒤 꾸준히 감소해 최근 19만 건대까지 떨어졌습니다.

SECTION 02

📉 코로나19의 충격

특정 한 해는 이동량 감소로 직전 해 대비 약 9% 급감했습니다(코로나19 시기).

SECTION 03

🥧 사고유형 분석

전체 사고의 74%가 차대차 사고입니다. 그러나 사망 비율은 차대사람이 35%로 가장 높습니다.

SECTION 04

🗺️ 시도별 분포

경기·서울·경남·부산 4개 시도가 전체 사고의 절반 이상을 차지합니다.

SECTION 05

⚠️ 핵심 수치

여전히 하루 평균 7명이 교통사고로 목숨을 잃고 있습니다.

④ ⭐ 완성 기사 — 풀스크린 데이터 저널리즘 결과물

🎯 이 탭에서 배우는 것

지금까지 배운 모든 것(차트 + 스크롤 감지 + sticky 레이아웃)을 합쳐 만든 실제 신문 기사 형태의 5섹션 인터랙티브 기사.
탭이 아닌 전체 화면에서 sticky·스크롤이 자연스럽게 동작하므로 새 창으로 엽니다.

📚 완성 기사의 구조
🎯 목적 · 데이터 저널리즘의 표준 — "히어로 → 도입글 → 5섹션 시각화 → 풋터".
✨ 특징 · position:sticky로 차트 영역이 화면 절반을 고정. 오른쪽 텍스트가 흐르며 차트가 단계 전환.
🚦 5섹션 구성
  • ① 8년 연속 감소 추세 (이중축 선그래프)
  • ② 외부 충격 한 해 강조 (강조 막대)
  • ③ 차대차 74% 비중 (도넛)
  • ④ 수도권 집중 65% (가로 막대)
  • ⑤ "하루 7명" 핵심 수치
⚠️ 주의 · 일부 브라우저는 새 창에서 document.write를 막을 수 있음 → 그땐 별도 .html로 저장 후 직접 여세요.
📝 실무 권장: 새 창 코드를 통째로 복사 → article.html로 저장 → Live Server에서 열기. ③ Scrollama 패턴이 실제 페이지에서 작동하는 환경이 됨.
// ── 새 창에 HTML 통째로 작성하는 패턴 (개발용) ──
function openArticle() {
    const html = `<!DOCTYPE html>
<html><head>
  <title>교통사고 — 데이터로 보는 1년</title>
  <script src="https://cdn.jsdelivr.net/npm/scrollama@3.2.0/build/scrollama.min.js"><\/script>
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"><\/script>
  <style>
    .sticky-stage { position: sticky; top: 0; height: 100vh; ... }
    .step { min-height: 90vh; ... }
  </style>
</head><body>
  <div class="hero">...</div>
  <div class="scroll-section">
    <div class="sticky-stage"><canvas id="art-canvas"></canvas></div>
    <div class="steps">
      <div class="step" data-step="0">...</div>
      ... 5단계 ...
    </div>
  </div>
  <script>
    function showStep(s) { ... 차트 갱신 ... }
    showStep(0);
    scrollama().setup({step:'.step',offset:0.5})
               .onStepEnter(({index}) => showStep(index));
  <\/script>
</body></html>`;

    const w = window.open('', '_blank');
    w.document.write(html);
    w.document.close();
}

// ── 실무 권장 ──
// article.html 파일로 저장하고 Live Server 등으로 직접 열기.
// 그 쪽이 디버깅·캐싱·SEO 모두 유리.
💡 직접 만들 때는 같은 폴더에 article.html로 저장하고 Live Server에서 열면 됩니다.