Svelte 데이터 바인딩

·2024년 10월 20일
0

Svelte

목록 보기
7/7
post-thumbnail

🥨 들어가며

일반적으로 Svelte는 부모 컴포넌트에서 자식 컴포넌트로 데이터가 전달되는 단방향 데이터 흐름을 가진다. 단방향 데이터 흐름으로 인해 부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터가 업데이트될 때 자식 컴포넌트는 영향을 받아 반응형 동작을 하게 되지만, 반대로 자식 컴포넌트에서 부모 컴포넌트로 전달받은 데이터가 업데이트될 때 부모 컴포넌트는 영향을 받지 않는다.

이런 규칙을 깨고 자식 컴포넌트에서 부모 컴포넌트로 전달받은 데이터가 업데이트될 때 부모 컴포넌트가 반응형 동작을 할 수 있게 하는 양방향 데이터 흐름을 가질 수 있도록 하는 것이 데이터 바인딩이다. 🙂


🥨 데이터 바인딩

🧀 Input 태그

type="text"

<script>
	let name = 'world';
</script>

<input bind:value={name}>
<h1>Hello {name}!</h1>

완성 화면

이처럼 input에 입력된 값이 변경될 때마다 자동으로 name의 값 또한 변경되는 것을 볼 수 있다.

type="number", type="range"

<script>
	let a = 1;
	let b = 2;
</script>

<label>
	<input type="number" bind:value={a} min="0" max="10">
	<input type="range" bind:value={a} min="0" max="10">
</label>

<label>
	<input type="number" bind:value={b} min="0" max="10">
	<input type="range" bind:value={b} min="0" max="10">
</label>

<p> {a} + {b} = {a+b}</p>

완성 화면

input 태그의 type 속성이 number 혹은 range일 경우 바인딩되는 값은 자동으로 Number 타입이 된다.

type="checkbox"

<script>
	let yes = false;
</script>

<label>
	<input type="checkbox" bind:checked={yes}>
	CheckBox
</label>

완성 화면

type 속성이 checkbox인 경우, bind:checked를 사용해서 checked 속성을 바인딩해야 한다.

type="radio"

<script>
	let picked = null;
</script>

<label>
	<input type="radio" value="One" bind:group={picked}>
	One
</label>
<label>
	<input type="radio" value="Two" bind:group={picked}>
	Two
</label>
<br>
<span>선택: {picked}</span>

완성 화면

radio의 경우 여러 개의 input 태그들이 동일한 데이터에 바인딩되어야 한다. 이때는 bind:group을 사용한다. value 속성에서는 각각 input 태그들이 선택되었을 때 지정되어야 하는 데이터를 지정한다. bind:group은 type이 radio에서뿐만 아니라 동일한 데이터를 바인딩해야 할 경우에도 사용 가능하다.


🧀 Textarea 태그

<script>
	let value = 'Some Text';
</script>

<style>
	textarea { width: 100%; height: 200px;}
</style>

<!-- <textarea bind:value={value}></textarea> -->
<textarea bind:value></textarea> // 약어 사용 가능

🧀 Select 태그

<script>
	const list = [
		{id: 1, text: 'A'},
		{id: 2, text: 'B'},
		{id: 3, text: 'C'}
	]
	let selected;
</script>

<select bind:value={selected}>
	{#each list as item (item.id)}
		<option value={item}>{item.text}</option>
	{/each}
</select>
<span>선택함: {selected? selected.id : '기다리는 중...'}</span>

완성 화면

bind:value로 데이터 바인딩되는 값의 타입은 Object, String 등 모든 타입이 가능하다. 바인딩 데이터의 초기값이 정의되어 있지 않다면 리스트이 첫 번째 값이 기본값으로 저장된다.

multiple 속성

<script>
	const list = [
		{id: 1, text: 'A'},
		{id: 2, text: 'B'},
		{id: 3, text: 'C'}
	];
	let selected = [];
</script>

<select multiple bind:value={selected}>
	{#each list as item (item.id)}
		<option value={item}>{item.text}</option>
	{/each}
</select>
<span>선택함: {selected? selected.map(x => x.id).join(',') : '기다리는 중...'}</span>

완성 화면


🧀 contenteditable 속성

<script>
	let html = '<p>텍스트를 입력해 주세요.</p>'
</script>

<div
	contenteditable="true"
	bind:innerHTML={html}
></div>

<div
	contenteditable="true"
	bind:innerHTML={html}
></div>

<pre>{html}</pre>

<style>
	[contenteditable] {
		padding: 0.5em;
		border: 1px solid #eee;
		border-radius: 4px;
	}
</style>

완성 화면

contenteditable를 true로 설정하면 textContent와 innerHTML 속성을 바인딩하여 사용할 수 있다.


🧀 Each 블록 바인딩

<script>
    import { each } from "svelte/internal";

	let todos = [
		{done: false, text: '독서하기'},
		{done: false, text: 'Svelte 공부하기'},
		{done: false, text: '일기 쓰기'}
	]
</script>

{#each todos as todo, index (index)}
	<label class:done={todo.done}>
		<input
			type="checkbox"
			bind:checked={todo.done}>
			{todo.text}
	</label>
{/each}

완성 화면


🧀 This 바인딩

DOM 요소를 가져오고 싶거나 컴포넌트 인스턴스를 가져올 때 사용하는 것이 This 바인딩이다.

HTML 태그에서 This 바인딩

<script>
    let value;
	let input;
</script>

<input bind:value bind:this={input}>
<button on:click={() => input.focus()}>focus</button>

완성 화면

버튼을 클릭하면 input 태그가 포커스되고, bind:this={input}와 같이 작성하면 input 변수에 input 태그가 바인딩된다. 이벤트 핸들러 등의 다른 함수에서 바인딩된 DOM 요소를 접근해서 사용할 수 있다.

컴포넌트에서 This 바인딩

Inner.svelte

<!-- src/Inner.svelte -->
<script>
    let value, input;
</script>

<input bind:value bind:this={input}>

App.svelte

<script>
	import Inner from "./Inner.svelte";
	let inner;
</script>

<Inner bind:this={inner} />
<button on:click={() => inner.$capture_state().input.focus()}>Focus</button>

This를 바인딩하여 컴포넌트 인스턴스를 가져온 경우, 바인딩된 컴포넌트 인스턴스의 $capture_state()를 호출하면 해당 컴포넌트의 데이터(State) 값을 가져올 수 있다.


🧀 컴포넌트 Props 바인딩

HTML 속성을 바인딩한 것처럼 bind:Props를 사용하여 컴포넌트의 Props를 바인딩할 수도 있다.

Inner.svelte

<!-- src/Inner.svelte -->
<script>
    export let value;
</script>

<input bind:value>

App.svelte

<script>
	import Inner from "./Inner.svelte";
	let value;
	function handleClick() {
		alert(value);
	}
</script>

<Inner bind:value />
<button on:click={handleClick}>출력</button>

Inner 컴포넌트는 value라는 Props를 전달받고, App 컴포넌트는 bind:value로 value라는 Props를 바인딩했다. 이렇게 컴포넌트의 Props를 바인딩하면 Inner 컴포넌트에서도 App 컴포넌트의 value 값을 업데이트할 수 있는 양방향 데이터 흐름을 가지게 된다.

profile
풀스택 개발자 기록집 📁

0개의 댓글