@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.
Files changed (162) hide show
  1. package/dist/components/button/ScrollTopButton.js +5 -3
  2. package/dist/components/error/ErrorBoundary.js +14 -4
  3. package/dist/components/error/NotFound.d.ts +20 -0
  4. package/dist/components/error/NotFound.d.ts.map +1 -0
  5. package/dist/components/error/NotFound.js +84 -0
  6. package/dist/components/error/index.d.ts +2 -0
  7. package/dist/components/error/index.d.ts.map +1 -1
  8. package/dist/components/error/index.js +1 -0
  9. package/dist/components/icons/Icons.d.ts +51 -0
  10. package/dist/components/icons/Icons.d.ts.map +1 -0
  11. package/dist/components/icons/Icons.js +100 -0
  12. package/dist/components/icons/index.d.ts +5 -0
  13. package/dist/components/icons/index.d.ts.map +1 -0
  14. package/dist/components/icons/index.js +4 -0
  15. package/dist/components/index.d.ts +3 -0
  16. package/dist/components/index.d.ts.map +1 -1
  17. package/dist/components/index.js +6 -0
  18. package/dist/components/layout/Container.js +7 -2
  19. package/dist/components/loading/DeferredComponent.d.ts +19 -0
  20. package/dist/components/loading/DeferredComponent.d.ts.map +1 -0
  21. package/dist/components/loading/DeferredComponent.js +32 -0
  22. package/dist/components/loading/GlobalLoading.js +14 -3
  23. package/dist/components/loading/index.d.ts +1 -0
  24. package/dist/components/loading/index.d.ts.map +1 -1
  25. package/dist/components/loading/index.js +1 -0
  26. package/dist/components/logo/Logo.d.ts +2 -0
  27. package/dist/components/logo/Logo.d.ts.map +1 -1
  28. package/dist/components/logo/Logo.js +13 -4
  29. package/dist/components/modal/ModalContainer.js +17 -8
  30. package/dist/components/modal/ModalContext.js +2 -3
  31. package/dist/components/navigation/AppNavbar.js +21 -9
  32. package/dist/components/navigation/AppSidebar.css +58 -3
  33. package/dist/components/navigation/AppSidebar.d.ts +1 -1
  34. package/dist/components/navigation/AppSidebar.d.ts.map +1 -1
  35. package/dist/components/navigation/AppSidebar.js +58 -15
  36. package/dist/components/navigation/Footer.d.ts +15 -0
  37. package/dist/components/navigation/Footer.d.ts.map +1 -0
  38. package/dist/components/navigation/Footer.js +12 -0
  39. package/dist/components/navigation/Header.d.ts.map +1 -1
  40. package/dist/components/navigation/Header.js +17 -4
  41. package/dist/components/navigation/Lnb.d.ts +2 -7
  42. package/dist/components/navigation/Lnb.d.ts.map +1 -1
  43. package/dist/components/navigation/Lnb.js +34 -6
  44. package/dist/components/navigation/StickyNav.js +19 -11
  45. package/dist/components/navigation/index.d.ts +1 -0
  46. package/dist/components/navigation/index.d.ts.map +1 -1
  47. package/dist/components/navigation/index.js +1 -0
  48. package/dist/components/page/LoginPage.d.ts +4 -1
  49. package/dist/components/page/LoginPage.d.ts.map +1 -1
  50. package/dist/components/page/LoginPage.js +146 -21
  51. package/dist/components/remote/RemoteErrorBoundary.d.ts +28 -0
  52. package/dist/components/remote/RemoteErrorBoundary.d.ts.map +1 -0
  53. package/dist/components/remote/RemoteErrorBoundary.js +44 -0
  54. package/dist/components/remote/RemoteErrorFallback.d.ts +16 -0
  55. package/dist/components/remote/RemoteErrorFallback.d.ts.map +1 -0
  56. package/dist/components/remote/RemoteErrorFallback.js +76 -0
  57. package/dist/components/remote/index.d.ts +8 -0
  58. package/dist/components/remote/index.d.ts.map +1 -0
  59. package/dist/components/remote/index.js +5 -0
  60. package/dist/components/router/BrowserRouter.d.ts +13 -0
  61. package/dist/components/router/BrowserRouter.d.ts.map +1 -0
  62. package/dist/components/router/BrowserRouter.js +17 -0
  63. package/dist/components/router/RouteGuard.d.ts +79 -0
  64. package/dist/components/router/RouteGuard.d.ts.map +1 -0
  65. package/dist/components/router/RouteGuard.js +86 -0
  66. package/dist/components/router/index.d.ts +4 -0
  67. package/dist/components/router/index.d.ts.map +1 -0
  68. package/dist/components/router/index.js +2 -0
  69. package/dist/components/toast/ToastContainer.js +17 -6
  70. package/dist/components/toast/ToastContext.js +2 -3
  71. package/dist/hooks/index.d.ts +9 -1
  72. package/dist/hooks/index.d.ts.map +1 -1
  73. package/dist/hooks/index.js +15 -1
  74. package/dist/hooks/use-auth.d.ts +2 -1
  75. package/dist/hooks/use-auth.d.ts.map +1 -1
  76. package/dist/hooks/use-auth.js +19 -18
  77. package/dist/hooks/use-debounce.d.ts +56 -0
  78. package/dist/hooks/use-debounce.d.ts.map +1 -0
  79. package/dist/hooks/use-debounce.js +140 -0
  80. package/dist/hooks/use-effect-once.d.ts +77 -0
  81. package/dist/hooks/use-effect-once.d.ts.map +1 -0
  82. package/dist/hooks/use-effect-once.js +124 -0
  83. package/dist/hooks/use-error-notification.d.ts +1 -1
  84. package/dist/hooks/use-error-notification.js +1 -1
  85. package/dist/hooks/use-global-loading.d.ts +1 -1
  86. package/dist/hooks/use-global-loading.js +1 -1
  87. package/dist/hooks/use-initialize.d.ts +8 -1
  88. package/dist/hooks/use-initialize.d.ts.map +1 -1
  89. package/dist/hooks/use-initialize.js +126 -23
  90. package/dist/hooks/use-modal.d.ts +21 -5
  91. package/dist/hooks/use-modal.d.ts.map +1 -1
  92. package/dist/hooks/use-modal.js +57 -17
  93. package/dist/hooks/use-navigate.d.ts +1 -1
  94. package/dist/hooks/use-navigate.js +1 -1
  95. package/dist/hooks/use-network-status.d.ts +15 -0
  96. package/dist/hooks/use-network-status.d.ts.map +1 -0
  97. package/dist/hooks/use-network-status.js +49 -0
  98. package/dist/hooks/use-permission.d.ts +22 -0
  99. package/dist/hooks/use-permission.d.ts.map +1 -0
  100. package/dist/hooks/use-permission.js +73 -0
  101. package/dist/hooks/use-recent-menu.d.ts +46 -0
  102. package/dist/hooks/use-recent-menu.d.ts.map +1 -0
  103. package/dist/hooks/use-recent-menu.js +169 -0
  104. package/dist/hooks/use-scroll-restoration.d.ts +51 -0
  105. package/dist/hooks/use-scroll-restoration.d.ts.map +1 -0
  106. package/dist/hooks/use-scroll-restoration.js +143 -0
  107. package/dist/hooks/use-supabase-auth.d.ts +49 -0
  108. package/dist/hooks/use-supabase-auth.d.ts.map +1 -0
  109. package/dist/hooks/use-supabase-auth.js +229 -0
  110. package/dist/hooks/use-track-history.d.ts +2 -1
  111. package/dist/hooks/use-track-history.d.ts.map +1 -1
  112. package/dist/hooks/use-track-history.js +14 -2
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/network/axios-factory.d.ts +30 -1
  116. package/dist/network/axios-factory.d.ts.map +1 -1
  117. package/dist/network/axios-factory.js +192 -24
  118. package/dist/network/index.d.ts +3 -1
  119. package/dist/network/index.d.ts.map +1 -1
  120. package/dist/network/index.js +5 -1
  121. package/dist/network/supabase-client.d.ts +28 -0
  122. package/dist/network/supabase-client.d.ts.map +1 -0
  123. package/dist/network/supabase-client.js +46 -0
  124. package/dist/store/app-store.d.ts +222 -12
  125. package/dist/store/app-store.d.ts.map +1 -1
  126. package/dist/store/app-store.js +46 -29
  127. package/dist/store/index.d.ts +2 -0
  128. package/dist/store/index.d.ts.map +1 -1
  129. package/dist/store/index.js +3 -0
  130. package/dist/store/menu-slice.d.ts +96 -0
  131. package/dist/store/menu-slice.d.ts.map +1 -0
  132. package/dist/store/menu-slice.js +98 -0
  133. package/dist/store/recent-menu-slice.d.ts +209 -0
  134. package/dist/store/recent-menu-slice.d.ts.map +1 -0
  135. package/dist/store/recent-menu-slice.js +110 -0
  136. package/dist/store/store-access.d.ts +1 -1
  137. package/dist/store/store-access.js +1 -1
  138. package/dist/types/index.d.ts +74 -17
  139. package/dist/types/index.d.ts.map +1 -1
  140. package/dist/types/service.d.ts +1 -1
  141. package/dist/types/service.js +1 -1
  142. package/dist/utils/classnames.d.ts +65 -0
  143. package/dist/utils/classnames.d.ts.map +1 -0
  144. package/dist/utils/classnames.js +98 -0
  145. package/dist/utils/formatter.d.ts +78 -0
  146. package/dist/utils/formatter.d.ts.map +1 -0
  147. package/dist/utils/formatter.js +216 -0
  148. package/dist/utils/index.d.ts +5 -0
  149. package/dist/utils/index.d.ts.map +1 -1
  150. package/dist/utils/index.js +5 -0
  151. package/dist/utils/permission.d.ts +33 -0
  152. package/dist/utils/permission.d.ts.map +1 -0
  153. package/dist/utils/permission.js +132 -0
  154. package/dist/utils/query-string.d.ts +67 -0
  155. package/dist/utils/query-string.d.ts.map +1 -0
  156. package/dist/utils/query-string.js +136 -0
  157. package/dist/utils/storage.d.ts +1 -1
  158. package/dist/utils/storage.js +1 -1
  159. package/dist/utils/validation.d.ts +98 -0
  160. package/dist/utils/validation.d.ts.map +1 -0
  161. package/dist/utils/validation.js +260 -0
  162. package/package.json +5 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Track History Hook - KOMCA 패턴
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
- const state = store?.getState().recentMenu;
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
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @mfa/lib - MFA 공통 라이브러리
3
3
  *
