TypeScript OSDK 1.x에서 2.x로 이전
이전 대상은 애플리케이션이 사용하는 TypeScript OSDK입니다. Foundry 함수의 TSv1에서 TSv2로 옮기는 작업은 함수 이전에서 별도로 설명합니다.
Developer Console의 /workspace/developer-console/에서 버전 선택기를 2.0 이상으로 바꾸면 생성 SDK에 맞는 문서를 볼 수 있습니다.
생성 코드와 클라이언트 분리
1.x는 객체와 액션별 구현 코드를 생성하고 생성 온톨로지와 클라이언트를 밀접하게 결합합니다. 온톨로지의 크기가 커질수록 생성 코드도 커집니다.
2.x는 온톨로지의 구조 및 메타데이터와 공통 클라이언트 구현을 분리하고 필요한 항목을 지연 로드합니다. 클라이언트 라이브러리의 수정은 온톨로지 SDK를 다시 생성하지 않고 의존성 업데이트로 받을 수 있습니다. 인터페이스와 미디어 집합 같은 새 기능은 2.x를 사용합니다.
1.x 지원을 2.0 출시 뒤 최소 1년 유지한다는 전환 정책이 있었지만, 그 기간 문구만으로 특정 환경의 현재 지원 종료일을 결정해서는 안 됩니다. 사용할 생성기와 런타임의 지원 버전을 Developer Console에서 확인합니다.
이전 순서
- 앱의 SDK versions에서 TypeScript OSDK 2.0 이상의 생성기로 새 버전을 만듭니다.
- 새 생성 패키지와 @osdk/client 및 @osdk/oauth를 설치합니다.
- 새 클라이언트를 만들고 쿼리 및 액션 호출을 변경합니다.
- 객체 타입을 데이터 인스턴스와 구분하고 helper의 입력 및 출력 타입을 맞춥니다.
- 페이지, 링크, 날짜, 집계 및 오류 처리 코드를 바꿉니다.
npm install @osdk/client @osdk/oauth
새 앱은 2.x 생성기가 기본이며 Start developing의 설정을 따릅니다. SDK 패키지 버전과 런타임 버전을 함께 관리합니다.
인증과 Client
1.x는 생성 패키지의 FoundryClient 및 PublicClientAuth를 사용합니다. 2.x는 @osdk/client의 createClient와 @osdk/oauth의 인증 클라이언트를 사용하며 온톨로지 RID를 추가로 전달합니다.
가상 생성 패키지 @my-app/sdk가 $ontologyRid를 내보낸다고 가정합니다. 브라우저에서는 공개 클라이언트를, 비밀 자격 증명을 보관하는 서버에서는 기밀 클라이언트를 구성합니다.
브라우저의 공개 OAuth
import { createClient } from "@osdk/client";
import { createPublicOauthClient } from "@osdk/oauth";
import { $ontologyRid } from "@my-app/sdk";
export function publicClient(clientId: string, foundryUrl: string, redirectUrl: string) {
const auth = createPublicOauthClient(clientId, foundryUrl, redirectUrl);
return createClient(foundryUrl, $ontologyRid, auth);
}
clientId, foundryUrl과 redirectUrl은 해당 Developer Console 앱의 설정입니다. 실제 로그인 및 OAuth 리다이렉트는 앱의 인증 흐름에서 처리합니다. 공개 클라이언트에는 비밀 client secret을 넣지 않습니다.
서버의 기밀 OAuth
import { createClient } from "@osdk/client";
import { createConfidentialOauthClient } from "@osdk/oauth";
import { $ontologyRid } from "@my-app/sdk";
export function confidentialClient(clientId: string, clientSecret: string, foundryUrl: string) {
const auth = createConfidentialOauthClient(clientId, clientSecret, foundryUrl);
return createClient(foundryUrl, $ontologyRid, auth);
}
clientSecret은 서버의 비밀정보 관리 경로에서 전달합니다. 브라우저 번들이나 로그에 노출하면 안 됩니다. 서비스 사용자와 앱 권한은 해당 서버 앱 설정을 따릅니다.
SDK가 $ontologyRid를 내보내지 않으면 알고 있는 RID를 직접 제공합니다. Ontology Manager의 Ontology configuration > Ontology metadata에서 확인할 수 있습니다.
호출 형태
1.x의 legacyClient.ontology.objects.<유형> 경로는 2.x의 client(유형정의) 호출로 바뀝니다. 생성 패키지의 직접 정의 또는 $Objects.<유형> 네임스페이스를 사용할 수 있습니다.
import { Client } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
export function page(client: Client) {
return client(Restaurant).fetchPage({ $pageSize: 30 });
}
정의와 인스턴스 타입
1.x의 객체 타입과 2.x의 Osdk.Instance<ObjectType>는 동일하지 않습니다. 지리공간은 GeoJSON, 날짜와 시각은 ISO 문자열로 바뀌는 등 필드 표현에도 차이가 있습니다.
생성한 2.x ObjectType은 메타데이터 정의입니다. helper가 객체 데이터를 읽으려면 Osdk.Instance<ObjectType>로 받습니다. 페이지 결과도 Page<LegacyObject>에서 PageResult<Osdk.Instance<ObjectType>>로 바뀝니다.
이전 규모에 따라 다음 중 하나를 선택할 수 있습니다.
- 호출과 helper가 적으면 조회를 바꾸면서 legacy 타입 사용을 2.x 인스턴스로 직접 교체합니다.
- UI가 legacy 객체를 넓게 사용하면 먼저 프런트엔드 고유 타입을 정의하고 번역 함수를 둡니다. 이후 2.x 이전 때 번역 경계만 바꾸고 UI 타입을 유지할 수 있습니다.
가상의 Restaurant을 UI 표시용 타입으로 변환하는 예제입니다.
import { Osdk } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
interface RestaurantCard {
id: string;
name: string | undefined;
}
export function toRestaurantCard(restaurant: Osdk.Instance<Restaurant>): RestaurantCard {
return { id: restaurant.$primaryKey, name: restaurant.restaurantName };
}
단일 객체와 RID
fetchOneWithErrors는 { value } 또는 { error }의 Result 포장을 반환하고 fetchOne은 객체를 직접 반환하며 실패를 예외로 전달합니다.
2.x는 기본으로 RID를 포함하지 않습니다. 필요한 조회에 $includeRid: true를 지정하고 인스턴스 타입에도 메타데이터 필드를 나타냅니다.
import { Client, Osdk } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
export async function restaurantRid(client: Client, primaryKey: string): Promise<string> {
const object: Osdk.Instance<Restaurant, "$rid"> =
await client(Restaurant).fetchOne(primaryKey, { $includeRid: true });
return object.$rid;
}
페이지와 전체 로드
1.x의 pageSize는 2.x의 $pageSize입니다. 결과 페이지는 data[0]처럼 배열에서 읽습니다. asyncIter로 모은 배열은 그 배열의 [0]에서 읽으며 .value[0]가 아닙니다.
import { Client, Osdk } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
export async function allRestaurants(client: Client): Promise<Osdk.Instance<Restaurant>[]> {
const result: Osdk.Instance<Restaurant>[] = [];
for await (const restaurant of client(Restaurant).asyncIter()) result.push(restaurant);
return result;
}
전체 배열이 필요 없으면 반복 중에 처리하여 메모리를 줄입니다. 2.x 참조에서 페이지 토큰과 저장소별 로드 한도를 확인합니다.
링크와 필터
1.x에서 객체의 링크 필드를 바로 읽던 코드는 2.x의 $link.<링크 API 이름>으로 바뀝니다. 다중 링크의 fetchPage 옵션도 $pageSize를 사용합니다.
where의 함수형 조건은 조건 객체로 바뀝니다. 예를 들어 이름의 접두사를 비교하면 { restaurantName: { $startsWith: "foo" } }를 사용합니다.
orderBy는 별도 체인에서 fetchPage 또는 asyncIter 옵션의 $orderBy로 이동합니다.
import { Client } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
export function namedRestaurantPage(client: Client) {
return client(Restaurant).where({ restaurantName: { $startsWith: "foo" } }).fetchPage({
$orderBy: { restaurantName: "asc" },
$pageSize: 30,
});
}
집계
1.x는 count().compute(), groupBy와 지표 메서드를 이어서 사용하고 Result.value에서 값을 읽습니다. 2.x는 aggregate의 $select와 $groupBy를 사용하며 반환 구조도 바뀝니다.
| 계산 | 2.x 요청과 결과 |
|---|---|
| 객체 수 | $select: { $count: "unordered" }, 결과.$count |
| 속성 고유 값 근사 개수 | $select: { "restaurantName:approximateDistinct": "unordered" }, 결과.restaurantName.approximateDistinct |
| 두 속성 그룹 | $groupBy: { imageId: "exact", objectLabel: "exact" }, 평평한 행 배열의 각 $group |
지표 값의 정렬은 unordered, asc와 desc이며 둘 이상의 그룹 키를 쓰면 unordered만 사용합니다. 그룹 없는 집계는 객체 하나, 그룹 집계는 행 배열입니다. 입력 필터는 먼저 where를 호출합니다.
import { Client } from "@osdk/client";
import { Restaurant } from "@my-app/sdk";
export async function countRestaurants(client: Client): Promise<number> {
const result = await client(Restaurant).aggregate({ $select: { $count: "unordered" } });
return result.$count;
}
export function countRestaurantNames(client: Client) {
return client(Restaurant).aggregate({
$select: { $count: "unordered" },
$groupBy: { restaurantName: "exact" },
});
}
액션과 쿼리
| 1.x 호출 경로 | 2.x 호출 |
|---|---|
legacyClient.ontology.actions.<액션>(입력) | client(액션).applyAction(입력) |
legacyClient.ontology.batchActions.<액션>(입력) | client(액션).batchApplyAction(입력 배열) |
legacyClient.queries.<쿼리>(입력) | client(쿼리).executeFunction(입력) |
배치에는 단일 입력 객체가 아니라 입력 객체 배열을 전달합니다.
1.x의 ActionExecutionMode와 ReturnEditsMode는 2.x 옵션 $validateOnly 및 $returnEdits로 바뀝니다. 두 값을 동시에 true로 사용할 수 없습니다.
import { Client } from "@osdk/client";
import { addReview } from "@my-app/sdk";
interface ReviewInput {
restaurantId: string;
reviewRating: number;
reviewSummary: string;
}
export function validate(client: Client, input: ReviewInput) {
return client(addReview).applyAction(input, { $validateOnly: true });
}
export function execute(client: Client, input: ReviewInput) {
return client(addReview).applyAction(input, { $returnEdits: true });
}
export function executeBatch(client: Client, inputs: ReviewInput[]) {
return client(addReview).batchApplyAction(inputs, { $returnEdits: true });
}
$validateOnly는 검증 결과를 반환합니다. 실제 실행은 검증 오류에서 예외를 던집니다. 반환 편집의 edits와 largeScaleEdits를 구분하며, raw HTTP 응답의 validation 포장과 SDK의 반환을 혼용하지 않습니다.
날짜와 공통 유틸리티
2.x는 Date와 Timestamp의 구체 클래스를 제공하지 않고 올바른 ISO 문자열을 사용합니다. 예를 들어 Timestamp 비교값은 2010-10-01T00:00:00Z 형식이며 조회 결과도 같은 시각 문자열입니다.
OntologyObject 타입은 2.x에 없고 가장 가까운 공통 타입은 @osdk/api의 OsdkBase입니다. IsOntologyObject도 제거되었으므로 기존 유틸리티를 그대로 가져오지 않습니다. 객체 동일성과 키 기반 맵은 자료형 및 식별의 조건을 따릅니다.