Svelte 5 정적 전용 (Runes)
이 스킬이 다루는 내용
"정적 전용"이란 결과물이 사전 구축된 HTML/CSS/JS 폴더이며 요청 시 Node/에지 서버가 없는 것을 의미합니다. GitHub Pages, Netlify/Cloudflare/Vercel 정적 호스팅, S3+CloudFront에서 호스팅하거나 로컬 파일로 열 수 있습니다. 이러한 제약은 +page.server.js, +server.js, 폼 액션, hooks.server.js 및 실행 중인 서버를 가정하는 모든 코드 경로를 배제합니다. "Runes"는 구성 요소 반응성이 Svelte 5의 $state/$derived/$effect/$props 프리미티브로 작성되었음을 의미하며, Svelte 4의 export let / $: / 암시적 반응성이 아닙니다.
이 두 가지 제약은 상호 작용합니다. 정적 빌드는 $effect 내부를 포함하여 어떤 코드도 요청별로 서버에서 실행된다고 가정하지 않는 경우에만 깔끔하게 작동합니다. 이 점을 항상 염두에 두십시오. 사람들이 SvelteKit 앱을 adapter-static으로 이동할 때 대부분의 버그가 발생하는 원천입니다.
1단계 — 올바른 프로젝트 형태 선택
앱에 파일 기반 라우팅, 여러 개의 사전 렌더링된 페이지 또는 SEO 친화적인 경로별 HTML이 필요한지 질문(또는 컨텍스트에서 추론)하십시오. 이는 전체 설정을 결정하므로 건너뛰지 마십시오.
| 필요 사항 | 사용 방법 |
|---|---|
| 단일 페이지 / 대시보드 / 위젯 / 임베드, 라우팅 불필요 | 일반 Vite + Svelte (npm create vite@latest -- --template svelte-ts). 실수로 서버를 도입할 위험이 없는 가장 간단한 정적 전용 설정입니다. |
| 여러 경로, 각각 자체 HTML 파일로 사전 렌더링됨, SEO 중요 | prerender = true 가 설정된 SvelteKit + @sveltejs/adapter-static |
| 여러 경로지만 SEO는 중요하지 않음 (내부 도구, Chrome 확장 프로그램, Electron/Tauri UI) | SPA 모드의 SvelteKit + adapter-static (단일 fallback: 'index.html' 셸, 클라이언트 측 라우팅) |
특별히 라우팅이 필요하지 않다면 기본적으로 일반 Vite+Svelte를 사용하십시오. 설정이 덜 복잡하고 잘못 구성할 위험이 적습니다. 두 경로에 대한 전체 설정 단계, 구성 파일 및 배포 노트는 references/static-build.md에 있습니다. svelte.config.js / vite.config.js를 메모리에서 생성하기 전에 읽으십시오. prerender/adapter 옵션은 Svelte 버전에 따라 변경되며 미묘하게 잘못 설정하기 쉽습니다(예: trailingSlash를 잊거나, 사전 렌더링하려는 페이지에서 ssr = false를 설정하여 실제 HTML 대신 빈 셸을 생성하는 경우).
2단계 — Svelte 4 구문 대신 runes로 구성 요소 작성
새로운 Svelte 5 코드에서 export let, $:, on:click 또는 일반 writable() 스토어를 구성 요소 상태를 관리하는 주요 방식으로 절대 작성하지 마십시오. 이러한 구문은 대부분의 구성에서 여전히 컴파일되지만 레거시 패턴입니다. 여기서 요구하는 것은 runes입니다. 핵심 매핑:
| Svelte 4 | Svelte 5 |
|---|---|
let count = 0 (최상위 수준이므로 반응적) | let count = $state(0) |
$: doubled = count * 2 | let doubled = $derived(count * 2) (또는 다중 문 로직의 경우 $derived.by(() => ...)) |
$: { sideEffect(count) } | $effect(() => { sideEffect(count) }) |
export let name | let { name } = $props() |
export let value; bind:value (자식 변경) | let { value = $bindable() } = $props() |
DOM 전용 설정/해제를 위한 onMount/onDestroy | $effect(() => { ...; return () => cleanup(); }) |
<slot name="header"> | {#snippet header()}...{/snippet} + {@render header()} |
writable()/readable() 스토어, 구성 요소 내 $store | .svelte.js/.svelte.ts 모듈의 $state(...), 직접 가져오기 (반응성이 이제 구성 요소 외부에서도 작동) |
중요하지 않은 rune 코드를 작성하기 전에 references/runes.md를 읽으십시오. 사람들이 확실히 틀리는 부분을 다룹니다: $derived는 순수해야 함(내부에 부작용이나 상태 변이 없음), $effect는 읽는 동일한 상태에 절대 쓰면 안 됨(무한 루프), 재할당하는 대규모/동결 데이터를 위한 $state.raw, 구성 요소 수명 주기 외부에서 공유되는 상태를 위한 $effect.root/untrack, 그리고 구성 요소 간/모듈 상태를 위한 정확한 클래스 기반 패턴. 이러한 부분을 잘못 이해하면 이상한 재렌더링 루프나 오래된 UI로만 나타나는 버그가 발생하므로 나중에 진단하기 어렵습니다. 처음부터 올바르게 작성하는 것이 좋습니다.
기존 Svelte 4 코드를 마이그레이션할 때 npx sv migrate svelte-5는 기계적인 재작성을 처리합니다(현재 도구입니다. 오래된 문서는 svelte-migrate를 참조할 수 있음). 그러나 항상 diff를 검토하십시오. 자동화된 마이그레이션은 교차 모듈 상태 패턴으로서의 스토어나 파생된 값이어야 하는 효과를 안정적으로 수정하지 못합니다.
3단계 — 보안 체크리스트 적용
정적 호스팅이 "보안 표면이 없다"는 의미는 아닙니다. 서버 측 공격 벡터는 제거되지만 클라이언트 측 공격 벡터(XSS, 비밀 누출, 안전하지 않은 타사 임베드)는 이를 차단할 서버 측 레이어가 없으므로 전적으로 개발자에게 달려 있습니다. 사용자 생성 콘텐츠, {@html}, 환경 변수 또는 타사 스크립트를 다루는 작업을 완료하기 전에 references/security.md를 확인하십시오. 빠른 작업에서도 놓치지 않도록 가장 영향력이 큰 규칙:
- 비위생 콘텐츠를
{@html}에 전달하지 마십시오. Svelte는{expression}중괄호 안의 모든 것을 자동으로 이스케이프 처리하지만{@html ...}는 이를 완전히 우회합니다. 이는 Svelte 앱에서 가장 큰 XSS 원천입니다. DOMPurify(또는 엄격한 허용 목록)를 사용하여 렌더링 직전에 즉시 위생 처리하고, 파이프라인 초기 단계에서 처리하지 마십시오. 나중에 편집으로 인해 우회될 수 있습니다. VITE_접두사(또는 SvelteKitPUBLIC_접두사)가 붙은 환경 변수 뒤에 실제 비밀을 절대 두지 마십시오. Vite는 이를 빌드 시 출하된 JS 번들에 인라인 처리합니다. 누구나 빌드된 파일에서 읽을 수 있습니다. 정적 전용 앱에는 비밀을 숨길 서버가 전혀 없습니다. 따라서 앱에 실제 API 키가 필요한 경우 키는 타사 서비스의 서버에 있거나 별도로 배포한 서버리스 함수/프록시 뒤에 있어야 합니다. 조용히 키를 클라이언트 코드에 연결하지 말고 이 점을 알려주십시오.- 서버에서 헤더를 보낼 수 없더라도 Content-Security-Policy를 설정하십시오. 정적 호스트는
app.html/index.html의<meta http-equiv="Content-Security-Policy">태그 또는 호스트별 헤더 파일(Netlify_headers, Cloudflare Pages_headers, Vercelvercel.jsonheaders)을 통해 이를 계속 지원합니다. 자세한 내용은references/security.md를 참조하십시오. - 모든 동적
href/src가 사용자 입력 또는 API 응답에서 올 수 있다면 공격자에 의해 제어된다고 간주하십시오. Svelte는href바인딩에서javascript:URL을 차단하지 않습니다. 직접 체계를 검증/허용 목록에 추가하십시오. - 출시 전에
npm audit(또는 이에 상응하는 도구)를 실행하고 잠금 파일을 고정하십시오. 정적 사이트는 모든 종속성의 코드를 브라우저로 직접 전달하므로 손상된 종속성은 직접적인 클라이언트 측 손상입니다.
참조 파일
references/runes.md— 올바른/잘못된 패턴, 클래스 기반 공유 상태, props/snippets용 TypeScript 타이핑 및 Svelte 4에서 5로의 마이그레이션 테이블을 상세히 다루는 전체 rune별 가이드.references/static-build.md— 일반 Vite+Svelte 설정, SvelteKit+adapter-static 설정(전체 사전 렌더링 및 SPA 폴백 모드), trailing-slash/base-path 문제 및 호스트별 배포 노트(GitHub Pages, Netlify, Cloudflare Pages, S3/CloudFront).references/security.md— 전체 체크리스트: XSS/{@html}/DOMPurify, 정적 호스트용 CSP(메타 태그 +_headers템플릿), 비밀/환경 변수, 안전한 외부 링크, 종속성 감사, Trusted Types, localStorage/sessionStorage 민감성 및 출시 전 체크리스트.assets/— 복사 가능한 시작 파일:vite.config.ts,svelte.config.js(adapter-static),_headers(CSP 템플릿) 및SecureHtml.svelte(DOMPurify 래퍼 구성 요소).
내용을 메모리에서 재현하기보다 필요에 따라 이러한 파일을 가져오십시오. 구성 옵션 이름과 CSP 지시문 세부 사항은 정확히 시간이 지나면서 구식이 되거나 잘못 기억되기 쉬운 종류의 세부 정보입니다.