컴포넌트끼리 소통하지 않으면 화면이 분리됩니다. 이번 글에서는 props와 이벤트 전달을 같은 순서로 연습해 부모-자식 흐름을 단단히 묶습니다.
이번 글에서 새로 나오는 용어
- createEventDispatcher: 자식 컴포넌트가 직접 이벤트를 만들어 부모에게 알리는 Svelte 함수입니다.
- 이벤트 디스패처: 위 함수가 돌려주는 도구로,
dispatch('close')처럼 이름을 붙여 신호를 보냅니다. - 이벤트 포워딩: 중간 컴포넌트가 받은 이벤트를 막지 않고 다시 바깥으로 전달해 버튼 체인을 깨지 않게 만드는 패턴입니다.
개념
props는 부모 컴포넌트가 자식에게 내려주는 값입니다. props가 있으면 같은 카드 컴포넌트를 여러 데이터로 쉽게 반복 렌더링할 수 있습니다. 이벤트 디스패처(dispatcher)는 자식이 부모에게 신호를 올리는 함수입니다. createEventDispatcher는 클릭이나 제출 같은 동작을 사용자 정의 이벤트로 바꿉니다. 이벤트 포워딩은 중간 컴포넌트가 받은 이벤트를 가로막지 않고 다시 바깥으로 흘려보내는 패턴입니다. 이 세 요소가 동시에 있어야 다층 화면에서도 상태 흐름이 끊기지 않습니다.
프레임워크가 몰래 처리하는 규칙
export let으로 선언한 값은 전부 읽기 전용 props입니다. 자식이 그 값을 직접 바꾸려 하면 에러가 나니, 수정이 필요하면 이벤트로 부모에게 요청해야 합니다.createEventDispatcher로 만든 이벤트 이름은 전부 소문자 기준입니다. 부모는 반드시on:close처럼 같은 철자를 써야 신호가 올라옵니다.- 슬롯 안 버튼에
on:click같은 속성이 자동으로 내려오는 것이 아니라,...$$restProps로 명시해야 상위 이벤트가 막히지 않습니다. 모달처럼 껍데기 컴포넌트는 이 규칙을 기억해야 합니다.
이런 “보이지 않는 규칙”을 먼저 기억해 두면 props와 이벤트가 어디서 막히는지 빠르게 진단할 수 있습니다.
코드
모달 컴포넌트로 props, 이벤트 디스패처, 슬롯 흐름을 한 번에 확인합니다. 아래 코드는 모두 브라우저에서 돌아가는 Svelte 컴포넌트이며, 서버와의 통신은 일절 없습니다. 즉, 클릭 이벤트가 일어나면 즉시 같은 파일 안 dispatch가 실행된다는 점을 염두에 두고 코드를 읽어 보세요.
<!-- Modal.svelte -->
<script>
export let open = false;
export let title = "설정";
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
</script>
{#if open}
<section class="modal">
<header>
<h2>{title}</h2>
<button on:click={() => dispatch('close')}>닫기</button>
</header>
<slot />
</section>
{/if}
부모 컴포넌트는 on:close를 감지해 상태를 false로 바꾸면 됩니다. 이렇게 하면 동아리 투표나 시간표 수정 모달에서도 같은 코드를 재사용할 수 있습니다. 버튼에 ...$$restProps를 붙이면 부모가 전달한 on:click 같은 속성도 그대로 흐르게 할 수 있어 이벤트가 막히지 않습니다.
왜 중요한가
학교 앱 화면은 대체로 카드 리스트, 상세 모달, 확인 버튼 같은 반복 UI로 구성됩니다. props와 이벤트를 정확히 이해하면 카드 선택→모달 열기→확인 클릭 같은 흐름을 한 방향으로 유지할 수 있습니다. 또한 이벤트 디스패처는 실시간 알림, 토글, 폼 제출 등 모든 사용자 행동을 부모 상태와 연결하는 기본 도구가 됩니다. 흐름을 명확히 잡으면 6편 이후의 스토어와 페이지 데이터까지 같은 패턴으로 확장할 수 있습니다.
실습
- 따라 하기:
Modal.svelte와CardList.svelte를 만들고 부모에서on:close와onSelect를 처리한다. - 확장하기: 카드 클릭 시 선택된 항목을 헤더에 표시하는 상태를 추가한다.
- 디버깅: 이벤트가 올라오지 않으면
on:접두사와event.detail에 값이 있는지 확인한다. - 완료 기준: 리스트 클릭으로 모달이 열리고 닫기 버튼이 부모 상태를 false로 만든다.
마무리
props로 데이터를 내리고 이벤트로 다시 끌어올리면 컴포넌트가 서로 연결됩니다. 다음 편에서는 이 데이터가 조건에 따라 어떻게 렌더링되는지를 {#if}와 {#each}로 살펴봅니다.
💬 댓글
이 글에 대한 의견을 남겨주세요