TypeScript OSDK 객체 집합 구독
객체 집합 구독(Subscription)은 선택한 집합에 객체가 추가되거나 속성이 바뀌거나 객체가 삭제될 때 업데이트를 받는 기능입니다. SDK 2.1부터 GA이며 Developer Console에서 2.1.x 이상의 SDK를 생성하고 호환되는 @osdk/client를 설치해야 합니다.
npm install @osdk/client@^2.1.0
위 명령은 지원 최소 계열을 지정한 예이며 기존 프로젝트가 더 높은 호환 버전을 사용하면 그 버전을 유지합니다. 다른 언어나 연결 제어가 필요하면 WebSocket 프로토콜을 직접 사용할 수 있습니다.
집합과 구독 수
생성 OSDK의 객체 집합에 subscribe를 연결합니다. where로 집합을 좁힐 수 있지만 pivotTo로 만든 집합의 구독은 지원하지 않습니다.
같은 질의에는 활성 구독 하나만 둘 수 있습니다. 같은 객체 집합을 보는 React 컴포넌트 둘이 각각 별도 구독을 만들기보다 앱의 공유 구독을 소비하도록 구성해야 합니다.
subscribe는 unsubscribe 함수를 가진 객체를 반환합니다. 화면이나 기능이 종료되면 호출하여 구독을 닫습니다.
생명주기 콜백
| 콜백 | 의미 |
|---|---|
| onSuccessfulSubscription | 구독이 성립했고 이후 업데이트를 받을 수 있습니다. 이 콜백 이전의 변경은 다른 콜백에 반영되지 않을 수 있습니다. |
| onChange | 객체가 추가, 수정 또는 삭제된 업데이트를 받습니다. |
| onOutOfDate | 최초 구독 초기화와 증분 변경이 부족한 경우에 호출합니다. 집합을 다시 조회해야 합니다. |
| onError | 치명적인 구독 오류입니다. 구독이 닫히거나 성립하지 않으며 인수에 subscriptionClosed와 error를 제공합니다. |
onChange의 update.object는 선택한 속성의 Osdk.Instance이며 update.state는 ADDED_OR_UPDATED 또는 REMOVED입니다. ADDED_OR_UPDATED는 집합에 추가되었거나 속성이 바뀐 경우이며 REMOVED는 삭제 또는 필터에서 빠진 경우입니다.
기본 키 속성과 $primaryKey는 항상 있습니다. 다른 속성이 undefined이면 응답에서 빠진 것일 수 있으므로 그 속성의 실제 값을 삭제하거나 null로 바꾸라는 뜻으로 해석하면 안 됩니다. SDK 생성 뒤 새 속성이 추가되었다면 코드가 아는 속성이 바뀌지 않았어도 업데이트를 받을 수 있습니다.
지리 시계열 참조 속성이 있는 유형은 시계열에 새 값이 생길 때도 업데이트를 받으며 반환 객체의 lastFetchedValue에서 값을 확인할 수 있습니다.
필요한 속성만 받기
두 번째 인수의 properties에 속성 API 이름 배열을 지정합니다. 예를 들어 { properties: ["population"] }는 업데이트 전송량을 줄입니다.
includeRid는 기본 false이며 필요할 때 $rid를 포함하도록 요청할 수 있습니다. 지리 시계열 참조 속성의 새 값 업데이트에는 RID가 없으므로 해당 참조 속성을 구독하는 설정에서 includeRid를 사용할 수 있는지 생성 타입을 확인해야 합니다.
아래는 가상 @subscribe-osdk-example/sdk의 Country를 사용하는 이벤트 수신 helper입니다. Country는 String 기본 키와 Integer population을 가진다고 가정합니다. 이 스키마에서 숫자 필터는 숫자 1,000,000을 사용합니다. Long 속성인 실제 스키마라면 생성 SDK의 문자열 기반 Long 조건을 따라야 합니다.
receive는 실제 앱이 제공하는 이벤트 처리 함수입니다. ready는 구독 성립, snapshot은 전체를 새로 조회한 객체 목록, change는 부분 속성을 포함할 수 있는 변경, stale은 재조회 필요, error는 조회 또는 구독 오류입니다. helper는 알림을 기록하거나 화면 상태를 갱신하는 앱에 이 값을 전달합니다.
import { Client, Osdk } from "@osdk/client";
import { Country } from "@subscribe-osdk-example/sdk";
type CountryFeedEvent =
| { type: "snapshot"; objects: Osdk.Instance<Country>[] }
| { type: "change"; state: "ADDED_OR_UPDATED" | "REMOVED"; object: Osdk.Instance<Country, never, "population"> }
| { type: "ready" }
| { type: "stale" }
| { type: "error"; error: unknown };
export function observeCountries(client: Client, receive: (event: CountryFeedEvent) => void): () => void {
const countries = client(Country).where({ population: { $gte: 1_000_000 } });
let closed = false;
const emit = (event: CountryFeedEvent): void => {
if (!closed) receive(event);
};
const reload = async (): Promise<void> => {
try {
const objects: Osdk.Instance<Country>[] = [];
for await (const country of countries.asyncIter()) objects.push(country);
emit({ type: "snapshot", objects });
} catch (error) {
emit({ type: "error", error });
}
};
const subscription = countries.subscribe({
onSuccessfulSubscription: () => emit({ type: "ready" }),
onChange: update => emit({ type: "change", state: update.state, object: update.object }),
onOutOfDate: () => {
emit({ type: "stale" });
void reload();
},
onError: error => emit({ type: "error", error }),
}, { properties: ["population"] });
return () => {
closed = true;
subscription.unsubscribe();
};
}
이 helper는 구독 이벤트와 새 조회 결과를 앱에 전달합니다. 해당 이벤트들의 처리 순서와 UI 병합은 앱이 결정하며, 객체의 일부 필드가 빠진 변경을 완전한 객체 스냅샷처럼 덮어쓰면 안 됩니다.
전체 집합을 갱신할 때 fetchPage 한 번만 호출하면 첫 페이지만 읽습니다. 위 예제는 asyncIter로 모든 페이지를 읽습니다. 구독 이벤트와 여러 페이지 조회 사이에 원자적인 동일 시점을 제공한다는 의미는 아니므로, 지속적으로 바뀌는 데이터를 표시하는 앱은 재조회 중 상태와 후속 업데이트를 구분해야 합니다.
오류로 닫힌 구독은 앱의 복구 정책에 따라 새로 만들고, 같은 질의에 기존 구독이 남지 않도록 종료를 관리합니다.