Svelte는 프론트엔드 프레임워크로, 빠른 속도와 간결한 문법으로 주목받고 있다. Svelte를 사용하는 방법은 크게 두 가지가 있다.
- Vite + Svelte: SPA에 적합. 기본 Svelte 기능만 활용하는 방법.
- SvelteKit: 웹앱에 적합. 라우터 등 다양한 기능이 추가됨.
Svelte와 SvelteKit은 같은 키워드와 문법을 공유한다. 하지만 파일 구조와 지원하는 기능이 다르다.
본 글은 SvelteKit에 대해 설명한다.
Introduction • Docs • Svelte
Before we begin If you’re new to Svelte or SvelteKit we recommend checking out the interactive tutorial. If you get stuck, reach out for help in the Discord chatroom. What is SvelteKit? SvelteKit is a framework for rapidly developing robust, performant w
svelte.dev
환경 설정
먼저 Node JS가 설치되어 있어야 한다. 설치되었다면 아래 명령어를 입력한다.
npx sv create my-app
cd my-app
npm install
sv create를 하면 다양한 옵션을 선택할 수 있다. 본인이 편한 설정을 선택하면 된다.
npm run dev
run은 작성된 파일을 localhost로 띄운다.
파일 구조
my-app/
├ src/
│ ├ lib/
│ ├ routes/
│ ├ app.html
│ ├ error.html
├ static/
기본적인 기능 구현을 위한 주요 파일만 표기했다.
- lib/: 라이브러리 및 서버 코드가 있는 곳. 다른 디렉토리 파일에서 $lib로 불러올 수 있다. (예: '$lib/server/...')
- routes/: 서브 페이지를 작성하는 곳. (아래 '라우팅' 참고)
- app.html: 페이지 템플릿. 정해진 %svelte.*% 키워드로 {head, body, assets} 등을 지정한다.
- error.html: Error 페이지 템플릿. %sveltekit.status%와 %sveltekit.error.massage% 키워드를 이용해 정보를 표시할 수 있다.
- static/: 이미지, 스타일시트 등 파일을 저장하는 곳. "/" 경로로 불러올 수 있다.
라우팅
파일 경로 기반 라우팅을 지원한다.
src/
├ routes/
│ ├ hello/
예를 들어, routes/hello 아래에 작성한 파일은 localhost/hello로 접속 가능하다.
페이지
src/
├ routes/
├ +page.svelte
├ +layout.svelte
+로 시작하는 파일은 약속된 이름의 파일로, +를 붙여야 적용된다.
- +page.svelte: 사용자에게 보여지는 HTML 템플릿. 'index.html' 같은 역할을 한다.
- +layout.svelte: 모든 페이지에 적용할 내용. <header>, <footer>와 같이 템플릿 전반에 걸쳐 적용할 내용을 작성한다.
+layout.svelte 예시는 다음과 같다.
<header>
<nav>...</nav>
</header>
<slot />
<!-- +page.svelte -->
<footer>Designed by Jin</footer>
<slot> 자리에 +page.svelte 내용이 들어간다. 이는 /routes 아래 페이지에도 적용된다.
State 관리
state는 페이지 내에서 실시간으로 변경되는 값을 말한다. *.svelte 내에서 관리하며, $state 키워드를 사용한다.
<script>
let count = $state(0);
</script>
state로부터 파생된 값은 $derived를 사용한다.
<script>
let logs = $state([]);
let numLogs = $derived(logs.length);
</script>
위 예시에서 derived를 사용하지 않으면, logs가 변경돼도 numLogs는 업데이트되지 않는다.
이렇게 선언된 변수는 { }로 감싸 HTML 태그에서 사용 가능하다.
<p>Items: {numLogs}</p>
조건 및 반복문
{#if lenText > 100}
<p>Too long!!</p>
{:else if 10 > lenText}
<p>Too short!!</p>
{:else}
<p>Great!</p>
{/if}
{#if } 키워드로 조건문 구현이 가능하다. 조건문 내 변수가 변하면 보이는 값도 실시간으로 변한다.
{#each items as item, i}
<li>{i + 1}: {item.name}</li>
{/each}
{#each } 키워드는 forEach처럼 반복 가능한 자료를 순회한다. i는 인덱스로 생략 가능하다.
{#each items as item (item.id)}
<li>{i + 1}: {item.name}</li>
{/each}
( ) 안에 고유한 primary key를 입력하면, svelte가 해당하는 아이템을 찾아 자동 업데이트 한다.
컴포넌트
템플릿을 나누어 관리할 수 있다.
routes/
├ items/
├ +page.svelte
├ ListBox.svelte
위 예시는 페이지에서 ListBox라는 컴포넌트를 분리했다.
<!-- +page.svelte -->
<script>
import ListBox from "./ListBox.svelte";
let num = 3;
let name = "David";
</script>
<ListBox id={num} {name} />
분리한 컴포넌트는 import 해 사용한다. 변수명={ }를 이용해 변수를 전달할 수 있다. 변수명은 생략 가능하다.
- num은 ListBox에서 id라는 변수명으로 사용할 수 있다.
- name은 ListBox에서 name이라는 변수명으로 사용할 수 있다.
변수명을 지정하지 않으면 기존 변수명을 그대로 사용한다.
<!-- ListBox.svelte -->
<script>
let { id, name } = $props();
</script>
<div>
{id}: {name}
</div>
전달한 변수는 $props 키워드로 불러와 사용한다.
기본적으로 변수는 부모 → 자식으로 전달한다. 만약 부모 ↔ 자식 양방향으로 변수를 전달하고 싶다면 bind 키워드를 사용한다.
<ListBox bind:name={name} >
<ListBox bind:name >
하지만 자식 컴포넌트가 여러 번 사용되면 문제가 발생할 수 있다. 따라서 부모에서 수동으로 관리하는 방법도 있다.
<!-- +page.svelte (Parent) -->
<script>
let parent_id = $state(3);
const update_id = (new_id) => {
parent_id = new_id;
}
</script>
<Child {parent_id} {update_id}/>
<Child {parent_id} {update_id}/>
<!-- Child.svelte (Children) -->
<script>
let { parent_id, update_id } = $props();
</script>
<button onclick={()=>{update_id(5)}}>Update</button>
부모에서 상태를 관리하는 함수를 선언하고, 자식이 사용할 수 있도록 전달한다.
Load
+page.js는 페이지를 렌더링 할 때 실행할 동작을 작성한다.
export async function load({ fetch }) {
const res = await fetch("/api");
const json = await res.json();
return { json };
}
load 함수는 페이지가 렌더링 될 때 실행한다. 이렇게 불러온 정보는 data 키워드로 전달된다.
<!-- +page.svelte -->
<script>
export let data;
// data: { json: {...}, ... }
let req = data.json;
</script>
참고로 렌더링 방식에 따라 두 종류를 지원한다.
- +page.js: Client Side Rendering
- +page.server.js: Server Side Rendering
같은 맥락에서 +layout.svelte에서 사용할 정보는 +layout.js 또는 +layout.server.js에 작성한다.
API 생성
+server.js로 간단한 API도 구현할 수 있다.
export function GET() {
const data = {
message: "Hello!",
time: new Date().toISOString(),
};
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
});
}
위 예시는 GET 요청을 처리하는 코드로, POST 등 요청도 구현할 수 있다.