Angular
Angular Signals는 작은 adapter를 통해 외부 상태를 읽을 수 있습니다. @ilokesto/store를 source of truth로 두고, snapshot을 writable Angular signal에 반영한 뒤 DestroyRef로 cleanup을 등록하세요.
signal adapter 만들기
import { DestroyRef, inject, signal, type Signal } from "@angular/core";
import { Store } from "@ilokesto/store";
export function useStoreSignal<T>(store: Store<T>): Signal<Readonly<T>> {
const destroyRef = inject(DestroyRef);
const state = signal<Readonly<T>>(store.getState());
const unsubscribe = store.subscribe(() => {
state.set(store.getState());
});
destroyRef.onDestroy(unsubscribe);
return state.asReadonly();
}이 helper는 Angular injection context 안에서 호출하세요. 예를 들어 component field initializer, constructor, directive, Angular DI가 만든 service가 해당됩니다. DestroyRef는 그 context가 파괴될 때 cleanup callback을 실행합니다.
컴포넌트에서 사용하기
import { Component } from "@angular/core";
import { Store } from "@ilokesto/store";
import { useStoreSignal } from "./use-store-signal";
type CounterState = { count: number };
const counterStore = new Store<CounterState>({ count: 0 });
@Component({
selector: "app-counter-button",
template: `
<button type="button" (click)="increment()">
Count: {{ counter().count }}
</button>
`,
})
export class CounterButtonComponent {
readonly counter = useStoreSignal(counterStore);
increment(): void {
counterStore.setState((prev) => ({ count: prev.count + 1 }));
}
}Angular에서는 signal을 함수처럼 호출해서 읽습니다. template과 class 코드에서 counter()를 사용하세요.
Service가 소유하는 store
앱 전체 상태라면 Store<T>와 action method를 Angular service에 두세요. 컴포넌트에는 readonly signal을 노출하고 write는 이름 있는 method 뒤에 둡니다.
import { Injectable } from "@angular/core";
import { Store } from "@ilokesto/store";
import { useStoreSignal } from "./use-store-signal";
type SessionState = { userId: string | null };
@Injectable({ providedIn: "root" })
export class SessionStoreService {
private readonly store = new Store<SessionState>({ userId: null });
readonly state = useStoreSignal(this.store);
signIn(userId: string): void {
this.store.setState({ userId });
}
signOut(): void {
this.store.setState({ userId: null });
}
}root에 제공된 service의 cleanup은 root injector와 함께 실행됩니다. 컴포넌트 범위 상태라면 service를 컴포넌트에 provide하거나 컴포넌트에서 adapter를 직접 호출하세요.
RxJS 참고
Angular는 Observable을 위한 toSignal() 같은 RxJS interop도 제공합니다. @ilokesto/store는 Observable이 아니므로 위의 직접 signal() adapter가 가장 작은 연결 방식입니다. 앱이 이미 RxJS를 표준으로 사용한다면 store.subscribe()를 먼저 Observable로 감싼 뒤 Angular의 toSignal()을 사용할 수 있습니다.