@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,4 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ /**
2
+ * Toast Container - KOMCA 패턴
3
+ * 토스트 알림 UI 렌더링
4
+ */
5
+ import React from 'react';
2
6
  import { useToast } from './ToastContext';
3
7
  // 타입별 아이콘
4
8
  const icons = {
@@ -36,10 +40,17 @@ const ToastContainer = ({ position = 'top-right' }) => {
36
40
  };
37
41
  if (toasts.length === 0)
38
42
  return null;
39
- return (_jsxs("div", { style: getPositionStyles(), children: [_jsx("div", { className: "toast-list", children: toasts.map((toast) => (_jsxs("div", { className: `toast-item toast-${toast.type}`, style: {
40
- backgroundColor: colors[toast.type].bg,
41
- borderLeftColor: colors[toast.type].border,
42
- }, children: [_jsx("div", { className: "toast-icon", style: { color: colors[toast.type].icon }, children: icons[toast.type] }), _jsxs("div", { className: "toast-content", children: [toast.title && _jsx("div", { className: "toast-title", children: toast.title }), _jsx("div", { className: "toast-message", children: toast.message })] }), _jsx("button", { className: "toast-close", onClick: () => removeToast(toast.id), "aria-label": "\uB2EB\uAE30", children: "\u2715" })] }, toast.id))) }), _jsx("style", { children: `
43
+ return (React.createElement("div", { style: getPositionStyles() },
44
+ React.createElement("div", { className: "toast-list" }, toasts.map((toast) => (React.createElement("div", { key: toast.id, className: `toast-item toast-${toast.type}`, style: {
45
+ backgroundColor: colors[toast.type].bg,
46
+ borderLeftColor: colors[toast.type].border,
47
+ } },
48
+ React.createElement("div", { className: "toast-icon", style: { color: colors[toast.type].icon } }, icons[toast.type]),
49
+ React.createElement("div", { className: "toast-content" },
50
+ toast.title && React.createElement("div", { className: "toast-title" }, toast.title),
51
+ React.createElement("div", { className: "toast-message" }, toast.message)),
52
+ React.createElement("button", { className: "toast-close", onClick: () => removeToast(toast.id), "aria-label": "\uB2EB\uAE30" }, "\u2715"))))),
53
+ React.createElement("style", null, `
43
54
  .toast-list {
44
55
  display: flex;
45
56
  flex-direction: column;
@@ -120,6 +131,6 @@ const ToastContainer = ({ position = 'top-right' }) => {
120
131
  background: rgba(0, 0, 0, 0.1);
121
132
  color: #374151;
122
133
  }
123
- ` })] }));
134
+ `)));
124
135
  };
125
136
  export default ToastContainer;
@@ -1,9 +1,8 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  /**
3
2
  * Toast Context - KOMCA 패턴
4
3
  * 전역 토스트 알림 상태 관리
5
4
  */
6
- import { createContext, useContext, useCallback, useState } from 'react';
5
+ import React, { createContext, useContext, useCallback, useState } from 'react';
7
6
  const ToastContext = createContext(null);
8
7
  /**
9
8
  * Toast Provider
@@ -41,7 +40,7 @@ export const ToastProvider = ({ children, defaultDuration = 3000, maxToasts = 5,
41
40
  const info = useCallback((message, title) => {
42
41
  addToast({ type: 'info', message, title });
43
42
  }, [addToast]);
44
- return (_jsx(ToastContext.Provider, { value: { toasts, addToast, removeToast, success, error, warning, info }, children: children }));
43
+ return (React.createElement(ToastContext.Provider, { value: { toasts, addToast, removeToast, success, error, warning, info } }, children));
45
44
  };
46
45
  /**
47
46
  * useToast Hook
@@ -1,11 +1,19 @@
1
1
  /**
2
- * Hooks 모듈 - KOMCA 패턴
2
+ * Hooks 모듈
3
3
  */
4
4
  export * from './use-auth';
5
+ export * from './use-supabase-auth';
5
6
  export * from './use-initialize';
6
7
  export * from './use-track-history';
7
8
  export * from './use-navigate';
8
9
  export * from './use-global-loading';
9
10
  export * from './use-modal';
10
11
  export * from './use-error-notification';
12
+ export { default as useNetworkStatus } from './use-network-status';
13
+ export type { ConnectionType } from './use-network-status';
14
+ export * from './use-permission';
15
+ export * from './use-recent-menu';
16
+ export * from './use-debounce';
17
+ export * from './use-scroll-restoration';
18
+ export * from './use-effect-once';
11
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,aAAa,CAAC;AAG5B,cAAc,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,qBAAqB,CAAC;AAGpC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,aAAa,CAAC;AAG5B,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,0BAA0B,CAAC;AAGzC,cAAc,mBAAmB,CAAC"}
@@ -1,8 +1,10 @@
1
1
  /**
2
- * Hooks 모듈 - KOMCA 패턴
2
+ * Hooks 모듈
3
3
  */
4
4
  // Auth Hooks
5
5
  export * from './use-auth';
6
+ // Supabase Auth Hooks
7
+ export * from './use-supabase-auth';
6
8
  // Initialize Hook
7
9
  export * from './use-initialize';
8
10
  // Track History Hook
@@ -15,3 +17,15 @@ export * from './use-global-loading';
15
17
  export * from './use-modal';
16
18
  // Error Notification Hook
17
19
  export * from './use-error-notification';
20
+ // Network Status Hook
21
+ export { default as useNetworkStatus } from './use-network-status';
22
+ // Permission Hook
23
+ export * from './use-permission';
24
+ // Recent Menu Hook
25
+ export * from './use-recent-menu';
26
+ // Debounce & Throttle Hooks
27
+ export * from './use-debounce';
28
+ // Scroll Restoration Hook
29
+ export * from './use-scroll-restoration';
30
+ // Effect Once & Lifecycle Hooks
31
+ export * from './use-effect-once';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Auth Hooks - KOMCA 패턴
2
+ * Auth Hooks
3
3
  * 로그인, 로그아웃, 토큰 갱신
4
4
  */
5
5
  import { User } from '../types';
@@ -29,6 +29,7 @@ export declare function useLogout(logoutApi?: LogoutFn): () => Promise<void>;
29
29
  export declare function useTokenRefresh(refreshApi?: RefreshFn): () => Promise<string | null>;
30
30
  /**
31
31
  * 인증 상태 확인 Hook
32
+ * useSelector를 사용하여 상태 변경시 리렌더링 보장
32
33
  */
33
34
  export declare function useAuthState(): {
34
35
  isAuthenticated: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"use-auth.d.ts","sourceRoot":"","sources":["../../src/hooks/use-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;CACZ;AAGD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAGxE,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAG3C,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAErD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,aACN,YAAY,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAuChF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,QAAQ,SACf,OAAO,CAAC,IAAI,CAAC,CAuB3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,SAAS,SACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BpD;AAED;;GAEG;AACH,wBAAgB,YAAY;;;;EAS3B"}
1
+ {"version":3,"file":"use-auth.d.ts","sourceRoot":"","sources":["../../src/hooks/use-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;CACZ;AAGD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAGxE,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAG3C,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAErD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,aACN,YAAY,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAuChF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,QAAQ,SACf,OAAO,CAAC,IAAI,CAAC,CAuB3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,SAAS,SACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2BpD;AAED;;;GAGG;AACH,wBAAgB,YAAY;;;;EAS3B"}
@@ -1,9 +1,10 @@
1
1
  /**
2
- * Auth Hooks - KOMCA 패턴
2
+ * Auth Hooks
3
3
  * 로그인, 로그아웃, 토큰 갱신
4
4
  */
5
5
  import { useCallback } from 'react';
6
- import { getHostStore, dispatchToHost } from '../store/store-access';
6
+ import { useSelector } from 'react-redux';
7
+ import { getStore, setAccessToken, setUser, logout, selectAccessToken, selectUser } from '../store/app-store';
7
8
  import { storage } from '../utils/storage';
8
9
  /**
9
10
  * 로그인 Hook
@@ -11,9 +12,9 @@ import { storage } from '../utils/storage';
11
12
  export function useLogin(loginApi) {
12
13
  return useCallback(async (request) => {
13
14
  try {
15
+ const store = getStore();
14
16
  let response;
15
17
  if (loginApi) {
16
- // 실제 API 호출
17
18
  response = await loginApi(request);
18
19
  }
19
20
  else {
@@ -34,8 +35,8 @@ export function useLogin(loginApi) {
34
35
  }
35
36
  }
36
37
  // Store에 저장
37
- dispatchToHost({ type: 'app/setAccessToken', payload: response.accessToken });
38
- dispatchToHost({ type: 'app/setUser', payload: response.user });
38
+ store.dispatch(setAccessToken(response.accessToken));
39
+ store.dispatch(setUser(response.user));
39
40
  // Storage에도 저장 (새로고침 대비)
40
41
  storage.setAccessToken(response.accessToken);
41
42
  storage.setUser(response.user);
@@ -54,21 +55,20 @@ export function useLogin(loginApi) {
54
55
  export function useLogout(logoutApi) {
55
56
  return useCallback(async () => {
56
57
  try {
58
+ const store = getStore();
57
59
  // API 호출 (있는 경우)
58
60
  if (logoutApi) {
59
61
  await logoutApi();
60
62
  }
61
63
  // Store 초기화
62
- dispatchToHost({ type: 'app/setAccessToken', payload: '' });
63
- dispatchToHost({ type: 'app/setUser', payload: null });
64
- dispatchToHost({ type: 'recentMenu/resetRecentMenu' });
64
+ store.dispatch(logout());
65
+ store.dispatch({ type: 'recentMenu/resetRecentMenu' });
65
66
  // Storage 초기화
66
67
  storage.clearAuth();
67
68
  console.log('[Logout] 로그아웃 완료');
68
69
  }
69
70
  catch (error) {
70
71
  console.error('[Logout] 로그아웃 실패:', error);
71
- // 에러가 발생해도 로컬 상태는 초기화
72
72
  storage.clearAuth();
73
73
  throw error;
74
74
  }
@@ -80,14 +80,14 @@ export function useLogout(logoutApi) {
80
80
  export function useTokenRefresh(refreshApi) {
81
81
  return useCallback(async () => {
82
82
  try {
83
+ const store = getStore();
83
84
  if (!refreshApi) {
84
85
  console.warn('[Token Refresh] refresh API가 설정되지 않았습니다.');
85
86
  return null;
86
87
  }
87
88
  const newToken = await refreshApi();
88
89
  if (newToken) {
89
- // Store에 저장
90
- dispatchToHost({ type: 'app/setAccessToken', payload: newToken });
90
+ store.dispatch(setAccessToken(newToken));
91
91
  storage.setAccessToken(newToken);
92
92
  console.log('[Token Refresh] 토큰 갱신 성공');
93
93
  return newToken;
@@ -96,8 +96,8 @@ export function useTokenRefresh(refreshApi) {
96
96
  }
97
97
  catch (error) {
98
98
  console.error('[Token Refresh] 토큰 갱신 실패:', error);
99
- // 갱신 실패시 로그아웃 처리
100
- dispatchToHost({ type: 'app/setAccessToken', payload: '' });
99
+ const store = getStore();
100
+ store.dispatch(setAccessToken(''));
101
101
  storage.setAccessToken('');
102
102
  throw error;
103
103
  }
@@ -105,13 +105,14 @@ export function useTokenRefresh(refreshApi) {
105
105
  }
106
106
  /**
107
107
  * 인증 상태 확인 Hook
108
+ * useSelector를 사용하여 상태 변경시 리렌더링 보장
108
109
  */
109
110
  export function useAuthState() {
110
- const store = getHostStore();
111
- const state = store?.getState();
111
+ const accessToken = useSelector(selectAccessToken);
112
+ const user = useSelector(selectUser);
112
113
  return {
113
- isAuthenticated: !!state?.app?.accessToken,
114
- user: state?.app?.user || null,
115
- accessToken: state?.app?.accessToken || '',
114
+ isAuthenticated: !!accessToken,
115
+ user,
116
+ accessToken,
116
117
  };
117
118
  }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * useDebounce / useThrottle Hooks
3
+ * 입력 지연 및 스로틀링을 위한 훅
4
+ */
5
+ /**
6
+ * useDebounce Hook
7
+ * 값이 변경된 후 지정된 시간이 지난 뒤에 업데이트
8
+ *
9
+ * @example
10
+ * const [search, setSearch] = useState('');
11
+ * const debouncedSearch = useDebounce(search, 300);
12
+ *
13
+ * useEffect(() => {
14
+ * // debouncedSearch 값이 변경될 때만 API 호출
15
+ * fetchSearchResults(debouncedSearch);
16
+ * }, [debouncedSearch]);
17
+ */
18
+ export declare function useDebounce<T>(value: T, delay?: number): T;
19
+ /**
20
+ * useDebouncedCallback Hook
21
+ * 콜백 함수를 디바운스 처리
22
+ *
23
+ * @example
24
+ * const handleSearch = useDebouncedCallback((query: string) => {
25
+ * fetchSearchResults(query);
26
+ * }, 300);
27
+ *
28
+ * <input onChange={(e) => handleSearch(e.target.value)} />
29
+ */
30
+ export declare function useDebouncedCallback<T extends (...args: any[]) => any>(callback: T, delay?: number): (...args: Parameters<T>) => void;
31
+ /**
32
+ * useThrottle Hook
33
+ * 값 업데이트를 지정된 간격으로 제한
34
+ *
35
+ * @example
36
+ * const [scrollY, setScrollY] = useState(0);
37
+ * const throttledScrollY = useThrottle(scrollY, 100);
38
+ */
39
+ export declare function useThrottle<T>(value: T, interval?: number): T;
40
+ /**
41
+ * useThrottledCallback Hook
42
+ * 콜백 함수를 스로틀 처리
43
+ *
44
+ * @example
45
+ * const handleScroll = useThrottledCallback(() => {
46
+ * console.log('Scrolling...', window.scrollY);
47
+ * }, 100);
48
+ *
49
+ * useEffect(() => {
50
+ * window.addEventListener('scroll', handleScroll);
51
+ * return () => window.removeEventListener('scroll', handleScroll);
52
+ * }, [handleScroll]);
53
+ */
54
+ export declare function useThrottledCallback<T extends (...args: any[]) => any>(callback: T, interval?: number): (...args: Parameters<T>) => void;
55
+ export default useDebounce;
56
+ //# sourceMappingURL=use-debounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-debounce.d.ts","sourceRoot":"","sources":["../../src/hooks/use-debounce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAE,MAAY,GAAG,CAAC,CAc/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACpE,QAAQ,EAAE,CAAC,EACX,KAAK,GAAE,MAAY,GAClB,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CA8BlC;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,GAAE,MAAY,GAAG,CAAC,CAsBlE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACpE,QAAQ,EAAE,CAAC,EACX,QAAQ,GAAE,MAAY,GACrB,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAqClC;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * useDebounce / useThrottle Hooks
3
+ * 입력 지연 및 스로틀링을 위한 훅
4
+ */
5
+ import { useState, useEffect, useCallback, useRef } from 'react';
6
+ /**
7
+ * useDebounce Hook
8
+ * 값이 변경된 후 지정된 시간이 지난 뒤에 업데이트
9
+ *
10
+ * @example
11
+ * const [search, setSearch] = useState('');
12
+ * const debouncedSearch = useDebounce(search, 300);
13
+ *
14
+ * useEffect(() => {
15
+ * // debouncedSearch 값이 변경될 때만 API 호출
16
+ * fetchSearchResults(debouncedSearch);
17
+ * }, [debouncedSearch]);
18
+ */
19
+ export function useDebounce(value, delay = 300) {
20
+ const [debouncedValue, setDebouncedValue] = useState(value);
21
+ useEffect(() => {
22
+ const timer = setTimeout(() => {
23
+ setDebouncedValue(value);
24
+ }, delay);
25
+ return () => {
26
+ clearTimeout(timer);
27
+ };
28
+ }, [value, delay]);
29
+ return debouncedValue;
30
+ }
31
+ /**
32
+ * useDebouncedCallback Hook
33
+ * 콜백 함수를 디바운스 처리
34
+ *
35
+ * @example
36
+ * const handleSearch = useDebouncedCallback((query: string) => {
37
+ * fetchSearchResults(query);
38
+ * }, 300);
39
+ *
40
+ * <input onChange={(e) => handleSearch(e.target.value)} />
41
+ */
42
+ export function useDebouncedCallback(callback, delay = 300) {
43
+ const timeoutRef = useRef(null);
44
+ const callbackRef = useRef(callback);
45
+ // 콜백 참조 업데이트
46
+ useEffect(() => {
47
+ callbackRef.current = callback;
48
+ }, [callback]);
49
+ // 클린업
50
+ useEffect(() => {
51
+ return () => {
52
+ if (timeoutRef.current) {
53
+ clearTimeout(timeoutRef.current);
54
+ }
55
+ };
56
+ }, []);
57
+ return useCallback((...args) => {
58
+ if (timeoutRef.current) {
59
+ clearTimeout(timeoutRef.current);
60
+ }
61
+ timeoutRef.current = setTimeout(() => {
62
+ callbackRef.current(...args);
63
+ }, delay);
64
+ }, [delay]);
65
+ }
66
+ /**
67
+ * useThrottle Hook
68
+ * 값 업데이트를 지정된 간격으로 제한
69
+ *
70
+ * @example
71
+ * const [scrollY, setScrollY] = useState(0);
72
+ * const throttledScrollY = useThrottle(scrollY, 100);
73
+ */
74
+ export function useThrottle(value, interval = 300) {
75
+ const [throttledValue, setThrottledValue] = useState(value);
76
+ const lastExecuted = useRef(Date.now());
77
+ useEffect(() => {
78
+ const now = Date.now();
79
+ const timeSinceLastExec = now - lastExecuted.current;
80
+ if (timeSinceLastExec >= interval) {
81
+ lastExecuted.current = now;
82
+ setThrottledValue(value);
83
+ }
84
+ else {
85
+ const timer = setTimeout(() => {
86
+ lastExecuted.current = Date.now();
87
+ setThrottledValue(value);
88
+ }, interval - timeSinceLastExec);
89
+ return () => clearTimeout(timer);
90
+ }
91
+ }, [value, interval]);
92
+ return throttledValue;
93
+ }
94
+ /**
95
+ * useThrottledCallback Hook
96
+ * 콜백 함수를 스로틀 처리
97
+ *
98
+ * @example
99
+ * const handleScroll = useThrottledCallback(() => {
100
+ * console.log('Scrolling...', window.scrollY);
101
+ * }, 100);
102
+ *
103
+ * useEffect(() => {
104
+ * window.addEventListener('scroll', handleScroll);
105
+ * return () => window.removeEventListener('scroll', handleScroll);
106
+ * }, [handleScroll]);
107
+ */
108
+ export function useThrottledCallback(callback, interval = 300) {
109
+ const lastExecuted = useRef(0);
110
+ const timeoutRef = useRef(null);
111
+ const callbackRef = useRef(callback);
112
+ // 콜백 참조 업데이트
113
+ useEffect(() => {
114
+ callbackRef.current = callback;
115
+ }, [callback]);
116
+ // 클린업
117
+ useEffect(() => {
118
+ return () => {
119
+ if (timeoutRef.current) {
120
+ clearTimeout(timeoutRef.current);
121
+ }
122
+ };
123
+ }, []);
124
+ return useCallback((...args) => {
125
+ const now = Date.now();
126
+ const timeSinceLastExec = now - lastExecuted.current;
127
+ if (timeSinceLastExec >= interval) {
128
+ lastExecuted.current = now;
129
+ callbackRef.current(...args);
130
+ }
131
+ else if (!timeoutRef.current) {
132
+ timeoutRef.current = setTimeout(() => {
133
+ lastExecuted.current = Date.now();
134
+ callbackRef.current(...args);
135
+ timeoutRef.current = null;
136
+ }, interval - timeSinceLastExec);
137
+ }
138
+ }, [interval]);
139
+ }
140
+ export default useDebounce;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * useEffectOnce Hook
3
+ * 조건이 충족되면 한 번만 실행되는 useEffect
4
+ * KOMCA 패턴 업그레이드 버전
5
+ */
6
+ import { DependencyList } from 'react';
7
+ /**
8
+ * useEffectOnce
9
+ * ready 조건이 true가 되면 한 번만 실행
10
+ *
11
+ * @example
12
+ * // 기본 사용
13
+ * useEffectOnce(() => {
14
+ * console.log('한 번만 실행');
15
+ * }, isLoaded);
16
+ *
17
+ * @example
18
+ * // cleanup 함수 포함
19
+ * useEffectOnce(() => {
20
+ * const subscription = subscribe();
21
+ * return () => subscription.unsubscribe();
22
+ * }, isReady);
23
+ */
24
+ export declare function useEffectOnce(effect: () => (() => void) | void, ready?: boolean): void;
25
+ /**
26
+ * useMount
27
+ * 컴포넌트 마운트 시 한 번만 실행
28
+ *
29
+ * @example
30
+ * useMount(() => {
31
+ * analytics.pageView();
32
+ * });
33
+ */
34
+ export declare function useMount(effect: () => (() => void) | void): void;
35
+ /**
36
+ * useUnmount
37
+ * 컴포넌트 언마운트 시 실행
38
+ *
39
+ * @example
40
+ * useUnmount(() => {
41
+ * cleanup();
42
+ * });
43
+ */
44
+ export declare function useUnmount(cleanup: () => void): void;
45
+ /**
46
+ * useUpdateEffect
47
+ * 첫 렌더링을 제외하고 deps가 변경될 때만 실행
48
+ *
49
+ * @example
50
+ * useUpdateEffect(() => {
51
+ * console.log('count가 변경됨 (첫 렌더링 제외)');
52
+ * }, [count]);
53
+ */
54
+ export declare function useUpdateEffect(effect: () => (() => void) | void, deps: DependencyList): void;
55
+ /**
56
+ * useIsFirstRender
57
+ * 첫 렌더링인지 여부 반환
58
+ *
59
+ * @example
60
+ * const isFirstRender = useIsFirstRender();
61
+ * if (isFirstRender) {
62
+ * // 첫 렌더링 시에만 실행
63
+ * }
64
+ */
65
+ export declare function useIsFirstRender(): boolean;
66
+ /**
67
+ * usePrevious
68
+ * 이전 값 반환
69
+ *
70
+ * @example
71
+ * const [count, setCount] = useState(0);
72
+ * const prevCount = usePrevious(count);
73
+ * // count: 5, prevCount: 4
74
+ */
75
+ export declare function usePrevious<T>(value: T): T | undefined;
76
+ export default useEffectOnce;
77
+ //# sourceMappingURL=use-effect-once.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-effect-once.d.ts","sourceRoot":"","sources":["../../src/hooks/use-effect-once.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAkC,cAAc,EAAE,MAAM,OAAO,CAAC;AAEvE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,EACjC,KAAK,GAAE,OAAc,GACpB,IAAI,CAoBN;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAEhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAOpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,EACjC,IAAI,EAAE,cAAc,GACnB,IAAI,CAYN;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAS1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS,CAQtD;AAED,eAAe,aAAa,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * useEffectOnce Hook
3
+ * 조건이 충족되면 한 번만 실행되는 useEffect
4
+ * KOMCA 패턴 업그레이드 버전
5
+ */
6
+ import { useEffect, useRef } from 'react';
7
+ /**
8
+ * useEffectOnce
9
+ * ready 조건이 true가 되면 한 번만 실행
10
+ *
11
+ * @example
12
+ * // 기본 사용
13
+ * useEffectOnce(() => {
14
+ * console.log('한 번만 실행');
15
+ * }, isLoaded);
16
+ *
17
+ * @example
18
+ * // cleanup 함수 포함
19
+ * useEffectOnce(() => {
20
+ * const subscription = subscribe();
21
+ * return () => subscription.unsubscribe();
22
+ * }, isReady);
23
+ */
24
+ export function useEffectOnce(effect, ready = true) {
25
+ const hasRun = useRef(false);
26
+ const cleanupRef = useRef(undefined);
27
+ const effectRef = useRef(effect);
28
+ effectRef.current = effect;
29
+ useEffect(() => {
30
+ if (hasRun.current || !ready) {
31
+ return;
32
+ }
33
+ hasRun.current = true;
34
+ cleanupRef.current = effectRef.current();
35
+ return () => {
36
+ if (typeof cleanupRef.current === 'function') {
37
+ cleanupRef.current();
38
+ }
39
+ };
40
+ }, [ready]);
41
+ }
42
+ /**
43
+ * useMount
44
+ * 컴포넌트 마운트 시 한 번만 실행
45
+ *
46
+ * @example
47
+ * useMount(() => {
48
+ * analytics.pageView();
49
+ * });
50
+ */
51
+ export function useMount(effect) {
52
+ useEffectOnce(effect, true);
53
+ }
54
+ /**
55
+ * useUnmount
56
+ * 컴포넌트 언마운트 시 실행
57
+ *
58
+ * @example
59
+ * useUnmount(() => {
60
+ * cleanup();
61
+ * });
62
+ */
63
+ export function useUnmount(cleanup) {
64
+ const cleanupRef = useRef(cleanup);
65
+ cleanupRef.current = cleanup;
66
+ useEffect(() => {
67
+ return () => cleanupRef.current();
68
+ }, []);
69
+ }
70
+ /**
71
+ * useUpdateEffect
72
+ * 첫 렌더링을 제외하고 deps가 변경될 때만 실행
73
+ *
74
+ * @example
75
+ * useUpdateEffect(() => {
76
+ * console.log('count가 변경됨 (첫 렌더링 제외)');
77
+ * }, [count]);
78
+ */
79
+ export function useUpdateEffect(effect, deps) {
80
+ const isFirstMount = useRef(true);
81
+ useEffect(() => {
82
+ if (isFirstMount.current) {
83
+ isFirstMount.current = false;
84
+ return;
85
+ }
86
+ return effect();
87
+ // eslint-disable-next-line react-hooks/exhaustive-deps
88
+ }, deps);
89
+ }
90
+ /**
91
+ * useIsFirstRender
92
+ * 첫 렌더링인지 여부 반환
93
+ *
94
+ * @example
95
+ * const isFirstRender = useIsFirstRender();
96
+ * if (isFirstRender) {
97
+ * // 첫 렌더링 시에만 실행
98
+ * }
99
+ */
100
+ export function useIsFirstRender() {
101
+ const isFirst = useRef(true);
102
+ if (isFirst.current) {
103
+ isFirst.current = false;
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+ /**
109
+ * usePrevious
110
+ * 이전 값 반환
111
+ *
112
+ * @example
113
+ * const [count, setCount] = useState(0);
114
+ * const prevCount = usePrevious(count);
115
+ * // count: 5, prevCount: 4
116
+ */
117
+ export function usePrevious(value) {
118
+ const ref = useRef(undefined);
119
+ useEffect(() => {
120
+ ref.current = value;
121
+ }, [value]);
122
+ return ref.current;
123
+ }
124
+ export default useEffectOnce;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Error Notification Hook - KOMCA 패턴
2
+ * Error Notification Hook
3
3
  * API 에러 발생시 자동 알림
4
4
  */
5
5
  import { ErrorDetail } from '../network/axios-factory';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Error Notification Hook - KOMCA 패턴
2
+ * Error Notification Hook
3
3
  * API 에러 발생시 자동 알림
4
4
  */
5
5
  import { useEffect } from 'react';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Global Loading Hook - KOMCA 패턴
2
+ * Global Loading Hook
3
3
  * API 호출 시 전역 로딩 표시
4
4
  */
5
5
  /**