영상으로 웹사이트 만들기
todah.dev처럼 스크롤하면 영상처럼 재생되는 한 페이지를 Claude로 만드는 순서예요. Claude로 영상을 먼저 만들고, 그 영상을 주고 Astro로 사이트를 만들게 해요.
영상이 먼저 있으면 색, 글꼴, 장면 순서, 움직임이 이미 정해져 있어요. 그래서 사이트를 요청할 때 말로 설명할 게 줄고, 결과를 영상 캡처와 나란히 놓고 맞춰 볼 수 있어요. todah.dev도 HyperFrames로 먼저 만든 20초 영상 "귀찮으니까, 만든다"를 스크롤로 재생되는 한 페이지로 옮긴 거예요.
HyperFrames는 무료 오픈 소스이고 로컬 렌더는 HeyGen 크레딧을 쓰지 않아요. Claude Code 사용료와, 골라서 쓰는 음성이나 생성 미디어 서비스 요금은 따로예요.
이렇게 되면 성공이에요
- 1영상
npx hyperframes render 로 mp4가 나오고, 장면 캡처가 있어요 - 2스크롤
스크롤을 내리면 장면이 영상처럼 이어서 움직여요 - 3JS 꺼도
자바스크립트를 꺼도 모든 글이 보여요 - 4배포
내 도메인으로 접속하면 같은 페이지가 떠요
한눈에 보는 진행 순서
다섯 단계예요. 단계를 누르면 자세한 설명으로 가요.
1단계, Claude로 영상부터 만들기
HyperFrames는 HTML로 영상을 만드는 도구예요. Claude Code에 스킬로 붙여 쓰면 돼요.
설치
준비물: Claude Code, Node.js 22 이상(npm 포함), FFmpeg. FFmpeg는 mp4로 렌더할 때 필요해요.
영상 폴더에서 스킬을 추가하고, 고르는 화면에서 Core Skills를 선택해요. 그다음 그 폴더에서 Claude Code를 다시 켜요.
npx skills add heygen-com/hyperframes
미리 보기와 mp4 만들기는 이 두 명령이에요.
npx hyperframes preview npx hyperframes render --output video.mp4
Claude Code 플러그인으로 설치해도 돼요(claude plugin marketplace add heygen-com/hyperframes, claude plugin install hyperframes@hyperframes). 설치 뒤 새 세션을 열거나 /reload-plugins를 하고, 아래 프롬프트 앞의 /hyperframes 를 /hyperframes:hyperframes 로 바꿔요. 스킬과 플러그인 중 하나만 설치해요.
요청할 때 적을 네 가지
| 항목 | 적는 법 |
|---|---|
| 길이 | 15~20초 정도. 짧을수록 사이트로 옮기기 쉬워요 |
| 화면 비율 | 세로 1080x1920(폰 중심) 또는 가로 1920x1080 |
| 장면 수 | 5~8개. 장면마다 문구 한 줄 |
| 색과 분위기 | 배경색, 강조색 하나, 글꼴 느낌(굵은 고딕 등), 움직임 속도 |
그대로 복사해 쓰는 프롬프트
예시 1: 직업 소개 포트폴리오
/hyperframes 로 나를 소개하는 20초 세로 영상(1080x1920)을 만들어 줘. 나는 [직업]이고, 대표 작업은 [작업 1], [작업 2], [작업 3]이야. 장면은 6개: 이름과 한 줄 소개, 작업 3개를 하나씩, 일하는 방식 한 줄, 연락 방법. 배경은 흰색, 강조색은 파란색 하나, 굵은 고딕 글꼴, 장면 전환은 빠르고 깔끔하게. 나중에 이 영상을 웹사이트로 옮길 거라 글은 모두 HTML 텍스트로 만들어 줘. 끝나면 로컬 미리 보기를 열어 줘.
예시 2: 가게나 서비스 랜딩 페이지
/hyperframes 로 [가게/서비스 이름]의 15초 가로 영상(1920x1080)을 만들어 줘. 장면은 5개: 문제 한 줄, 해결 한 줄, 특징 2개, 마지막에 '지금 예약하기' 버튼. 분위기는 [따뜻하고 차분하게], 색은 [크림색 배경 + 초록 강조색 하나]. 글은 모두 HTML 텍스트로, 장면마다 문구는 한 줄만. 끝나면 로컬 미리 보기를 열어 줘.
예시 3: 개인 브랜드
/hyperframes 로 내 개인 브랜드 '[브랜드 이름]'을 보여 주는 20초 세로 영상(1080x1920)을 만들어 줘. 핵심 문장은 '[한 줄 슬로건]'이고, 이 문장이 첫 장면과 마지막 장면에 나와. 장면은 7개, 검은 배경에 강조색 하나, 글자가 크게 튀어나오는 리듬감 있는 움직임. 마지막 장면은 SNS 계정과 팔로우 버튼. 나중에 이 영상을 웹사이트로 옮길 거라 글은 모두 HTML 텍스트로 만들어 줘. 끝나면 로컬 미리 보기를 열어 줘.
부족한 정보가 있으면 Claude가 방향을 먼저 제안하고 중요한 것만 물어봐요. 답해 주면서 다듬으면 돼요.
영상을 장면으로 나누기
사이트는 장면 단위로 만들어요. 영상을 장면 목록으로 바꿔 두면 다음 단계 요청이 쉬워져요.
영상 프로젝트 폴더에서 Claude Code를 열고 요청해요. 그래야 Claude가 영상의 HTML 원본을 읽고 시간과 문구를 그대로 옮길 수 있어요. mp4만 보면 정확한 시간과 움직임을 알기 어려워요.
이 영상의 컴포지션 HTML을 읽고 장면별로 나눠 줘. 장면마다 시작 시간, 끝 시간, 화면에 나오는 문구, 움직임 설명을 HTML에 적힌 값 그대로 표로 정리해 SCENES.md에 써 줘. 그리고 npx hyperframes snapshot --at 으로 장면마다 가장 잘 보이는 순간을 1장씩 찍어 captures/ 폴더에 scene-01.png 처럼 저장해 줘.
2단계, 그 영상으로 Astro 사이트 만들기
영상, 영상 HTML 폴더, 장면 목록, 캡처를 새 폴더에 넣고 Claude Code에 요청해요. 설계 문서를 먼저 쓰고, 장면은 하나씩 만들어요.
Astro는 Node.js 22.12 이상이 필요해요(23 같은 홀수 버전은 안 돼요). 터미널에서 node -v로 확인해요.
첫 요청: SPEC.md부터
video.mp4, 영상 HTML 폴더, SCENES.md, captures/ 를 봐 줘. 색, 글꼴, 크기, 타이밍은 영상 HTML에서 그대로 가져와 줘. 이 영상을 Astro로, 스크롤하면 영상처럼 재생되는 한 페이지로 만들 거야. 코드는 아직 쓰지 말고 SPEC.md부터 써 줘. 넣을 것: 1. 꼭 지킬 것: 글은 모두 실제 HTML 텍스트, 강조색 하나, JS를 꺼도 모든 글이 보일 것, prefers-reduced-motion이면 움직임 없이 보여 줄 것, 모바일 우선, 아래 패키지 말고 새 패키지 금지 2. 쓸 것: Astro, @astrojs/sitemap, GSAP(ScrollTrigger), Lenis, @fontsource 글꼴, sharp 3. 색과 글꼴(영상에서 그대로) 4. 장면 순서와 장면마다 스크롤 길이(vh) 5. title, description, Open Graph, canonical, 사이트맵 다 쓰면 나한테 보여 주고 기다려.
다음 요청: 장면 하나씩
SPEC.md대로 Astro 프로젝트를 만들고 1번 장면만 만들어 줘. 장면 하나 = sticky 섹션(높이는 SPEC의 vh) + GSAP ScrollTrigger scrub 타임라인. captures/scene-01.png와 같아 보이게 하고, /dev/scene-01 개발 페이지에서 따로 볼 수 있게 해 줘. /dev/ 페이지는 배포에서 빼 줘. 사이트맵 filter에서 제외하고, 빌드 뒤 dist/dev는 지워 줘. 끝나면 데스크톱 1440x900, 폰 390x844로 스크린샷을 찍어 캡처와 비교해 줘.
1번이 마음에 들면 "2번 장면도 같은 방식으로"처럼 하나씩 이어 가요. 다 만들면 한 페이지로 이어 붙이게 해요.
구조
- 장면 섹션height: 300vh 같은 스크롤 길이를 가진 섹션
- sticky 화면안쪽 100svh 화면이 position: sticky로 붙어 있어요
- scrub 타임라인GSAP ScrollTrigger의 scrub이 스크롤 진행도에 애니메이션을 맞춰요
- Lenis스크롤을 부드럽게, ScrollTrigger와 같은 박자로
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import Lenis from 'lenis';
import 'lenis/dist/lenis.css';
gsap.registerPlugin(ScrollTrigger);
// 움직임 줄이기 설정이면 Lenis도 타임라인도 만들지 않아요
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
const lenis = new Lenis();
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => lenis.raf(time * 1000));
gsap.ticker.lagSmoothing(0);
// 첫 장면: 처음엔 다 보이고, 스크롤하면 나가요
gsap.timeline({
scrollTrigger: { trigger: '#scene-01', start: 'top top', end: 'bottom bottom', scrub: true }
})
.to('#scene-01 .card', { scale: 1.2 })
.to('#scene-01 h2', { y: -80, opacity: 0 });
}첫 장면은 처음부터 다 보이게 두고, 스크롤하면 나가게 해요. gsap.from()은 바로 시작 값을 적용해서 첫 장면에 쓰면 제목이 숨은 채 열려요. 들어오는 움직임은 2번 장면부터 써요.
| todah.dev가 쓴 것 | 역할 |
|---|---|
| Astro | 페이지를 HTML로 미리 만들어요 |
| @astrojs/sitemap | 빌드할 때 sitemap-index.xml을 만들어요 |
| GSAP + ScrollTrigger | 스크롤에 맞춰 움직이는 타임라인. GSAP는 플러그인까지 모두 무료예요 |
| Lenis | 부드러운 스크롤 |
| @fontsource | 글꼴을 npm 패키지로 받아 import해서 내 사이트에서 직접 제공. Astro 내장 Fonts API의 Fontsource 공급자로 해도 돼요 |
| sharp | 이미지 변환과 최적화 |
왜 Astro인가
Astro는 랜딩 페이지, 포트폴리오, 블로그처럼 콘텐츠 중심 사이트에 현업에서 많이 쓰는 프레임워크예요. 공식 문서도 블로그, 마케팅, 쇼핑몰 같은 콘텐츠 사이트를 위해 설계했다고 설명해요.
미리 만든 HTML (정적 생성)
기본 설정에서 사이트 전체를 빌드할 때 HTML로 미리 만들어 보내요. 검색 엔진이 자바스크립트를 실행하지 않아도 글을 바로 읽을 수 있어요.
JS는 필요한 곳에만
Astro 컴포넌트는 기본으로 HTML과 CSS로만 나가요. 스크롤 애니메이션 같은 JS는 .astro 파일 안 <script>에 쓰면 Astro가 처리해서 묶고, 같은 컴포넌트를 한 페이지에 여러 번 써도 한 번만 넣어요. client:load, client:visible 같은 지시어는 React, Svelte 같은 UI 프레임워크 컴포넌트를 쓸 때 붙이는 거예요. 보내는 JS가 적어서 로딩이 빨라요.
SEO에 필요한 것들을 넣기 쉬워요
| 항목 | 방법 |
|---|---|
| 사이트맵 | npx astro add sitemap, astro.config에 site 주소. 빌드하면 sitemap-index.xml이 생겨요 |
| title, description | 레이아웃의 <head>에 페이지마다 props로 넣어요 |
| Open Graph | og:title, og:description, og:image 메타 태그를 같은 레이아웃에 |
| canonical | <link rel="canonical">에 Astro.site 기준 주소 |
| 구조화 데이터 | <script type="application/ld+json" set:html={JSON.stringify(data)} />로 JSON-LD를 넣어요 |
| 이미지 | src/ 안에 두고 import한 이미지를 astro:assets의 <Image />로 넣으면 크기와 형식을 바꾸고 width, height를 자동으로 넣어 레이아웃 밀림(CLS)을 막아요. loading="lazy"도 붙어요. public/에 둔 이미지는 변환 없이 그대로 나가요 |
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.com',
integrations: [sitemap()],
});움직여도 글은 진짜 HTML
영상처럼 보여도 글을 이미지나 캔버스에 그리지 않고 HTML 텍스트로 두면, 검색 엔진도 화면 낭독기도 읽을 수 있어요. todah.dev가 지킨 원칙이에요.
다른 선택지와 비교
| 단일 페이지 앱(클라이언트 렌더링) | Astro (기본 정적 생성) | |
|---|---|---|
| HTML 만드는 곳 | 브라우저에서 JS가 화면을 그려요 | 빌드할 때 미리 HTML로 |
| 기본 JS | 앱 전체를 위한 JS를 보내요 | 없음. 내가 넣은 <script>와 아일랜드만 |
| 검색 엔진이 보는 글 | JS가 실행된 뒤에 생겨요 | 처음 받은 HTML에 이미 있어요 |
| 잘 맞는 곳 | 로그인 뒤 복잡한 앱, 대시보드 | 랜딩, 포트폴리오, 블로그 |
Astro 공식 문서는 Next.js, SvelteKit 같은 프레임워크가 복잡한 앱에는 강하지만 콘텐츠 사이트에는 클라이언트 렌더링 때문에 복잡성과 성능 부담이 생길 수 있다고 설명해요.
확인하고 고치기
장면마다 눈으로 보고 고쳐요. 숫자보다 화면이 먼저예요.
- 장면별 개발 페이지
/dev/scene-01 처럼 장면 하나만 보는 페이지에서 먼저 맞춰요.
- 장면 스크린샷
Playwright로 /dev/scene-01/ 을 스크롤 진행도 0, 0.5, 1에서 데스크톱 1440x900, 폰 390x844로 찍는 스크립트를 만들어 줘. 찍은 사진을 captures/scene-01.png와 나란히 보여 줘.
장면 페이지를 스크롤 진행도 몇 군데에서 데스크톱 1440x900, 폰 390x844 두 크기로 찍어 영상 캡처와 나란히 봐요. 찍는 스크립트는 Claude에게 만들게 해요. todah.dev도 scripts/shoot.mjs로 장면 페이지를 진행도별로 찍어요.
- Lighthouse
npm run build, npm run preview 로 띄운 뒤 Chrome 개발자 도구의 Lighthouse로 성능, 접근성, SEO를 봐요.
1440x900과 390x844 스크린샷을 captures/scene-03.png와 비교해서 다른 점을 적고 고쳐 줘. 고친 뒤 다시 찍어서 보여 줘.
배포
빌드하면 dist/ 폴더에 정적 파일이 생겨요. 이걸 올리면 끝이에요.
배포 전에 /dev/ 개발 페이지가 빌드와 사이트맵에서 빠지게 해 달라고 해요. 사이트맵은 기본으로 모든 페이지를 넣어요. todah.dev는 sitemap filter로 /dev/를 빼고, 빌드 명령을 astro build && node scripts/postbuild.mjs로 바꿔 빌드 뒤 dist/dev를 지워요.
// astro.config.mjs
integrations: [sitemap({ filter: (page) => !page.includes('/dev/') })],
// scripts/postbuild.mjs
import fs from 'node:fs';
fs.rmSync(new URL('../dist/dev/', import.meta.url), { recursive: true, force: true });Cloudflare Workers 정적 자산
프로젝트 루트에 wrangler.jsonc를 만들어요. 정적 사이트라 Astro 어댑터는 필요 없어요. 도메인은 Cloudflare에 연결된 내 도메인이어야 해요.
{
"name": "my-site",
"compatibility_date": "2026-10-09", // 배포하는 날짜로 바꿔요
"assets": {
"directory": "./dist",
"not_found_handling": "404-page"
},
"routes": [
{ "pattern": "example.com", "custom_domain": true }
]
}npm install wrangler@latest --save-dev npm run build npx wrangler deploy
처음 배포하면 브라우저가 열려 Cloudflare 계정으로 로그인해요. not_found_handling: "404-page"가 있어야 Astro의 404 페이지가 404로 나가요.
custom_domain을 쓰면 Cloudflare가 DNS 레코드와 인증서를 자동으로 만들어요. 같은 이름에 CNAME 레코드가 이미 있으면 만들 수 없어요.
또는 Coolify (Docker + nginx)
FROM node:22-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY nginx.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html EXPOSE 80
# nginx.conf (Dockerfile 옆에)
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / { try_files $uri $uri/index.html $uri.html =404; }
error_page 404 /404.html;
}Coolify에서 저장소를 연결하고 빌드 방식을 Dockerfile로 고르면 돼요. Coolify 앱의 포트는 80으로 맞춰요(Dockerfile의 EXPOSE 80과 같게). nginx.conf의 error_page 404 /404.html; 이 있어야 Astro의 404 페이지가 나와요(todah.dev 방식).
자주 막히는 곳
| 증상 | 고치는 법 |
|---|---|
| 애니메이션 시작 위치가 어긋나요 | 글꼴이 늦게 로드되면 높이가 바뀌어요. document.fonts.ready 뒤에 ScrollTrigger.refresh()로 위치를 다시 재요 |
| 폰에서 옆으로 밀려요 (가로 스크롤) | 화면 밖으로 나가는 요소 때문이에요. 바깥 래퍼에 overflow-x: clip, 넓은 요소는 max-width: 100% |
| 폰에서 효과가 안 보여요 | 폰에는 hover가 없어요. hover에만 기대는 효과는 스크롤이나 탭으로 바꿔요 |
| JS를 끄면 빈 화면 | 처음 상태를 opacity: 0으로 CSS에 두면 그래요. 숨기는 건 JS가 실행된 뒤 gsap.from으로 하게 해요. 또는 <head>의 짧은 스크립트로 <html>에 js 클래스를 붙이고, 숨기는 CSS는 html.js 아래에만 둬요 |
| 움직임을 줄인 사용자 | JS에서 window.matchMedia('(prefers-reduced-motion: reduce)').matches가 참이면 타임라인을 만들지 말고 최종 화면을 그대로 보여 줘요(gsap.matchMedia()로 해도 돼요) |
document.fonts.ready.then(() => ScrollTrigger.refresh());
명령어와 설정은 2026-10-09에 각 공식 문서에서 확인했어요. 프롬프트 예시는 따라 쓰기 쉽게 만든 예시예요.
- todah.dev
- HyperFrames 개요
- HyperFrames Quick start
- HyperFrames Plugins (설치, 준비물)
- HyperFrames CLI (snapshot)
- Astro: 설치 (Node.js 버전)
- Astro: Why Astro
- Astro: Islands architecture
- Astro: 스크립트
- Astro: 지시어 (client:, set:html)
- Astro: Rendering modes
- Astro: @astrojs/sitemap
- Astro: Images
- Astro: Fonts (내장 Fonts API)
- Astro: Deploy to Cloudflare
- GSAP ScrollTrigger
- GSAP ScrollTrigger.refresh()
- GSAP gsap.from()
- GSAP gsap.matchMedia()
- GSAP 가격 (무료 안내)
- Lenis
- Lenis README (GSAP 연동)
- Cloudflare Workers Static Assets
- Cloudflare Workers 시작하기 (로그인)
- Cloudflare Workers Custom Domains
2LAZZZY / 2026-10-09 기준. 이 페이지에는 실제 계정 ID, 키, 개인 정보가 없어요. 체크 표시는 이 기기에만 저장돼요.