4
- * KOMCA 패턴 기반 마이크로프론트엔드 공통 유틸리티
4
+ * 마이크로프론트엔드 공통 유틸리티
5
5
  */
6
6
  export * from './types';
7
7
  export * from './store';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @mfa/lib - MFA 공통 라이브러리
3
3
  *
4
- * KOMCA 패턴 기반 마이크로프론트엔드 공통 유틸리티
4
+ * 마이크로프론트엔드 공통 유틸리티
5
5
  */
6
6
  // Types
7
7
  export * from './types';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Axios Factory - KOMCA 패턴
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;AAGpE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAKD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,QAErD;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAElC;IAEF;;OAEG;IACH,MAAM,CAAC,YAAY,CACjB,aAAa,EAAE,WAAW,EAC1B,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,GAAG,aAAa,GACvF,aAAa;CA0FjB"}
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 - KOMCA 패턴
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
- if (error.message === 'Network Error') {
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
- if (error.response?.status === 401 && factoryConfig?.refreshToken) {
191
+ // ============================================
192
+ if (status === 401 && factoryConfig?.refreshToken) {
79
193
  const originalRequest = error.config;
80
- if (!originalRequest._isRetry) {
81
- originalRequest._isRetry = true;
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
- // refresh 엔드포인트가 아닌 경우에만 갱신 시도
84
- if (!originalRequest.url?.includes('/auth/refresh')) {
85
- const newToken = await factoryConfig.refreshToken();
86
- if (newToken) {
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
- console.error('[Token Refresh Failed]', refreshError);
96
- factoryConfig.setAccessToken('');
97
- factoryConfig.onUnauthorized?.();
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
- // API 에러 처리
102
- if (isApiError(error)) {
103
- const errorDetails = hasErrorDetails(error);
104
- if (errorDetails && factoryConfig?.onError) {
105
- factoryConfig.onError(errorDetails);
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);
@@ -1,5 +1,7 @@
1
1
  /**
2
- * Network 모듈 - KOMCA 패턴
2
+ * Network 모듈
3
3
  */
4
4
  export * from './axios-factory';
5
+ export * from './supabase-client';
6
+ export * from './supabase-axios';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -1,4 +1,8 @@
1
1
  /**
2
- * Network 모듈 - KOMCA 패턴
2
+ * Network 모듈
3
3
  */
4
4
  export * from './axios-factory';
5
+ // Supabase Client
6
+ export * from './supabase-client';
7
+ // Supabase Axios (REST API용)
8
+ export * from './supabase-axios';
@@ -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
+ }