$props: 컴포넌트에 값 전달하기
$props: 컴포넌트에 값 전달하기
컴포넌트는 화면의 재사용 가능한 조각인데, 조각마다 서로 다른 값을 받아야 할 때가 있어요. 예를 들어 같은 카드 컴포넌트에 "cool", "warm" 같은 수식어를 다르게 넣고 싶은 상황이죠. 컴포넌트에 전달하는 입력값을 props(properties 의 약자) 라고 해요. HTML 요소에 속성을 넘기듯, 컴포넌트에도 props를 넘겨요.
받는 쪽에서는 $props 룬으로 props를 꺼내 써요. 개별 값 하나씩 받는 구조라서, 어떤 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 할 때 서버·클라이언트 값이 일관돼요. for 나 aria-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>
더 알아보기
- 공식 문서 (1차): $props
- 이어지는 개념: 반응형 상태($state) · 스토어 · 컨텍스트