$props: 컴포넌트에 값 전달하기

$props: 컴포넌트에 값 전달하기

컴포넌트는 화면의 재사용 가능한 조각인데, 조각마다 서로 다른 값을 받아야 할 때가 있어요. 예를 들어 같은 카드 컴포넌트에 "cool", "warm" 같은 수식어를 다르게 넣고 싶은 상황이죠. 컴포넌트에 전달하는 입력값을 props(properties 의 약자) 라고 해요. HTML 요소에 속성을 넘기듯, 컴포넌트에도 props를 넘겨요.

받는 쪽에서는 $props 룬으로 props를 꺼내 써요. 개별 값 하나씩 받는 구조라서, 어떤 props가 들어올지 컴포넌트의 계약이 분명해져요.

출처: Svelte 공식 문서 — $props

기본 사용법

props는 요소에 속성을 넘기듯 컴포넌트에 넘겨요.

<!--- file: App.svelte --->
<script>
	import MyComponent from './MyComponent.svelte';
</script>

<MyComponent adjective="cool" />

받는 쪽 MyComponent.svelte 에서는 $props 룬으로 받아요.

<!--- file: MyComponent.svelte --->
<script>
	let props = $props();
</script>

<p>this component is {props.adjective}</p>

더 자주 쓰는 방식은 props를 구조분해(destructure) 하는 거예요.

<!--- file: MyComponent.svelte --->
<script>
	let { adjective } = $props();
</script>

<p>this component is {adjective}</p>

기본값(Fallback values)

구조분해를 쓰면, 부모가 props를 주지 않았거나 값이 undefined 일 때 사용할 기본값을 선언할 수 있어요.

let { adjective = 'happy' } = $props();

⚠️ 기본값은 반응형 state proxy로 바뀌지 않아요. (자세한 건 아래 'props 업데이트' 참고)

props 이름 바꾸기

구조분해를 이용해 props의 이름을 바꿀 수도 있어요. super 처럼 JavaScript 키워드이거나 유효하지 않은 식별자일 때 필요해요.

let { super: trouper = 'lights are gonna find me' } = $props();

나머지 props(Rest props)

rest property 를 쓰면 나머지 props를 한 번에 받을 수 있어요.

let { a, b, c, ...others } = $props();

props 업데이트

컴포넌트 안에서 props에 대한 참조는 props 자체가 바뀔 때 함께 갱신돼요. App.svelte 에서 count 가 바뀌면 Child.svelte 안의 count 도 따라 바뀌죠. 다만 자식 컴포넌트는 props 값을 임시로(재)할당할 수 있어요. 저장되지 않은 일시적인 상태를 다룰 때 유용해요.

<!--- file: App.svelte --->
<script>
	import Child from './Child.svelte';

	let count = $state(0);
</script>

<button onclick={() => (count += 1)}>
	clicks (parent): {count}
</button>

<Child {count} />
<!--- file: Child.svelte --->
<script>
	let { count } = $props();
</script>

<button onclick={() => (count += 1)}>
	clicks (child): {count}
</button>

props를 임시로 재할당하는 건 가능하지만, $bindable 로 선언되지 않은 props를 변형(mutate) 하는 건 하면 안 돼요.

props가 평범한 객체라면 변형해도 효과가 없어요:

<!--- file: Child.svelte --->
<script>
	let { object } = $props();
</script>

<button onclick={() => {
	// 효과 없음
	object.count += 1
}}>
	clicks: {object.count}
</button>

props가 반응형 state proxy라면 변형이 효과를 내긴 하지만, 컴포넌트가 '자기 것이 아닌' 상태를 바꾸는 것이므로 ownership_invalid_mutation 경고가 떠요. 요약하면 props를 변형하지 말 것 — 변경을 알리고 싶으면 콜백 props를 쓰고, 부모·자식이 같은 객체를 공유해야 한다면 $bindable 룬을 써요.

타입 안전성

props에 타입을 붙일 수 있어요. TypeScript에서는:

<script lang="ts">
	let { adjective }: { adjective: string } = $props();
</script>

JSDoc에서는:

<script>
	/** @type {{ adjective: string }} */
	let { adjective } = $props();
</script>

타입 선언을 분리할 수도 있어요:

<script lang="ts">
	interface Props {
		adjective: string;
	}

	let { adjective }: Props = $props();
</script>

컴포넌트를 쓰는 사람이 어떤 props를 넘겨야 하는지 쉽게 알 수 있으니 타입을 붙이는 걸 권장해요. children 같은 snippet props를 노출한다면 'svelte' 에서 import 한 Snippet 인터페이스로 타입을 지정해요.

$props.id()

5.20.0 버전에 추가된 이 룬은 현재 컴포넌트 인스턴스에 고유한 ID를 만들어요. 서버 렌더링된 컴포넌트를 hydrate 할 때 서버·클라이언트 값이 일관돼요. foraria-labelledby 등으로 요소를 연결할 때 유용해요.

<script>
	const uid = $props.id();
</script>

<form>
	<label for="{uid}-firstname">First Name: </label>
	<input id="{uid}-firstname" type="text" />

	<label for="{uid}-lastname">Last Name: </label>
	<input id="{uid}-lastname" type="text" />
</form>

더 알아보기