Context API — 컴포넌트 트리에서 데이터 공유하기
Store는 전역, Context는 트리 범위 — 이 차이를 알면 언제 뭘 써야 할지 명확해집니다.
개념 정의
Context API 는 부모 컴포넌트가 자손 컴포넌트에게 명시적인 prop 전달 없이 데이터를 공유하는 메커니즘입니다. React의 Context API와 같은 개념이며, setContext로 설정하고 getContext로 가져옵니다.
기본 사용법
<!-- Parent.svelte -->
<script>
import { setContext } from 'svelte';
import Child from './Child.svelte';
// 키와 값을 지정하여 컨텍스트 설정
setContext('theme', {
primary: '#ff3e00',
secondary: '#676778',
mode: 'light',
});
</script>
<Child />
<!-- Child.svelte (또는 더 깊은 자손) -->
<script>
import { getContext } from 'svelte';
// 같은 키로 컨텍스트 가져오기
const theme = getContext('theme');
</script>
<div style:color={theme.primary}>
테마 색상이 적용됩니다
</div>
hasContext — 컨텍스트 존재 확인
<script>
import { getContext, hasContext } from 'svelte';
// 컨텍스트가 있는지 확인
const hasTheme = hasContext('theme');
const theme = hasTheme ? getContext('theme') : { primary: '#333' };
</script>
타입 안전한 Context
// context.ts — 컨텍스트 키와 타입을 중앙 관리
import { setContext, getContext } from 'svelte';
interface ThemeContext {
primary: string;
secondary: string;
mode: 'light' | 'dark';
}
const THEME_KEY = Symbol('theme');
export function setThemeContext(theme: ThemeContext) {
setContext(THEME_KEY, theme);
}
export function getThemeContext(): ThemeContext {
return getContext<ThemeContext>(THEME_KEY);
}
<!-- Provider.svelte -->
<script>
import { setThemeContext } from './context';
setThemeContext({
primary: '#ff3e00',
secondary: '#676778',
mode: 'light',
});
</script>
<slot />
<!-- Consumer.svelte -->
<script>
import { getThemeContext } from './context';
const theme = getThemeContext();
// TypeScript가 theme의 타입을 알고 있음!
</script>
반응형 Context
기본 Context 값은 반응형이 아닙니다. 반응형으로 만들려면 $state를 사용합니다.
<!-- ThemeProvider.svelte -->
<script>
import { setContext } from 'svelte';
let mode = $state('light');
// $state 객체를 컨텍스트로 전달하면 반응형!
setContext('theme', {
get mode() { return mode; },
toggle() { mode = mode === 'light' ? 'dark' : 'light'; },
});
</script>
{@render children()}
<!-- DeepChild.svelte -->
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<p>현재 모드: {theme.mode}</p>
<button onclick={theme.toggle}>테마 전환</button>
Store + Context 조합
Store를 Context에 넣으면 트리 범위의 반응형 상태를 깔끔하게 구현할 수 있습니다.
// cartContext.js
import { writable } from 'svelte/store';
import { setContext, getContext } from 'svelte';
const CART_KEY = Symbol('cart');
export function createCartContext() {
const cart = writable([]);
const context = {
cart,
addItem(item) {
cart.update(items => [...items, item]);
},
removeItem(id) {
cart.update(items => items.filter(i => i.id !== id));
},
get total() {
let t = 0;
cart.subscribe(items => {
t = items.reduce((sum, i) => sum + i.price, 0);
})();
return t;
},
};
setContext(CART_KEY, context);
return context;
}
export function getCartContext() {
return getContext(CART_KEY);
}
Context vs Store vs Props
| 특성 | Props | Context | Store |
|---|---|---|---|
| 범위 | 부모→자식 직접 | 컴포넌트 트리 | 전역 |
| 반응성 | 자동 | 수동 설정 필요 | 자동 |
| 인스턴스 | N/A | 트리별 독립 | 싱글턴 |
| 용도 | 직접 연결된 컴포넌트 | 테마, 로케일 등 트리 범위 | 인증, 전역 설정 |
**핵심 차이 **: Store는 앱 전체에서 하나의 인스턴스를 공유합니다. Context는 같은 컴포넌트가 다른 트리에 있으면 다른 Context 값을 받을 수 있습니다.
<!-- 같은 컴포넌트가 다른 Context를 받는 예시 -->
<script>
import ThemeProvider from './ThemeProvider.svelte';
import Card from './Card.svelte';
</script>
<ThemeProvider theme="light">
<Card /> <!-- 라이트 테마 -->
</ThemeProvider>
<ThemeProvider theme="dark">
<Card /> <!-- 다크 테마 — 같은 Card지만 다른 테마 -->
</ThemeProvider>
면접 포인트
- "Context가 Store보다 나은 경우는?": 같은 컴포넌트를 다른 설정으로 여러 번 사용해야 할 때입니다. 예를 들어 하나의 페이지에 라이트 테마 섹션과 다크 테마 섹션이 공존하는 경우, Store는 전역이라 구분할 수 없지만 Context는 트리별로 다른 값을 제공합니다.
- "setContext는 어디서 호출해야 하나요?": 컴포넌트 초기화 시점에만 호출 가능합니다.
onMount안이나 이벤트 핸들러에서는 호출할 수 없습니다.
정리
Context API는 "이 트리 아래에서는 이 데이터를 쓰겠다"를 선언하는 도구입니다. Props는 직접 연결, Store는 전역, Context는 트리 범위 — 세 가지 데이터 공유 방식을 상황에 맞게 고르면 깔끔한 아키텍처가 나옵니다.
댓글 로딩 중...