@apps-in-toss/native-modules 0.0.0-dev.1752115036458 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridges-meta.json +36 -13
- package/dist/index.cjs +256 -120
- package/dist/index.d.cts +697 -229
- package/dist/index.d.ts +697 -229
- package/dist/index.js +233 -103
- package/package.json +7 -8
- package/src/AppsInTossModule/constants.ts +6 -0
- package/src/AppsInTossModule/native-event-emitter/appsInTossEvent.ts +13 -0
- package/src/AppsInTossModule/native-event-emitter/contactsViral.ts +140 -0
- package/src/AppsInTossModule/native-event-emitter/event-plugins/EntryMessageExitedEvent.ts +10 -0
- package/src/AppsInTossModule/native-event-emitter/event-plugins/UpdateLocationEvent.ts +60 -0
- package/src/AppsInTossModule/native-event-emitter/index.ts +5 -0
- package/src/AppsInTossModule/native-event-emitter/internal/AppBridgeCallbackEvent.ts +45 -0
- package/src/AppsInTossModule/native-event-emitter/internal/VisibilityChangedByTransparentServiceWebEvent.ts +50 -0
- package/src/AppsInTossModule/native-event-emitter/internal/appBridge.spec.ts +135 -0
- package/src/AppsInTossModule/native-event-emitter/internal/appBridge.ts +79 -0
- package/src/AppsInTossModule/native-event-emitter/internal/onVisibilityChangedByTransparentServiceWeb.ts +20 -0
- package/src/AppsInTossModule/native-event-emitter/nativeEventEmitter.ts +35 -0
- package/src/AppsInTossModule/native-event-emitter/startUpdateLocation.ts +98 -0
- package/src/AppsInTossModule/native-event-emitter/types.ts +4 -0
- package/src/AppsInTossModule/native-modules/AppsInTossModule.ts +89 -0
- package/src/AppsInTossModule/native-modules/ads/googleAdMob.ts +690 -0
- package/src/AppsInTossModule/native-modules/ads/types.ts +106 -0
- package/src/AppsInTossModule/native-modules/appLogin.ts +29 -0
- package/src/AppsInTossModule/native-modules/checkoutPayment.ts +80 -0
- package/src/AppsInTossModule/native-modules/eventLog.spec.ts +300 -0
- package/src/AppsInTossModule/native-modules/eventLog.ts +77 -0
- package/src/AppsInTossModule/native-modules/fetchAlbumPhotos.ts +88 -0
- package/src/AppsInTossModule/native-modules/fetchContacts.ts +121 -0
- package/src/AppsInTossModule/native-modules/getClipboardText.ts +47 -0
- package/src/AppsInTossModule/native-modules/getCurrentLocation.ts +65 -0
- package/src/AppsInTossModule/native-modules/getDeviceId.ts +33 -0
- package/src/AppsInTossModule/native-modules/getGameCenterGameProfile.ts +68 -0
- package/src/AppsInTossModule/native-modules/getOperationalEnvironment.ts +37 -0
- package/src/AppsInTossModule/native-modules/getPermission.ts +58 -0
- package/src/AppsInTossModule/native-modules/getTossAppVersion.ts +33 -0
- package/src/AppsInTossModule/native-modules/getTossShareLink.ts +39 -0
- package/src/AppsInTossModule/native-modules/iap.ts +213 -0
- package/src/AppsInTossModule/native-modules/index.ts +86 -0
- package/src/AppsInTossModule/native-modules/isMinVersionSupported.spec.ts +190 -0
- package/src/AppsInTossModule/native-modules/isMinVersionSupported.ts +68 -0
- package/src/AppsInTossModule/native-modules/openCamera.ts +81 -0
- package/src/AppsInTossModule/native-modules/openGameCenterLeaderboard.ts +44 -0
- package/src/AppsInTossModule/native-modules/openPermissionDialog.ts +54 -0
- package/src/AppsInTossModule/native-modules/requestPermission.ts +63 -0
- package/src/AppsInTossModule/native-modules/saveBase64Data.ts +57 -0
- package/src/AppsInTossModule/native-modules/setClipboardText.ts +39 -0
- package/src/AppsInTossModule/native-modules/setDeviceOrientation.ts +74 -0
- package/src/AppsInTossModule/native-modules/storage.ts +100 -0
- package/src/AppsInTossModule/native-modules/submitGameCenterLeaderBoardScore.ts +74 -0
- package/src/AppsInTossModule/native-modules/tossCore.ts +29 -0
- package/src/BedrockModule/native-modules/core/BedrockCoreModule.ts +8 -0
- package/src/BedrockModule/native-modules/index.ts +4 -0
- package/src/BedrockModule/native-modules/natives/BedrockModule.ts +20 -0
- package/src/BedrockModule/native-modules/natives/closeView.ts +25 -0
- package/src/BedrockModule/native-modules/natives/generateHapticFeedback/index.ts +27 -0
- package/src/BedrockModule/native-modules/natives/generateHapticFeedback/types.ts +38 -0
- package/src/BedrockModule/native-modules/natives/getLocale.ts +46 -0
- package/src/BedrockModule/native-modules/natives/getNetworkStatus/index.ts +59 -0
- package/src/BedrockModule/native-modules/natives/getNetworkStatus/types.ts +1 -0
- package/src/BedrockModule/native-modules/natives/getPlatformOS.ts +37 -0
- package/src/BedrockModule/native-modules/natives/getSchemeUri.ts +27 -0
- package/src/BedrockModule/native-modules/natives/index.ts +11 -0
- package/src/BedrockModule/native-modules/natives/openURL.ts +40 -0
- package/src/BedrockModule/native-modules/natives/setIosSwipeGestureEnabled.ts +43 -0
- package/src/BedrockModule/native-modules/natives/setScreenAwakeMode.ts +66 -0
- package/src/BedrockModule/native-modules/natives/setSecureScreen.ts +31 -0
- package/src/BedrockModule/native-modules/natives/share.ts +36 -0
- package/src/async-bridges.ts +3 -0
- package/src/event-bridges.ts +2 -0
- package/src/index.ts +16 -0
- package/src/types.ts +108 -0
- package/src/utils/compareVersion.spec.ts +176 -0
- package/src/utils/compareVersion.ts +104 -0
- package/src/utils/generateUUID.ts +5 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @public
|
|
3
|
+
* @category 광고
|
|
4
|
+
* @name AdMobFullScreenEvent
|
|
5
|
+
* @description 앱 화면을 덮는 광고(예: 전면 광고, 보상형 광고)를 사용하는 경우에 발생하는 이벤트 타입이에요.
|
|
6
|
+
* @example
|
|
7
|
+
* ### 광고 이벤트 처리하기
|
|
8
|
+
* ```ts
|
|
9
|
+
* function handleEvent(event: AdMobFullScreenEvent) {
|
|
10
|
+
* switch (event.type) {
|
|
11
|
+
* case 'clicked':
|
|
12
|
+
* console.log('광고가 클릭됐어요.');
|
|
13
|
+
* break;
|
|
14
|
+
*
|
|
15
|
+
* case 'dismissed':
|
|
16
|
+
* console.log('광고가 닫혔어요.');
|
|
17
|
+
* break;
|
|
18
|
+
*
|
|
19
|
+
* case 'failedToShow':
|
|
20
|
+
* console.log('광고가 보여지지 않았어요.');
|
|
21
|
+
* break;
|
|
22
|
+
*
|
|
23
|
+
* case 'impression':
|
|
24
|
+
* console.log('광고가 노출됐어요.');
|
|
25
|
+
* break;
|
|
26
|
+
*
|
|
27
|
+
* case 'show':
|
|
28
|
+
* console.log('광고가 보여졌어요.');
|
|
29
|
+
* break;
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export type AdMobFullScreenEvent = AdClicked | AdDismissed | AdFailedToShow | AdImpression | AdShow;
|
|
35
|
+
|
|
36
|
+
type AdClicked = { type: 'clicked' };
|
|
37
|
+
type AdDismissed = { type: 'dismissed' };
|
|
38
|
+
type AdFailedToShow = { type: 'failedToShow' };
|
|
39
|
+
type AdImpression = { type: 'impression' };
|
|
40
|
+
type AdShow = { type: 'show' };
|
|
41
|
+
|
|
42
|
+
export interface AdMobHandlerParams<Options = void, Event = AdMobFullScreenEvent> {
|
|
43
|
+
options: Options;
|
|
44
|
+
onEvent: (event: Event) => void;
|
|
45
|
+
onError: (error: unknown) => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @public
|
|
50
|
+
* @category 광고
|
|
51
|
+
* @name ResponseInfo
|
|
52
|
+
* @description 광고 로드 응답 정보를 담고 있는 객체예요.
|
|
53
|
+
* @property {Array<AdNetworkResponseInfo>} adNetworkInfoArray 광고 네트워크 응답 정보 배열예요.
|
|
54
|
+
* @property {AdNetworkResponseInfo | null} loadedAdNetworkInfo 로드된 광고 네트워크 응답 정보예요.
|
|
55
|
+
* @property {string | null} responseId 광고 응답 ID예요.
|
|
56
|
+
*/
|
|
57
|
+
export interface ResponseInfo {
|
|
58
|
+
adNetworkInfoArray: Array<AdNetworkResponseInfo>;
|
|
59
|
+
loadedAdNetworkInfo: AdNetworkResponseInfo | null;
|
|
60
|
+
responseId: string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
* @category 광고
|
|
66
|
+
* @name AdNetworkResponseInfo
|
|
67
|
+
* @description 광고 네트워크 응답 정보를 담고 있는 객체예요.
|
|
68
|
+
* @property {string} adSourceId 광고 소스 ID예요.
|
|
69
|
+
* @property {string} adSourceName 광고 소스 이름이예요.
|
|
70
|
+
* @property {string} adSourceInstanceId 광고 소스 인스턴스 ID예요.
|
|
71
|
+
* @property {string} adSourceInstanceName 광고 소스 인스턴스 이름이예요.
|
|
72
|
+
* @property {string | null} adNetworkClassName 광고 네트워크 클래스 이름이예요.
|
|
73
|
+
*/
|
|
74
|
+
export interface AdNetworkResponseInfo {
|
|
75
|
+
adSourceId: string;
|
|
76
|
+
adSourceName: string;
|
|
77
|
+
adSourceInstanceId: string;
|
|
78
|
+
adSourceInstanceName: string;
|
|
79
|
+
adNetworkClassName: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @public
|
|
84
|
+
* @category 광고
|
|
85
|
+
* @name InterstitialAd
|
|
86
|
+
* @description 전면 광고의 ID와 응답 정보를 담고 있는 객체예요. 광고를 로드한 뒤, 관련 정보를 확인할 때 유용해요.
|
|
87
|
+
* @property {string} adUnitId 광고 ID예요.
|
|
88
|
+
* @property {ResponseInfo} responseInfo 광고 로드 응답 정보예요. 자세한 내용은 [ResponseInfo](/react-native/reference/native-modules/광고/ResponseInfo.html)를 참고하세요.
|
|
89
|
+
*/
|
|
90
|
+
export interface InterstitialAd {
|
|
91
|
+
adUnitId: string;
|
|
92
|
+
responseInfo: ResponseInfo;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @public
|
|
97
|
+
* @category 광고
|
|
98
|
+
* @name RewardedAd
|
|
99
|
+
* @description 보상형 광고의 ID와 응답 정보를 담고 있는 객체예요. 광고를 로드한 뒤, 관련 정보를 확인할 때 유용해요.
|
|
100
|
+
* @property {string} adUnitId 광고 ID예요.
|
|
101
|
+
* @property {ResponseInfo} responseInfo 광고 로드 응답 정보예요. 자세한 내용은 [ResponseInfo](/react-native/reference/native-modules/광고/ResponseInfo.html)를 참고하세요.
|
|
102
|
+
*/
|
|
103
|
+
export interface RewardedAd {
|
|
104
|
+
adUnitId: string;
|
|
105
|
+
responseInfo: ResponseInfo;
|
|
106
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AppsInTossModule } from './AppsInTossModule';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* @category 로그인
|
|
6
|
+
* @name appLogin
|
|
7
|
+
* @description 토스 인증으로 로그인해요. 로그인이 완료되면 다시 토스 앱으로 이동해요.
|
|
8
|
+
* @example
|
|
9
|
+
*
|
|
10
|
+
* ### 토스 인증을 통해 로그인을 하는 예제
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { Button } from 'react-native';
|
|
14
|
+
* import { appLogin } from '@apps-in-toss/framework';
|
|
15
|
+
*
|
|
16
|
+
* function Page() {
|
|
17
|
+
* const handleLogin = async () => {
|
|
18
|
+
* const { authorizationCode, referrer } = await appLogin();
|
|
19
|
+
*
|
|
20
|
+
* // 획득한 인가 코드(`authorizationCode`)와 `referrer`를 서버로 전달해요.
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* return <Button title="로그인" onPress={handleLogin} />;
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export async function appLogin(): Promise<{ authorizationCode: string; referrer: 'DEFAULT' | 'SANDBOX' }> {
|
|
28
|
+
return AppsInTossModule.appLogin({});
|
|
29
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { AppsInTossModule } from './AppsInTossModule';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
* @category 토스페이
|
|
6
|
+
* @name CheckoutPaymentOptions
|
|
7
|
+
* @description 토스페이 결제창을 띄울 때 필요한 옵션이에요.
|
|
8
|
+
* @property {string} payToken 결제 토큰이에요.
|
|
9
|
+
*/
|
|
10
|
+
export interface CheckoutPaymentOptions {
|
|
11
|
+
/**
|
|
12
|
+
* 결제 토큰이에요.
|
|
13
|
+
*/
|
|
14
|
+
payToken: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
* @category 토스페이
|
|
20
|
+
* @name CheckoutPaymentResult
|
|
21
|
+
* @description 토스페이 결제창에서 사용자가 인증에 성공했는지 여부예요.
|
|
22
|
+
* @property {boolean} success 인증이 성공했는지 여부예요.
|
|
23
|
+
* @property {string} [reason] 인증이 실패했을 경우의 이유예요.
|
|
24
|
+
*/
|
|
25
|
+
export interface CheckoutPaymentResult {
|
|
26
|
+
/**
|
|
27
|
+
* 인증이 성공했는지 여부예요.
|
|
28
|
+
*/
|
|
29
|
+
success: boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 인증이 실패했을 경우의 이유예요.
|
|
33
|
+
*/
|
|
34
|
+
reason?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @public
|
|
39
|
+
* @category 토스페이
|
|
40
|
+
* @name checkoutPayment
|
|
41
|
+
* @description 토스페이 결제창을 띄우고, 사용자 인증을 수행해요. 인증이 완료되면 성공 여부를 반환해요.
|
|
42
|
+
*
|
|
43
|
+
* 이 함수는 결제창을 통해 사용자 인증만 해요. 실제 결제 처리는 인증 성공 후 서버에서 별도로 해야 해요.
|
|
44
|
+
*
|
|
45
|
+
* @param {CheckoutPaymentOptions} options 결제창을 띄울 때 필요한 옵션이에요.
|
|
46
|
+
* @returns {Promise<CheckoutPaymentResult>} 인증 성공 여부를 포함한 결과를 반환해요.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
*
|
|
50
|
+
* ### 토스페이 결제창 띄우고 인증 처리하기
|
|
51
|
+
*
|
|
52
|
+
* ```tsx
|
|
53
|
+
* import { TossPay } from '@apps-in-toss/framework';
|
|
54
|
+
*
|
|
55
|
+
* async function handlePayment() {
|
|
56
|
+
* try {
|
|
57
|
+
* // 실제 구현 시 결제 생성 역할을 하는 API 엔드포인트로 대체해주세요.
|
|
58
|
+
* const { payToken } = await fetch('/my-api/payment/create').then(res => res.json());
|
|
59
|
+
*
|
|
60
|
+
* const { success, reason } = await TossPay.checkoutPayment({ payToken });
|
|
61
|
+
*
|
|
62
|
+
* if (success) {
|
|
63
|
+
* // 실제 구현 시 결제를 실행하는 API 엔드포인트로 대체해주세요.
|
|
64
|
+
* await fetch('/my-api/payment/execute', {
|
|
65
|
+
* method: 'POST',
|
|
66
|
+
* body: JSON.stringify({ payToken }),
|
|
67
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
* });
|
|
69
|
+
* } else {
|
|
70
|
+
* console.log('인증 실패:', reason);
|
|
71
|
+
* }
|
|
72
|
+
* } catch (error) {
|
|
73
|
+
* console.error('결제 인증 중 오류가 발생했어요:', error);
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export async function checkoutPayment(options: CheckoutPaymentOptions): Promise<CheckoutPaymentResult> {
|
|
79
|
+
return AppsInTossModule.checkoutPayment({ params: options });
|
|
80
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { describe, beforeEach, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('eventLog', () => {
|
|
4
|
+
const mockEventLog = vi.fn();
|
|
5
|
+
let originalConsoleLog: typeof console.log;
|
|
6
|
+
|
|
7
|
+
// 테스트 전에 모킹 설정
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// 각 테스트 전 모듈 캐시 초기화
|
|
10
|
+
vi.resetModules();
|
|
11
|
+
|
|
12
|
+
// 기본 모킹 설정
|
|
13
|
+
vi.doMock('react-native', () => ({
|
|
14
|
+
NativeModules: {
|
|
15
|
+
AppsInTossModule: {
|
|
16
|
+
eventLog: mockEventLog,
|
|
17
|
+
tossAppVersion: '5.208.0',
|
|
18
|
+
operationalEnvironment: 'toss',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
Platform: {
|
|
22
|
+
OS: 'ios',
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
originalConsoleLog = console.log;
|
|
27
|
+
console.log = vi.fn();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
console.log = originalConsoleLog;
|
|
32
|
+
vi.restoreAllMocks();
|
|
33
|
+
mockEventLog.mockReset();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('샌드박스 환경에서는 콘솔에 로그를 출력하고 네이티브 모듈을 호출하지 않아야 한다', async () => {
|
|
37
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
38
|
+
AppsInTossModule: {
|
|
39
|
+
tossAppVersion: '5.208.0',
|
|
40
|
+
operationalEnvironment: 'sandbox',
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const { eventLog } = await import('./eventLog.js');
|
|
45
|
+
|
|
46
|
+
const params = {
|
|
47
|
+
log_name: 'test_log',
|
|
48
|
+
log_type: 'info' as const,
|
|
49
|
+
params: {
|
|
50
|
+
key1: 'value1',
|
|
51
|
+
key2: 123,
|
|
52
|
+
key3: true,
|
|
53
|
+
key4: undefined,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
await eventLog(params);
|
|
58
|
+
|
|
59
|
+
expect(console.log).toHaveBeenCalledWith('[eventLogDebug]', {
|
|
60
|
+
log_name: 'test_log',
|
|
61
|
+
log_type: 'info',
|
|
62
|
+
params: {
|
|
63
|
+
key1: 'value1',
|
|
64
|
+
key2: '123',
|
|
65
|
+
key3: 'true',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
expect(mockEventLog).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('토스 환경에서는 네이티브 모듈을 호출해야 한다', async () => {
|
|
72
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
73
|
+
AppsInTossModule: {
|
|
74
|
+
eventLog: mockEventLog,
|
|
75
|
+
tossAppVersion: '5.208.0',
|
|
76
|
+
operationalEnvironment: 'toss',
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
const { eventLog } = await import('./eventLog.js');
|
|
81
|
+
|
|
82
|
+
const params = {
|
|
83
|
+
log_name: 'test_log',
|
|
84
|
+
log_type: 'error' as const,
|
|
85
|
+
params: {
|
|
86
|
+
key1: 'value1',
|
|
87
|
+
key2: 123,
|
|
88
|
+
key3: true,
|
|
89
|
+
key4: null,
|
|
90
|
+
key5: undefined,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
await eventLog(params);
|
|
95
|
+
|
|
96
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
97
|
+
log_name: 'test_log',
|
|
98
|
+
log_type: 'error',
|
|
99
|
+
params: {
|
|
100
|
+
key1: 'value1',
|
|
101
|
+
key2: '123',
|
|
102
|
+
key3: 'true',
|
|
103
|
+
key4: 'null',
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
expect(console.log).not.toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('undefined 값은 정규화된 파라미터에서 제외되어야 한다', async () => {
|
|
110
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
111
|
+
AppsInTossModule: {
|
|
112
|
+
eventLog: mockEventLog,
|
|
113
|
+
tossAppVersion: '5.208.0',
|
|
114
|
+
operationalEnvironment: 'toss',
|
|
115
|
+
},
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
const { eventLog } = await import('./eventLog.js');
|
|
119
|
+
|
|
120
|
+
const params = {
|
|
121
|
+
log_name: 'test_log',
|
|
122
|
+
log_type: 'debug' as const,
|
|
123
|
+
params: {
|
|
124
|
+
key1: 'value1',
|
|
125
|
+
key2: undefined,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
await eventLog(params);
|
|
130
|
+
|
|
131
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
132
|
+
log_name: 'test_log',
|
|
133
|
+
log_type: 'debug',
|
|
134
|
+
params: {
|
|
135
|
+
key1: 'value1',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('모든 값은 문자열로 변환되어야 한다', async () => {
|
|
141
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
142
|
+
AppsInTossModule: {
|
|
143
|
+
eventLog: mockEventLog,
|
|
144
|
+
tossAppVersion: '5.208.0',
|
|
145
|
+
operationalEnvironment: 'toss',
|
|
146
|
+
},
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
const { eventLog } = await import('./eventLog.js');
|
|
150
|
+
|
|
151
|
+
const params = {
|
|
152
|
+
log_name: 'test_log',
|
|
153
|
+
log_type: 'warn' as const,
|
|
154
|
+
params: {
|
|
155
|
+
number: 123,
|
|
156
|
+
boolean: false,
|
|
157
|
+
nullValue: null,
|
|
158
|
+
symbol: Symbol('test').toString(),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
await eventLog(params);
|
|
163
|
+
|
|
164
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
165
|
+
log_name: 'test_log',
|
|
166
|
+
log_type: 'warn',
|
|
167
|
+
params: {
|
|
168
|
+
number: '123',
|
|
169
|
+
boolean: 'false',
|
|
170
|
+
nullValue: 'null',
|
|
171
|
+
symbol: 'Symbol(test)',
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('빈 객체가 전달되면 빈 객체를 반환해야 한다', async () => {
|
|
177
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
178
|
+
AppsInTossModule: {
|
|
179
|
+
eventLog: mockEventLog,
|
|
180
|
+
tossAppVersion: '5.208.0',
|
|
181
|
+
operationalEnvironment: 'toss',
|
|
182
|
+
},
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
const { eventLog } = await import('./eventLog.js');
|
|
186
|
+
|
|
187
|
+
const params = {
|
|
188
|
+
log_name: 'test_log',
|
|
189
|
+
log_type: 'info' as const,
|
|
190
|
+
params: {},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
await eventLog(params);
|
|
194
|
+
|
|
195
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
196
|
+
log_name: 'test_log',
|
|
197
|
+
log_type: 'info',
|
|
198
|
+
params: {},
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('여러 개의 undefined 값이 있는 경우 모두 제외되어야 한다', async () => {
|
|
203
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
204
|
+
AppsInTossModule: {
|
|
205
|
+
eventLog: mockEventLog,
|
|
206
|
+
tossAppVersion: '5.208.0',
|
|
207
|
+
operationalEnvironment: 'toss',
|
|
208
|
+
},
|
|
209
|
+
}));
|
|
210
|
+
|
|
211
|
+
const { eventLog } = await import('./eventLog.js');
|
|
212
|
+
|
|
213
|
+
const params = {
|
|
214
|
+
log_name: 'test_log',
|
|
215
|
+
log_type: 'debug' as const,
|
|
216
|
+
params: {
|
|
217
|
+
key1: undefined,
|
|
218
|
+
key2: undefined,
|
|
219
|
+
key3: 'value3',
|
|
220
|
+
key4: undefined,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await eventLog(params);
|
|
225
|
+
|
|
226
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
227
|
+
log_name: 'test_log',
|
|
228
|
+
log_type: 'debug',
|
|
229
|
+
params: {
|
|
230
|
+
key3: 'value3',
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('숫자 0과 빈 문자열은 정상적으로 문자열로 변환되어야 한다', async () => {
|
|
236
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
237
|
+
AppsInTossModule: {
|
|
238
|
+
eventLog: mockEventLog,
|
|
239
|
+
tossAppVersion: '5.208.0',
|
|
240
|
+
operationalEnvironment: 'toss',
|
|
241
|
+
},
|
|
242
|
+
}));
|
|
243
|
+
|
|
244
|
+
const { eventLog } = await import('./eventLog.js');
|
|
245
|
+
|
|
246
|
+
const params = {
|
|
247
|
+
log_name: 'test_log',
|
|
248
|
+
log_type: 'info' as const,
|
|
249
|
+
params: {
|
|
250
|
+
zeroNumber: 0,
|
|
251
|
+
emptyString: '',
|
|
252
|
+
falseValue: false,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
await eventLog(params);
|
|
257
|
+
|
|
258
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
259
|
+
log_name: 'test_log',
|
|
260
|
+
log_type: 'info',
|
|
261
|
+
params: {
|
|
262
|
+
zeroNumber: '0',
|
|
263
|
+
emptyString: '',
|
|
264
|
+
falseValue: 'false',
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('복잡한 심볼 값도 문자열로 적절히 변환되어야 한다', async () => {
|
|
270
|
+
vi.doMock('./AppsInTossModule.js', () => ({
|
|
271
|
+
AppsInTossModule: {
|
|
272
|
+
eventLog: mockEventLog,
|
|
273
|
+
tossAppVersion: '5.208.0',
|
|
274
|
+
operationalEnvironment: 'toss',
|
|
275
|
+
},
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
const { eventLog } = await import('./eventLog.js');
|
|
279
|
+
|
|
280
|
+
const testSymbol = Symbol('복잡한_테스트');
|
|
281
|
+
|
|
282
|
+
const params = {
|
|
283
|
+
log_name: 'test_log',
|
|
284
|
+
log_type: 'debug' as const,
|
|
285
|
+
params: {
|
|
286
|
+
symbolValue: testSymbol.toString(),
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
await eventLog(params);
|
|
291
|
+
|
|
292
|
+
expect(mockEventLog).toHaveBeenCalledWith({
|
|
293
|
+
log_name: 'test_log',
|
|
294
|
+
log_type: 'debug',
|
|
295
|
+
params: {
|
|
296
|
+
symbolValue: 'Symbol(복잡한_테스트)',
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AppsInTossModule } from './AppsInTossModule';
|
|
2
|
+
import { isMinVersionSupported } from './isMinVersionSupported';
|
|
3
|
+
import { Primitive } from '../../types';
|
|
4
|
+
|
|
5
|
+
function normalizeParams(params: Record<string, Primitive>): Record<string, string> {
|
|
6
|
+
return Object.fromEntries(
|
|
7
|
+
Object.entries(params)
|
|
8
|
+
.filter(([, value]) => value !== undefined)
|
|
9
|
+
.map(([key, value]) => [key, String(value)])
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface EventLogParams {
|
|
14
|
+
log_name: string;
|
|
15
|
+
log_type: 'debug' | 'info' | 'warn' | 'error' | 'screen' | 'impression' | 'click';
|
|
16
|
+
params: Record<string, Primitive>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @category 로깅
|
|
21
|
+
* @kind function
|
|
22
|
+
* @name eventLog
|
|
23
|
+
* @description
|
|
24
|
+
* 이벤트 로그를 기록하는 함수예요.
|
|
25
|
+
*
|
|
26
|
+
* 이 함수는 앱 내에서 발생하는 다양한 이벤트를 로깅하는 데 사용돼요. 디버깅, 정보 제공, 경고, 오류 등 다양한 유형의 로그를 기록할 수 있어요. 샌드박스 환경에서는 콘솔에 로그가 출력되고, 실제 환경에서는 로그 시스템에 기록돼요.
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} params 로그 기록에 필요한 매개변수 객체예요.
|
|
29
|
+
* @param {string} params.log_name 로그의 이름이에요.
|
|
30
|
+
* @param {'debug' | 'info' | 'warn' | 'error' | 'screen' | 'impression' | 'click'} params.log_type 로그의 유형이에요.
|
|
31
|
+
* @param {Record<string, Primitive>} params.params 로그에 포함할 추가 매개변수 객체예요.
|
|
32
|
+
*
|
|
33
|
+
* @returns {Promise<void>} 로그 기록이 완료되면 해결되는 Promise예요.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ### 이벤트 로그 기록하기
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* import { eventLog } from '@apps-in-toss/framework';
|
|
40
|
+
*
|
|
41
|
+
* function logUserAction() {
|
|
42
|
+
* eventLog({
|
|
43
|
+
* log_name: 'user_action',
|
|
44
|
+
* log_type: 'info',
|
|
45
|
+
* params: {
|
|
46
|
+
* action: 'button_click',
|
|
47
|
+
* screen: 'main',
|
|
48
|
+
* userId: 12345
|
|
49
|
+
* }
|
|
50
|
+
* });
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export async function eventLog(params: EventLogParams) {
|
|
55
|
+
if (AppsInTossModule.operationalEnvironment === 'sandbox') {
|
|
56
|
+
console.log('[eventLogDebug]', {
|
|
57
|
+
log_name: params.log_name,
|
|
58
|
+
log_type: params.log_type,
|
|
59
|
+
params: normalizeParams(params.params),
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isSupported = isMinVersionSupported({
|
|
65
|
+
android: '5.208.0',
|
|
66
|
+
ios: '5.208.0',
|
|
67
|
+
});
|
|
68
|
+
if (!isSupported) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return AppsInTossModule.eventLog({
|
|
73
|
+
log_name: params.log_name,
|
|
74
|
+
log_type: params.log_type,
|
|
75
|
+
params: normalizeParams(params.params),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AppsInTossModule } from './AppsInTossModule';
|
|
2
|
+
import { requestPermission } from './requestPermission';
|
|
3
|
+
import { ImageResponse } from '../../types';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_MAX_COUNT = 10;
|
|
6
|
+
const DEFAULT_MAX_WIDTH = 1024;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 앨범 사진을 조회할 때 사용하는 옵션 타입이에요.
|
|
10
|
+
*/
|
|
11
|
+
export interface FetchAlbumPhotosOptions {
|
|
12
|
+
/** 가져올 사진의 최대 개수를 설정해요. 숫자를 입력하고 기본값은 10이에요. */
|
|
13
|
+
maxCount?: number;
|
|
14
|
+
/** 사진의 최대 폭을 제한해요. 단위는 픽셀이고 기본값은 1024이에요. */
|
|
15
|
+
maxWidth?: number;
|
|
16
|
+
/** 이미지를 base64 형식으로 반환할지 설정해요. 기본값은 `false`예요. */
|
|
17
|
+
base64?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
* @category 사진
|
|
23
|
+
* @name fetchAlbumPhotos
|
|
24
|
+
* @description
|
|
25
|
+
* 사용자의 앨범에서 사진 목록을 불러오는 함수예요.
|
|
26
|
+
* 최대 개수와 해상도를 설정할 수 있고 갤러리 미리보기, 이미지 선택 기능 등에 활용할 수 있어요.
|
|
27
|
+
*
|
|
28
|
+
* @param options - 조회 옵션을 담은 객체예요.
|
|
29
|
+
* @param {number} [options.maxCount=10] 가져올 사진의 최대 개수를 설정해요. 숫자로 입력하며 기본값은 10이에요.
|
|
30
|
+
* @param {number} [options.maxWidth=1024] 사진의 최대 폭을 제한해요. 단위는 픽셀이며 기본값은 `1024`이에요.
|
|
31
|
+
* @param {boolean} [options.base64=false] 이미지를 base64 형식으로 반환할지 설정해요. 기본값은 `false`예요.
|
|
32
|
+
* @returns {Promise<ImageResponse[]>}
|
|
33
|
+
* 앨범 사진의 고유 ID와 데이터 URI를 포함한 배열을 반환해요.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ### 사진의 최대 폭을 360px로 제한하여 가져오기
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* import React, { useState } from 'react';
|
|
40
|
+
* import { View, Image, Button } from 'react-native';
|
|
41
|
+
* import { fetchAlbumPhotos } from '@apps-in-toss/framework';
|
|
42
|
+
*
|
|
43
|
+
* const base64 = true;
|
|
44
|
+
*
|
|
45
|
+
* // 앨범 사진 목록을 가져와 화면에 표시하는 컴포넌트
|
|
46
|
+
* function AlbumPhotoList() {
|
|
47
|
+
* const [albumPhotos, setAlbumPhotos] = useState([]);
|
|
48
|
+
*
|
|
49
|
+
* const handlePress = async () => {
|
|
50
|
+
* try {
|
|
51
|
+
* const response = await fetchAlbumPhotos({
|
|
52
|
+
* base64,
|
|
53
|
+
* maxWidth: 360,
|
|
54
|
+
* });
|
|
55
|
+
* setAlbumPhotos((prev) => ([...prev, ...response]));
|
|
56
|
+
* } catch (error) {
|
|
57
|
+
* console.error('앨범을 가져오는 데 실패했어요:', error);
|
|
58
|
+
* }
|
|
59
|
+
* };
|
|
60
|
+
*
|
|
61
|
+
* return (
|
|
62
|
+
* <View>
|
|
63
|
+
* {albumPhotos.map((image) => {
|
|
64
|
+
* // base64 형식으로 반환된 이미지를 표시하려면 데이터 URL 스키마 Prefix를 붙여야해요.
|
|
65
|
+
* const imageUri = base64 ? 'data:image/jpeg;base64,' + image.dataUri : image.dataUri;
|
|
66
|
+
*
|
|
67
|
+
* return <Image source={{ uri: imageUri }} key={image.id} />;
|
|
68
|
+
* })}
|
|
69
|
+
* <Button title="앨범 가져오기" onPress={handlePress} />
|
|
70
|
+
* </View>
|
|
71
|
+
* );
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export async function fetchAlbumPhotos(options: FetchAlbumPhotosOptions): Promise<ImageResponse[]> {
|
|
76
|
+
const permissionStatus = await requestPermission({ name: 'photos', access: 'read' });
|
|
77
|
+
if (permissionStatus === 'denied') {
|
|
78
|
+
throw new Error('사진첩 권한이 거부되었어요.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const albumPhotos = await AppsInTossModule.fetchAlbumPhotos({
|
|
82
|
+
...options,
|
|
83
|
+
maxCount: options.maxCount ?? DEFAULT_MAX_COUNT,
|
|
84
|
+
maxWidth: options.maxWidth ?? DEFAULT_MAX_WIDTH,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return albumPhotos;
|
|
88
|
+
}
|