← Course hub← 교안 허브
Personal project · not client work개인 프로젝트 · 고객 납품물이 아닙니다

How the /g/data course site is built/g/data 교안 사이트는 이렇게 만들었습니다

Course material for a 4-day Google Cloud data and database training, built as a website with narrated slides in Korean, English and Portuguese. While it speaks, the narration scrolls the page, steps through the diagrams and moves a cursor to point at things. Google Cloud 데이터·데이터베이스 4일 과정의 교안을 웹사이트로 만들었습니다. 한국어·영어·포르투갈어 해설이 말하는 동안 페이지를 스크롤하고, 그림을 한 단계씩 넘기고, 커서를 움직여 짚어 줍니다.

855narration segments per language언어별 해설 구간
2,565audio files across 3 languages3개 언어 음성 파일
174layout checks: 6 screen widths × 29 scenes화면 겹침 측정: 6개 폭 × 29개 장면
261live spots scanned, 0 errors라이브 점검 261곳, 오류 0

How it fits together전체 구조

Words글

Script스크립트

Korean text per segment, with scroll and cursor cues구간별 한국어 글과 스크롤·커서 연출 지시

Converter변환기

Turns the script into cue JSON, one file per language스크립트를 언어별 cue JSON으로 변환

Static site정적 사이트

HTML on Vercel, slides switch by URL hashVercel의 HTML, 장표는 주소 해시로 전환

Voice음성

Segment text구간 글

The same segments, one set per language같은 구간을 언어별로 한 벌씩

Text-to-speech음성 합성

One engine and one voice per language언어마다 엔진과 목소리 하나씩

Cloudflare R2

audio.cloudaimarket.com, cached close to the listeneraudio.cloudaimarket.com, 듣는 사람 가까이에 캐시

Reader's browser보는 사람의 브라우저

The player keeps voice, scroll, cursor and diagram steps in sync플레이어가 음성·스크롤·커서·그림 단계를 맞춰 재생합니다

Before every release배포할 때마다

Deploy guard배포 가드Compares with the live site first. Only the files I changed are allowed to differ.라이브와 먼저 대조해서 이번에 고친 파일만 달라야 통과합니다.
Real-browser scan실제 브라우저 점검Playwright opens the pages in each language and counts errors.Playwright가 언어별로 페이지를 열어 오류를 셉니다.
Layout measurement화면 겹침 측정Narration card versus buttons, at six widths from 1920 to 390 px.폭 1920~390px 여섯 가지에서 해설 카드와 버튼의 겹침을 잽니다.

The stack, in five parts구성 요소 다섯 가지

Pages페이지

Plain HTML, CSS and JavaScript generated by Python build scripts. Several slides share one file and switch by URL hash, so nothing reloads.순수 HTML·CSS·JavaScript를 Python 빌드 스크립트로 생성합니다. 여러 장표를 한 파일에 담아 주소 해시로 바꾸므로 새로고침이 없습니다.

HTML · CSS · JSPython

Narration player해설 플레이어

Reads cue JSON that says when to speak, scroll, press a button or advance a diagram step. It switches between a card, a bar and a phone dock to fit the screen.언제 말하고, 스크롤하고, 버튼을 누르고, 그림을 한 단계 넘길지 적힌 cue JSON을 읽습니다. 화면에 맞춰 카드·가로 막대·휴대폰 하단 도킹으로 바뀝니다.

JavaScriptJSON cues

Voices음성

One text-to-speech engine per language, generated for all 855 segments.언어마다 음성 합성 엔진을 하나씩 써서 855개 구간 전부를 만들었습니다.

xAI Grok · KOAzure HD neural · ENGemini TTS on Vertex AI · PT

Hosting호스팅

Pages on Vercel. The audio sits on Cloudflare R2 because Vercel's free plan caps an upload at 100 MB and the audio is several hundred MB.페이지는 Vercel에 둡니다. 음성은 수백 MB인데 Vercel 무료 플랜 업로드가 100MB까지라서 Cloudflare R2에서 내려줍니다.

VercelCloudflare R2Wrangler CLI

Release safety배포 안전장치

A deploy script rebuilds the live state, lets only the intended files differ, publishes, then checks everything again.배포 스크립트가 라이브와 같은 상태를 만들고, 의도한 파일만 다른지 확인한 뒤 올리고, 올린 뒤 전체를 다시 대조합니다.

BashPythonPlaywright

Three things that broke고장 났던 세 가지

Notes read aloud메모가 음성으로 읽힐 뻔했습니다

Author memos such as "needs verification" were mixed into 14 script segments and would have been spoken to students. The converter now cuts them out and keeps them in a separate list."확인 필요" 같은 작성자 메모가 14개 구간에 섞여 학생에게 읽힐 뻔했습니다. 변환기가 잘라 내고 별도 목록으로 남깁니다.

A false alarm허위 경보

My first overlap test reported 36 collisions, but the test skipped the real click flow. I re-measured with the real flow and fixed only what was real.처음 겹침 측정은 36곳이라고 나왔지만 측정 스크립트가 실제 클릭 흐름을 건너뛴 결과였습니다. 실제 흐름으로 다시 재고 진짜 문제만 고쳤습니다.

A silent failure on live라이브에서 조용히 죽은 장표

Unescaped quotes in 124 places broke one page's slide data, so none of its slides opened. Scanning the live pages is how it was found.따옴표 124곳이 이스케이프되지 않아 한 페이지의 장표 데이터가 깨졌고 장표가 하나도 열리지 않았습니다. 라이브 페이지를 점검하다 찾았습니다.