폼 하나만으로는 서비스가 완성되지 않습니다. 여러 컴포넌트가 같은 데이터를 바라보게 만드는 store 개념이 필요합니다.
이번 글에서 새로 나오는 용어
- 스토어(store): 값을 보관하고 구독할 수 있는 Svelte 객체로, props 없이도 여러 컴포넌트가 같은 상태를 공유합니다.
- 구독(subscription): 스토어를 관찰해 값이 바뀔 때마다 콜백을 실행하는 동작입니다.
writable/readable: 각각 읽고 쓰기가 가능한 스토어와 읽기 전용 스토어를 만드는 헬퍼 함수입니다.- derived: 다른 스토어 값을 가공해 새 값을 계산하는 함수로, 카운트·배지 같은 파생 데이터를 만들 때 씁니다.
개념
스토어(store)는 값을 보관하고 구독하는 객체입니다. props를 계속 전달하지 않아도 여러 컴포넌트가 같은 스토어를 구독하면 동시에 상태를 볼 수 있습니다. 구독(subscription)은 값이 바뀌면 자동으로 알림을 받는 동작입니다. writable 스토어는 읽기와 쓰기가 모두 가능한 기본 형태입니다. readable은 외부에서 값을 바꾸지 못하게 막고, derived는 기존 스토어 값을 가공해 새 값을 만듭니다. 이 조합으로 알림 배지, 헤더 카운트, 본문 리스트를 같은 데이터에 맞춰 둘 수 있습니다.
프레임워크가 몰래 처리하는 규칙
- 스토어 정의 파일은 모듈 1회 실행 규칙을 따릅니다.
src/lib/stores/*.js파일이 불리면 서버·클라이언트 중 어디서든 한 번만 실행되고, 내보낸 스토어 인스턴스가 공유됩니다. - 컴포넌트에서
$store처럼$접두사를 붙이면 자동으로 구독/해제가 됩니다. 단,<script context="module">나 서버 파일에서는$문법이 안 되니 반드시store.subscribe를 직접 호출해야 합니다. - 스토어는 JS 객체일 뿐이라 서버 DB 권한이 없습니다. 서버 요청이 필요하면
load나 form action에서 데이터를 받아 스토어에 넣는 순서를 지켜야 합니다. 클라이언트가 마음대로 DB에 접근하지 못하도록 하는 안전장치입니다.
코드
알림 센터를 예로 들어 하나의 스토어를 여럿이 공유하는 방법을 살펴봅니다.
// src/lib/stores/notifications.js
const base = writable([]);
export const notifications = {
subscribe: base.subscribe,
push(message) {
base.update((list) => [...list, { id: crypto.randomUUID(), message }]);
},
remove(id) {
base.update((list) => list.filter((item) => item.id !== id));
}
};
export const unreadCount = derived(base, ($list) => $list.length);
컴포넌트에서는 import { notifications, unreadCount } 후 $notifications처럼 앞에 $를 붙여 값을 즉시 사용할 수 있습니다. push와 remove 메서드를 호출하면 대시보드 카드, 헤더 뱃지, 모달 리스트가 동시에 갱신되어 정보가 틀어지지 않습니다.
왜 중요한가
학교 앱은 같은 데이터를 여러 위치에서 보여 줍니다. 과제 알림을 카드와 헤더에 동시에 띄우거나, 출석 현황을 리스트와 팝업에서 함께 보여 줘야 합니다. 스토어가 없으면 props 체인이 길어져 실수하기 쉽습니다. 스토어를 활용하면 데이터 흐름이 한 중심에서 관리되고, 나중에 API 통신을 붙일 때도 로딩 상태를 한 번에 공유할 수 있습니다.
실습
- 따라 하기:
notifications와unreadCount스토어를 만든 뒤 두 컴포넌트에서 동시에 구독한다. - 확장하기:
derived스토어로 읽지 않은 메시지를 3개까지만 보여 주는 배열을 만들어 본다. - 디버깅: 값이 보이지 않으면
$store접두사를 붙였는지와subscribe반환 함수가 제대로 정리되는지 점검한다. - 완료 기준: 알림 추가·삭제 시 헤더와 리스트가 함께 갱신된다.
마무리
스토어를 중심에 두면 화면이 늘어나도 상태 관리가 단순해집니다. 다음 편에서는 이 스토어를 페이지 데이터 로딩과 어떻게 연결하는지 살펴보겠습니다.
💬 댓글
이 글에 대한 의견을 남겨주세요