@sonhoseong/mfa-lib 1.3.7 → 1.3.10
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/components/button/ScrollTopButton.js +5 -3
- package/dist/components/error/ErrorBoundary.js +14 -4
- package/dist/components/error/NotFound.d.ts +20 -0
- package/dist/components/error/NotFound.d.ts.map +1 -0
- package/dist/components/error/NotFound.js +84 -0
- package/dist/components/error/index.d.ts +2 -0
- package/dist/components/error/index.d.ts.map +1 -1
- package/dist/components/error/index.js +1 -0
- package/dist/components/icons/Icons.d.ts +51 -0
- package/dist/components/icons/Icons.d.ts.map +1 -0
- package/dist/components/icons/Icons.js +100 -0
- package/dist/components/icons/index.d.ts +5 -0
- package/dist/components/icons/index.d.ts.map +1 -0
- package/dist/components/icons/index.js +4 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +6 -0
- package/dist/components/layout/Container.js +7 -2
- package/dist/components/loading/DeferredComponent.d.ts +19 -0
- package/dist/components/loading/DeferredComponent.d.ts.map +1 -0
- package/dist/components/loading/DeferredComponent.js +32 -0
- package/dist/components/loading/GlobalLoading.js +14 -3
- package/dist/components/loading/index.d.ts +1 -0
- package/dist/components/loading/index.d.ts.map +1 -1
- package/dist/components/loading/index.js +1 -0
- package/dist/components/logo/Logo.d.ts +2 -0
- package/dist/components/logo/Logo.d.ts.map +1 -1
- package/dist/components/logo/Logo.js +13 -4
- package/dist/components/modal/ModalContainer.js +17 -8
- package/dist/components/modal/ModalContext.js +2 -3
- package/dist/components/navigation/AppNavbar.js +21 -9
- package/dist/components/navigation/AppSidebar.css +58 -3
- package/dist/components/navigation/AppSidebar.d.ts +1 -1
- package/dist/components/navigation/AppSidebar.d.ts.map +1 -1
- package/dist/components/navigation/AppSidebar.js +58 -15
- package/dist/components/navigation/Footer.d.ts +15 -0
- package/dist/components/navigation/Footer.d.ts.map +1 -0
- package/dist/components/navigation/Footer.js +12 -0
- package/dist/components/navigation/Header.d.ts.map +1 -1
- package/dist/components/navigation/Header.js +17 -4
- package/dist/components/navigation/Lnb.d.ts +2 -7
- package/dist/components/navigation/Lnb.d.ts.map +1 -1
- package/dist/components/navigation/Lnb.js +34 -6
- package/dist/components/navigation/StickyNav.js +19 -11
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +1 -0
- package/dist/components/page/LoginPage.d.ts +4 -1
- package/dist/components/page/LoginPage.d.ts.map +1 -1
- package/dist/components/page/LoginPage.js +146 -21
- package/dist/components/remote/RemoteErrorBoundary.d.ts +28 -0
- package/dist/components/remote/RemoteErrorBoundary.d.ts.map +1 -0
- package/dist/components/remote/RemoteErrorBoundary.js +44 -0
- package/dist/components/remote/RemoteErrorFallback.d.ts +16 -0
- package/dist/components/remote/RemoteErrorFallback.d.ts.map +1 -0
- package/dist/components/remote/RemoteErrorFallback.js +76 -0
- package/dist/components/remote/index.d.ts +8 -0
- package/dist/components/remote/index.d.ts.map +1 -0
- package/dist/components/remote/index.js +5 -0
- package/dist/components/router/BrowserRouter.d.ts +13 -0
- package/dist/components/router/BrowserRouter.d.ts.map +1 -0
- package/dist/components/router/BrowserRouter.js +17 -0
- package/dist/components/router/RouteGuard.d.ts +79 -0
- package/dist/components/router/RouteGuard.d.ts.map +1 -0
- package/dist/components/router/RouteGuard.js +86 -0
- package/dist/components/router/index.d.ts +4 -0
- package/dist/components/router/index.d.ts.map +1 -0
- package/dist/components/router/index.js +2 -0
- package/dist/components/toast/ToastContainer.js +17 -6
- package/dist/components/toast/ToastContext.js +2 -3
- package/dist/hooks/index.d.ts +9 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -1
- package/dist/hooks/use-auth.d.ts +2 -1
- package/dist/hooks/use-auth.d.ts.map +1 -1
- package/dist/hooks/use-auth.js +19 -18
- package/dist/hooks/use-debounce.d.ts +56 -0
- package/dist/hooks/use-debounce.d.ts.map +1 -0
- package/dist/hooks/use-debounce.js +140 -0
- package/dist/hooks/use-effect-once.d.ts +77 -0
- package/dist/hooks/use-effect-once.d.ts.map +1 -0
- package/dist/hooks/use-effect-once.js +124 -0
- package/dist/hooks/use-error-notification.d.ts +1 -1
- package/dist/hooks/use-error-notification.js +1 -1
- package/dist/hooks/use-global-loading.d.ts +1 -1
- package/dist/hooks/use-global-loading.js +1 -1
- package/dist/hooks/use-initialize.d.ts +8 -1
- package/dist/hooks/use-initialize.d.ts.map +1 -1
- package/dist/hooks/use-initialize.js +126 -23
- package/dist/hooks/use-modal.d.ts +21 -5
- package/dist/hooks/use-modal.d.ts.map +1 -1
- package/dist/hooks/use-modal.js +57 -17
- package/dist/hooks/use-navigate.d.ts +1 -1
- package/dist/hooks/use-navigate.js +1 -1
- package/dist/hooks/use-network-status.d.ts +15 -0
- package/dist/hooks/use-network-status.d.ts.map +1 -0
- package/dist/hooks/use-network-status.js +49 -0
- package/dist/hooks/use-permission.d.ts +22 -0
- package/dist/hooks/use-permission.d.ts.map +1 -0
- package/dist/hooks/use-permission.js +73 -0
- package/dist/hooks/use-recent-menu.d.ts +46 -0
- package/dist/hooks/use-recent-menu.d.ts.map +1 -0
- package/dist/hooks/use-recent-menu.js +169 -0
- package/dist/hooks/use-scroll-restoration.d.ts +51 -0
- package/dist/hooks/use-scroll-restoration.d.ts.map +1 -0
- package/dist/hooks/use-scroll-restoration.js +143 -0
- package/dist/hooks/use-supabase-auth.d.ts +49 -0
- package/dist/hooks/use-supabase-auth.d.ts.map +1 -0
- package/dist/hooks/use-supabase-auth.js +229 -0
- package/dist/hooks/use-track-history.d.ts +2 -1
- package/dist/hooks/use-track-history.d.ts.map +1 -1
- package/dist/hooks/use-track-history.js +14 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/network/axios-factory.d.ts +30 -1
- package/dist/network/axios-factory.d.ts.map +1 -1
- package/dist/network/axios-factory.js +192 -24
- package/dist/network/index.d.ts +3 -1
- package/dist/network/index.d.ts.map +1 -1
- package/dist/network/index.js +5 -1
- package/dist/network/supabase-client.d.ts +28 -0
- package/dist/network/supabase-client.d.ts.map +1 -0
- package/dist/network/supabase-client.js +46 -0
- package/dist/store/app-store.d.ts +222 -12
- package/dist/store/app-store.d.ts.map +1 -1
- package/dist/store/app-store.js +46 -29
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +3 -0
- package/dist/store/menu-slice.d.ts +96 -0
- package/dist/store/menu-slice.d.ts.map +1 -0
- package/dist/store/menu-slice.js +98 -0
- package/dist/store/recent-menu-slice.d.ts +209 -0
- package/dist/store/recent-menu-slice.d.ts.map +1 -0
- package/dist/store/recent-menu-slice.js +110 -0
- package/dist/store/store-access.d.ts +1 -1
- package/dist/store/store-access.js +1 -1
- package/dist/types/index.d.ts +74 -17
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/service.d.ts +1 -1
- package/dist/types/service.js +1 -1
- package/dist/utils/classnames.d.ts +65 -0
- package/dist/utils/classnames.d.ts.map +1 -0
- package/dist/utils/classnames.js +98 -0
- package/dist/utils/formatter.d.ts +78 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +216 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/permission.d.ts +33 -0
- package/dist/utils/permission.d.ts.map +1 -0
- package/dist/utils/permission.js +132 -0
- package/dist/utils/query-string.d.ts +67 -0
- package/dist/utils/query-string.d.ts.map +1 -0
- package/dist/utils/query-string.js +136 -0
- package/dist/utils/storage.d.ts +1 -1
- package/dist/utils/storage.js +1 -1
- package/dist/utils/validation.d.ts +98 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +260 -0
- package/package.json +5 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Track History Hook
|
|
2
|
+
* Track History Hook
|
|
3
3
|
* 라우팅 변경 감지 및 자동 탭(Recent Menu) 관리
|
|
4
4
|
*/
|
|
5
5
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
@@ -135,10 +135,22 @@ export function useTrackHistory(options) {
|
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* Recent Menu 상태 Hook
|
|
138
|
+
* useSelector를 사용하여 상태 변경 시 리렌더링 보장
|
|
138
139
|
*/
|
|
139
140
|
export function useRecentMenuState() {
|
|
140
141
|
const store = getHostStore();
|
|
141
|
-
|
|
142
|
+
// useSelector 대신 useSyncExternalStore 패턴 사용 (Host store 구독)
|
|
143
|
+
const [state, setState] = useState(() => store?.getState().recentMenu);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!store)
|
|
146
|
+
return;
|
|
147
|
+
// Store 변경 구독
|
|
148
|
+
const unsubscribe = store.subscribe(() => {
|
|
149
|
+
const newState = store.getState().recentMenu;
|
|
150
|
+
setState(newState);
|
|
151
|
+
});
|
|
152
|
+
return unsubscribe;
|
|
153
|
+
}, [store]);
|
|
142
154
|
const currentMenu = state?.list?.find((menu) => menu.id === state?.currentId);
|
|
143
155
|
return {
|
|
144
156
|
list: state?.list || [],
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Axios Factory
|
|
2
|
+
* Axios Factory
|
|
3
3
|
* 401 에러시 자동 토큰 갱신 포함
|
|
4
4
|
*/
|
|
5
5
|
import { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
@@ -37,12 +37,41 @@ export interface ServiceConfig {
|
|
|
37
37
|
}
|
|
38
38
|
export type RefreshTokenFn = () => Promise<string | null>;
|
|
39
39
|
export type DispatchErrorFn = (errorDetails: ErrorDetail[]) => void;
|
|
40
|
+
/** HTTP 에러 타입 */
|
|
41
|
+
export type HttpErrorType = 'toast' | 'modal' | 'silent';
|
|
42
|
+
/** HTTP 에러 정보 */
|
|
43
|
+
export interface HttpErrorInfo {
|
|
44
|
+
status: number;
|
|
45
|
+
message: string;
|
|
46
|
+
title?: string;
|
|
47
|
+
type: HttpErrorType;
|
|
48
|
+
/** 원본 에러 */
|
|
49
|
+
error: AxiosError;
|
|
50
|
+
}
|
|
51
|
+
/** HTTP 에러 핸들러 함수 타입 */
|
|
52
|
+
export type HttpErrorHandler = (errorInfo: HttpErrorInfo) => void;
|
|
53
|
+
/** HTTP 상태 코드별 기본 메시지 */
|
|
54
|
+
export declare const HTTP_ERROR_MESSAGES: Record<number, {
|
|
55
|
+
message: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
type: HttpErrorType;
|
|
58
|
+
}>;
|
|
59
|
+
/** 네트워크 에러 메시지 */
|
|
60
|
+
export declare const NETWORK_ERROR_MESSAGE: {
|
|
61
|
+
message: string;
|
|
62
|
+
title: string;
|
|
63
|
+
type: HttpErrorType;
|
|
64
|
+
};
|
|
40
65
|
export interface FactoryConfig {
|
|
41
66
|
getAccessToken: () => string;
|
|
42
67
|
setAccessToken: (token: string) => void;
|
|
43
68
|
refreshToken?: RefreshTokenFn;
|
|
44
69
|
onError?: DispatchErrorFn;
|
|
45
70
|
onUnauthorized?: () => void;
|
|
71
|
+
/** HTTP 에러 핸들러 (토스트/모달 표시용) */
|
|
72
|
+
onHttpError?: HttpErrorHandler;
|
|
73
|
+
/** 에러 핸들링 비활성화할 상태 코드 목록 */
|
|
74
|
+
silentStatusCodes?: number[];
|
|
46
75
|
}
|
|
47
76
|
export declare function initAxiosFactory(config: FactoryConfig): void;
|
|
48
77
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"axios-factory.d.ts","sourceRoot":"","sources":["../../src/network/axios-factory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAc,EAAE,UAAU,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG5F,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,GAAG;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,kBAAkB;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAGhH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,QAAQ,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;CAC5C;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,kBAAkB,CAOlE;AAGD,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,WAAW,EAAE,GAAG,SAAS,CAKrE;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,UAAU,CAE5D;AAGD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"axios-factory.d.ts","sourceRoot":"","sources":["../../src/network/axios-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Axios Factory
|
|
2
|
+
* Axios Factory
|
|
3
3
|
* 401 에러시 자동 토큰 갱신 포함
|
|
4
4
|
*/
|
|
5
5
|
var _a;
|
|
@@ -23,7 +23,105 @@ export function hasErrorDetails(error) {
|
|
|
23
23
|
export function isAxiosError(error) {
|
|
24
24
|
return Boolean(error.isAxiosError);
|
|
25
25
|
}
|
|
26
|
+
/** HTTP 상태 코드별 기본 메시지 */
|
|
27
|
+
export const HTTP_ERROR_MESSAGES = {
|
|
28
|
+
400: {
|
|
29
|
+
message: '요청이 올바르지 않습니다. 입력값을 확인해 주세요.',
|
|
30
|
+
title: '잘못된 요청',
|
|
31
|
+
type: 'toast',
|
|
32
|
+
},
|
|
33
|
+
401: {
|
|
34
|
+
message: '로그인이 필요합니다.',
|
|
35
|
+
title: '인증 필요',
|
|
36
|
+
type: 'silent', // 401은 토큰 갱신으로 처리
|
|
37
|
+
},
|
|
38
|
+
403: {
|
|
39
|
+
message: '접근 권한이 없습니다.',
|
|
40
|
+
title: '권한 없음',
|
|
41
|
+
type: 'toast',
|
|
42
|
+
},
|
|
43
|
+
404: {
|
|
44
|
+
message: '요청한 리소스를 찾을 수 없습니다.',
|
|
45
|
+
title: '찾을 수 없음',
|
|
46
|
+
type: 'toast',
|
|
47
|
+
},
|
|
48
|
+
408: {
|
|
49
|
+
message: '요청 시간이 초과되었습니다. 다시 시도해 주세요.',
|
|
50
|
+
title: '요청 시간 초과',
|
|
51
|
+
type: 'toast',
|
|
52
|
+
},
|
|
53
|
+
409: {
|
|
54
|
+
message: '요청이 현재 서버 상태와 충돌합니다.',
|
|
55
|
+
title: '충돌',
|
|
56
|
+
type: 'toast',
|
|
57
|
+
},
|
|
58
|
+
422: {
|
|
59
|
+
message: '요청 데이터를 처리할 수 없습니다.',
|
|
60
|
+
title: '처리 불가',
|
|
61
|
+
type: 'toast',
|
|
62
|
+
},
|
|
63
|
+
429: {
|
|
64
|
+
message: '너무 많은 요청을 보냈습니다. 잠시 후 다시 시도해 주세요.',
|
|
65
|
+
title: '요청 제한',
|
|
66
|
+
type: 'toast',
|
|
67
|
+
},
|
|
68
|
+
500: {
|
|
69
|
+
message: '서버에 문제가 발생했습니다. 잠시 후 다시 시도해 주세요.',
|
|
70
|
+
title: '서버 오류',
|
|
71
|
+
type: 'modal',
|
|
72
|
+
},
|
|
73
|
+
502: {
|
|
74
|
+
message: '서버와 연결할 수 없습니다.',
|
|
75
|
+
title: '연결 오류',
|
|
76
|
+
type: 'modal',
|
|
77
|
+
},
|
|
78
|
+
503: {
|
|
79
|
+
message: '서비스를 일시적으로 사용할 수 없습니다.',
|
|
80
|
+
title: '서비스 점검 중',
|
|
81
|
+
type: 'modal',
|
|
82
|
+
},
|
|
83
|
+
504: {
|
|
84
|
+
message: '서버 응답 시간이 초과되었습니다.',
|
|
85
|
+
title: '게이트웨이 시간 초과',
|
|
86
|
+
type: 'modal',
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
/** 네트워크 에러 메시지 */
|
|
90
|
+
export const NETWORK_ERROR_MESSAGE = {
|
|
91
|
+
message: '네트워크 연결을 확인해 주세요.',
|
|
92
|
+
title: '네트워크 오류',
|
|
93
|
+
type: 'toast',
|
|
94
|
+
};
|
|
26
95
|
let factoryConfig = null;
|
|
96
|
+
// ============================================
|
|
97
|
+
// 토큰 갱신 큐 (동시 요청 시 중복 갱신 방지)
|
|
98
|
+
// KOMCA 스타일 패턴: 동시 401 발생 시 하나만 갱신 요청
|
|
99
|
+
// ============================================
|
|
100
|
+
let isRefreshing = false;
|
|
101
|
+
let refreshSubscribers = [];
|
|
102
|
+
/**
|
|
103
|
+
* 토큰 갱신 대기 큐에 콜백 추가
|
|
104
|
+
* @returns Promise<string> 새 토큰
|
|
105
|
+
*/
|
|
106
|
+
const subscribeTokenRefresh = () => {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
refreshSubscribers.push({ resolve, reject });
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* 토큰 갱신 완료 시 대기 중인 모든 요청에 새 토큰 전달
|
|
113
|
+
*/
|
|
114
|
+
const onTokenRefreshed = (token) => {
|
|
115
|
+
refreshSubscribers.forEach(({ resolve }) => resolve(token));
|
|
116
|
+
refreshSubscribers = [];
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* 토큰 갱신 실패 시 대기 중인 모든 요청 거부
|
|
120
|
+
*/
|
|
121
|
+
const onTokenRefreshFailed = (error) => {
|
|
122
|
+
refreshSubscribers.forEach(({ reject }) => reject(error));
|
|
123
|
+
refreshSubscribers = [];
|
|
124
|
+
};
|
|
27
125
|
// Factory 초기화
|
|
28
126
|
export function initAxiosFactory(config) {
|
|
29
127
|
factoryConfig = config;
|
|
@@ -70,39 +168,109 @@ export class AxiosClientFactory {
|
|
|
70
168
|
});
|
|
71
169
|
// 응답 인터셉터
|
|
72
170
|
axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
73
|
-
|
|
74
|
-
|
|
171
|
+
const status = error.response?.status;
|
|
172
|
+
const silentCodes = factoryConfig?.silentStatusCodes || [];
|
|
173
|
+
// ============================================
|
|
174
|
+
// 네트워크 에러 처리
|
|
175
|
+
// ============================================
|
|
176
|
+
if (error.message === 'Network Error' || !error.response) {
|
|
75
177
|
console.error('[Network Error] 네트워크 연결을 확인해주세요.');
|
|
178
|
+
if (factoryConfig?.onHttpError) {
|
|
179
|
+
factoryConfig.onHttpError({
|
|
180
|
+
status: 0,
|
|
181
|
+
message: NETWORK_ERROR_MESSAGE.message,
|
|
182
|
+
title: NETWORK_ERROR_MESSAGE.title,
|
|
183
|
+
type: NETWORK_ERROR_MESSAGE.type,
|
|
184
|
+
error,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return Promise.reject(error);
|
|
76
188
|
}
|
|
189
|
+
// ============================================
|
|
77
190
|
// 401 에러 - 토큰 갱신 시도
|
|
78
|
-
|
|
191
|
+
// ============================================
|
|
192
|
+
if (status === 401 && factoryConfig?.refreshToken) {
|
|
79
193
|
const originalRequest = error.config;
|
|
80
|
-
|
|
81
|
-
|
|
194
|
+
// refresh 엔드포인트 자체의 401은 처리하지 않음
|
|
195
|
+
if (originalRequest.url?.includes('/auth/refresh')) {
|
|
196
|
+
return Promise.reject(error);
|
|
197
|
+
}
|
|
198
|
+
// 이미 재시도한 요청은 처리하지 않음
|
|
199
|
+
if (originalRequest._isRetry) {
|
|
200
|
+
return Promise.reject(error);
|
|
201
|
+
}
|
|
202
|
+
// 토큰 갱신 중이면 대기 큐에 추가
|
|
203
|
+
if (isRefreshing) {
|
|
82
204
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
factoryConfig.setAccessToken(newToken);
|
|
88
|
-
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
|
89
|
-
console.log('[Token Refresh] 토큰이 갱신되었습니다.');
|
|
90
|
-
return axiosInstance(originalRequest);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
205
|
+
const newToken = await subscribeTokenRefresh();
|
|
206
|
+
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
|
207
|
+
originalRequest._isRetry = true;
|
|
208
|
+
return axiosInstance(originalRequest);
|
|
93
209
|
}
|
|
94
210
|
catch (refreshError) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
211
|
+
return Promise.reject(refreshError);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// 첫 번째 401 요청: 토큰 갱신 수행
|
|
215
|
+
originalRequest._isRetry = true;
|
|
216
|
+
isRefreshing = true;
|
|
217
|
+
try {
|
|
218
|
+
const newToken = await factoryConfig.refreshToken();
|
|
219
|
+
if (newToken) {
|
|
220
|
+
factoryConfig.setAccessToken(newToken);
|
|
221
|
+
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
|
222
|
+
console.log('[Token Refresh] 토큰이 갱신되었습니다.');
|
|
223
|
+
// 대기 중인 모든 요청에 새 토큰 전달
|
|
224
|
+
onTokenRefreshed(newToken);
|
|
225
|
+
return axiosInstance(originalRequest);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// 토큰 갱신 실패 (null 반환)
|
|
229
|
+
throw new Error('Token refresh returned null');
|
|
98
230
|
}
|
|
99
231
|
}
|
|
232
|
+
catch (refreshError) {
|
|
233
|
+
console.error('[Token Refresh Failed]', refreshError);
|
|
234
|
+
onTokenRefreshFailed(refreshError);
|
|
235
|
+
factoryConfig.setAccessToken('');
|
|
236
|
+
factoryConfig.onUnauthorized?.();
|
|
237
|
+
return Promise.reject(refreshError);
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
isRefreshing = false;
|
|
241
|
+
}
|
|
100
242
|
}
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
243
|
+
// ============================================
|
|
244
|
+
// HTTP 상태 코드별 에러 처리
|
|
245
|
+
// ============================================
|
|
246
|
+
if (status && !silentCodes.includes(status)) {
|
|
247
|
+
// API 에러 상세 정보 처리
|
|
248
|
+
if (isApiError(error)) {
|
|
249
|
+
const errorDetails = hasErrorDetails(error);
|
|
250
|
+
if (errorDetails && factoryConfig?.onError) {
|
|
251
|
+
factoryConfig.onError(errorDetails);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// HTTP 에러 핸들러 호출
|
|
255
|
+
if (factoryConfig?.onHttpError) {
|
|
256
|
+
const errorConfig = HTTP_ERROR_MESSAGES[status] || {
|
|
257
|
+
message: `알 수 없는 오류가 발생했습니다. (${status})`,
|
|
258
|
+
title: '오류',
|
|
259
|
+
type: 'toast',
|
|
260
|
+
};
|
|
261
|
+
// 401은 토큰 갱신 실패 후에만 표시 (위에서 이미 처리됨)
|
|
262
|
+
if (status !== 401) {
|
|
263
|
+
// 서버 응답에 메시지가 있으면 우선 사용
|
|
264
|
+
const serverMessage = error.response?.data?.message;
|
|
265
|
+
const finalMessage = serverMessage || errorConfig.message;
|
|
266
|
+
factoryConfig.onHttpError({
|
|
267
|
+
status,
|
|
268
|
+
message: finalMessage,
|
|
269
|
+
title: errorConfig.title,
|
|
270
|
+
type: errorConfig.type,
|
|
271
|
+
error,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
106
274
|
}
|
|
107
275
|
}
|
|
108
276
|
return Promise.reject(error);
|
package/dist/network/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/network/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/network/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,kBAAkB,CAAC"}
|
package/dist/network/index.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Client
|
|
3
|
+
* Supabase Auth를 위한 공식 SDK 클라이언트
|
|
4
|
+
*/
|
|
5
|
+
import { SupabaseClient, Session, User as SupabaseUser } from '@supabase/supabase-js';
|
|
6
|
+
/**
|
|
7
|
+
* Supabase 클라이언트 설정
|
|
8
|
+
*/
|
|
9
|
+
export interface SupabaseClientConfig {
|
|
10
|
+
supabaseUrl: string;
|
|
11
|
+
supabaseAnonKey: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Supabase 클라이언트 초기화
|
|
15
|
+
* 앱 시작 시 한 번만 호출
|
|
16
|
+
*/
|
|
17
|
+
export declare function initSupabase(config: SupabaseClientConfig): SupabaseClient;
|
|
18
|
+
/**
|
|
19
|
+
* Supabase 클라이언트 가져오기
|
|
20
|
+
* 반드시 initSupabase()로 먼저 초기화해야 함
|
|
21
|
+
*/
|
|
22
|
+
export declare function getSupabase(): SupabaseClient;
|
|
23
|
+
/**
|
|
24
|
+
* Supabase 클라이언트 리셋 (테스트용)
|
|
25
|
+
*/
|
|
26
|
+
export declare function resetSupabase(): void;
|
|
27
|
+
export type { SupabaseClient, Session, SupabaseUser };
|
|
28
|
+
//# sourceMappingURL=supabase-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase-client.d.ts","sourceRoot":"","sources":["../../src/network/supabase-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAgB,cAAc,EAAE,OAAO,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKpG;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAqBzE;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,cAAc,CAQ5C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAGD,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Client
|
|
3
|
+
* Supabase Auth를 위한 공식 SDK 클라이언트
|
|
4
|
+
*/
|
|
5
|
+
import { createClient } from '@supabase/supabase-js';
|
|
6
|
+
// Supabase 클라이언트 싱글톤
|
|
7
|
+
let supabaseInstance = null;
|
|
8
|
+
/**
|
|
9
|
+
* Supabase 클라이언트 초기화
|
|
10
|
+
* 앱 시작 시 한 번만 호출
|
|
11
|
+
*/
|
|
12
|
+
export function initSupabase(config) {
|
|
13
|
+
if (supabaseInstance) {
|
|
14
|
+
return supabaseInstance;
|
|
15
|
+
}
|
|
16
|
+
const { supabaseUrl, supabaseAnonKey } = config;
|
|
17
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
18
|
+
throw new Error('[Supabase] URL과 Anon Key가 필요합니다.');
|
|
19
|
+
}
|
|
20
|
+
supabaseInstance = createClient(supabaseUrl, supabaseAnonKey, {
|
|
21
|
+
auth: {
|
|
22
|
+
autoRefreshToken: true,
|
|
23
|
+
persistSession: true,
|
|
24
|
+
detectSessionInUrl: true,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
console.log('[Supabase] 클라이언트 초기화 완료');
|
|
28
|
+
return supabaseInstance;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Supabase 클라이언트 가져오기
|
|
32
|
+
* 반드시 initSupabase()로 먼저 초기화해야 함
|
|
33
|
+
*/
|
|
34
|
+
export function getSupabase() {
|
|
35
|
+
if (!supabaseInstance) {
|
|
36
|
+
throw new Error('[Supabase] 클라이언트가 초기화되지 않았습니다. ' +
|
|
37
|
+
'initSupabase()를 먼저 호출해주세요.');
|
|
38
|
+
}
|
|
39
|
+
return supabaseInstance;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Supabase 클라이언트 리셋 (테스트용)
|
|
43
|
+
*/
|
|
44
|
+
export function resetSupabase() {
|
|
45
|
+
supabaseInstance = null;
|
|
46
|
+
}
|