@sonhoseong/mfa-lib 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/README.md +172 -0
- package/dist/components/button/ScrollTopButton.d.ts +5 -0
- package/dist/components/button/ScrollTopButton.d.ts.map +1 -0
- package/dist/components/button/ScrollTopButton.js +28 -0
- package/dist/components/button/index.d.ts +3 -0
- package/dist/components/button/index.d.ts.map +1 -0
- package/dist/components/button/index.js +2 -0
- package/dist/components/button/types/index.d.ts +10 -0
- package/dist/components/button/types/index.d.ts.map +1 -0
- package/dist/components/button/types/index.js +1 -0
- package/dist/components/error/ErrorBoundary.d.ts +25 -0
- package/dist/components/error/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/error/ErrorBoundary.js +130 -0
- package/dist/components/error/index.d.ts +2 -0
- package/dist/components/error/index.d.ts.map +1 -0
- package/dist/components/error/index.js +1 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +10 -0
- package/dist/components/loading/GlobalLoading.d.ts +12 -0
- package/dist/components/loading/GlobalLoading.d.ts.map +1 -0
- package/dist/components/loading/GlobalLoading.js +84 -0
- package/dist/components/loading/index.d.ts +2 -0
- package/dist/components/loading/index.d.ts.map +1 -0
- package/dist/components/loading/index.js +1 -0
- package/dist/components/modal/ModalContainer.d.ts +8 -0
- package/dist/components/modal/ModalContainer.d.ts.map +1 -0
- package/dist/components/modal/ModalContainer.js +161 -0
- package/dist/components/modal/ModalContext.d.ts +43 -0
- package/dist/components/modal/ModalContext.d.ts.map +1 -0
- package/dist/components/modal/ModalContext.js +78 -0
- package/dist/components/modal/index.d.ts +4 -0
- package/dist/components/modal/index.d.ts.map +1 -0
- package/dist/components/modal/index.js +2 -0
- package/dist/components/toast/ToastContainer.d.ts +12 -0
- package/dist/components/toast/ToastContainer.d.ts.map +1 -0
- package/dist/components/toast/ToastContainer.js +125 -0
- package/dist/components/toast/ToastContext.d.ts +40 -0
- package/dist/components/toast/ToastContext.d.ts.map +1 -0
- package/dist/components/toast/ToastContext.js +56 -0
- package/dist/components/toast/index.d.ts +4 -0
- package/dist/components/toast/index.d.ts.map +1 -0
- package/dist/components/toast/index.js +2 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +17 -0
- package/dist/hooks/use-auth.d.ts +38 -0
- package/dist/hooks/use-auth.d.ts.map +1 -0
- package/dist/hooks/use-auth.js +117 -0
- package/dist/hooks/use-error-notification.d.ts +29 -0
- package/dist/hooks/use-error-notification.d.ts.map +1 -0
- package/dist/hooks/use-error-notification.js +91 -0
- package/dist/hooks/use-global-loading.d.ts +16 -0
- package/dist/hooks/use-global-loading.d.ts.map +1 -0
- package/dist/hooks/use-global-loading.js +72 -0
- package/dist/hooks/use-initialize.d.ts +26 -0
- package/dist/hooks/use-initialize.d.ts.map +1 -0
- package/dist/hooks/use-initialize.js +104 -0
- package/dist/hooks/use-modal.d.ts +39 -0
- package/dist/hooks/use-modal.d.ts.map +1 -0
- package/dist/hooks/use-modal.js +83 -0
- package/dist/hooks/use-navigate.d.ts +34 -0
- package/dist/hooks/use-navigate.d.ts.map +1 -0
- package/dist/hooks/use-navigate.js +89 -0
- package/dist/hooks/use-track-history.d.ts +33 -0
- package/dist/hooks/use-track-history.d.ts.map +1 -0
- package/dist/hooks/use-track-history.js +150 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/network/axios-factory.d.ts +61 -0
- package/dist/network/axios-factory.d.ts.map +1 -0
- package/dist/network/axios-factory.js +119 -0
- package/dist/network/index.d.ts +5 -0
- package/dist/network/index.d.ts.map +1 -0
- package/dist/network/index.js +4 -0
- package/dist/network/supabase-axios.d.ts +62 -0
- package/dist/network/supabase-axios.d.ts.map +1 -0
- package/dist/network/supabase-axios.js +74 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +1 -0
- package/dist/store/store-access.d.ts +48 -0
- package/dist/store/store-access.d.ts.map +1 -0
- package/dist/store/store-access.js +131 -0
- package/dist/types/index.d.ts +58 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/service.d.ts +17 -0
- package/dist/types/service.d.ts.map +1 -0
- package/dist/types/service.js +31 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/storage.d.ts +27 -0
- package/dist/utils/storage.d.ts.map +1 -0
- package/dist/utils/storage.js +78 -0
- package/package.json +27 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Hooks - KOMCA 패턴
|
|
3
|
+
* 로그인, 로그아웃, 토큰 갱신
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
import { getHostStore, dispatchToHost } from '../store/store-access';
|
|
7
|
+
import { storage } from '../utils/storage';
|
|
8
|
+
/**
|
|
9
|
+
* 로그인 Hook
|
|
10
|
+
*/
|
|
11
|
+
export function useLogin(loginApi) {
|
|
12
|
+
return useCallback(async (request) => {
|
|
13
|
+
try {
|
|
14
|
+
let response;
|
|
15
|
+
if (loginApi) {
|
|
16
|
+
// 실제 API 호출
|
|
17
|
+
response = await loginApi(request);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
// Mock 로그인 (개발용)
|
|
21
|
+
if (request.username === 'admin@test.com' && request.password === '1234') {
|
|
22
|
+
response = {
|
|
23
|
+
accessToken: `mock-token-${Date.now()}`,
|
|
24
|
+
user: {
|
|
25
|
+
id: '1',
|
|
26
|
+
name: '관리자',
|
|
27
|
+
email: request.username,
|
|
28
|
+
role: 'admin',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error('아이디/비밀번호를 확인해주세요.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Store에 저장
|
|
37
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: response.accessToken });
|
|
38
|
+
dispatchToHost({ type: 'app/setUser', payload: response.user });
|
|
39
|
+
// Storage에도 저장 (새로고침 대비)
|
|
40
|
+
storage.setAccessToken(response.accessToken);
|
|
41
|
+
storage.setUser(response.user);
|
|
42
|
+
console.log('[Login] 로그인 성공:', response.user.email);
|
|
43
|
+
return response;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('[Login] 로그인 실패:', error);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}, [loginApi]);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 로그아웃 Hook
|
|
53
|
+
*/
|
|
54
|
+
export function useLogout(logoutApi) {
|
|
55
|
+
return useCallback(async () => {
|
|
56
|
+
try {
|
|
57
|
+
// API 호출 (있는 경우)
|
|
58
|
+
if (logoutApi) {
|
|
59
|
+
await logoutApi();
|
|
60
|
+
}
|
|
61
|
+
// Store 초기화
|
|
62
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: '' });
|
|
63
|
+
dispatchToHost({ type: 'app/setUser', payload: null });
|
|
64
|
+
dispatchToHost({ type: 'recentMenu/resetRecentMenu' });
|
|
65
|
+
// Storage 초기화
|
|
66
|
+
storage.clearAuth();
|
|
67
|
+
console.log('[Logout] 로그아웃 완료');
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('[Logout] 로그아웃 실패:', error);
|
|
71
|
+
// 에러가 발생해도 로컬 상태는 초기화
|
|
72
|
+
storage.clearAuth();
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}, [logoutApi]);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 토큰 갱신 Hook
|
|
79
|
+
*/
|
|
80
|
+
export function useTokenRefresh(refreshApi) {
|
|
81
|
+
return useCallback(async () => {
|
|
82
|
+
try {
|
|
83
|
+
if (!refreshApi) {
|
|
84
|
+
console.warn('[Token Refresh] refresh API가 설정되지 않았습니다.');
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const newToken = await refreshApi();
|
|
88
|
+
if (newToken) {
|
|
89
|
+
// Store에 저장
|
|
90
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: newToken });
|
|
91
|
+
storage.setAccessToken(newToken);
|
|
92
|
+
console.log('[Token Refresh] 토큰 갱신 성공');
|
|
93
|
+
return newToken;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error('[Token Refresh] 토큰 갱신 실패:', error);
|
|
99
|
+
// 갱신 실패시 로그아웃 처리
|
|
100
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: '' });
|
|
101
|
+
storage.setAccessToken('');
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}, [refreshApi]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 인증 상태 확인 Hook
|
|
108
|
+
*/
|
|
109
|
+
export function useAuthState() {
|
|
110
|
+
const store = getHostStore();
|
|
111
|
+
const state = store?.getState();
|
|
112
|
+
return {
|
|
113
|
+
isAuthenticated: !!state?.app?.accessToken,
|
|
114
|
+
user: state?.app?.user || null,
|
|
115
|
+
accessToken: state?.app?.accessToken || '',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Notification Hook - KOMCA 패턴
|
|
3
|
+
* API 에러 발생시 자동 알림
|
|
4
|
+
*/
|
|
5
|
+
import { ErrorDetail } from '../network/axios-factory';
|
|
6
|
+
export interface ErrorEvent {
|
|
7
|
+
errorDetails: ErrorDetail[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 에러 이벤트 발생
|
|
11
|
+
*/
|
|
12
|
+
export declare function dispatchErrorEvent(errorDetails: ErrorDetail[]): void;
|
|
13
|
+
/**
|
|
14
|
+
* 에러 알림 Hook
|
|
15
|
+
*/
|
|
16
|
+
export declare function useErrorNotification(onError?: (errorDetails: ErrorDetail[]) => void): void;
|
|
17
|
+
/**
|
|
18
|
+
* 에러 메시지 포맷팅
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatErrorDetails(errorDetails: ErrorDetail[]): string;
|
|
21
|
+
/**
|
|
22
|
+
* Custom Event 유틸리티
|
|
23
|
+
*/
|
|
24
|
+
export declare const customEvents: {
|
|
25
|
+
dispatchError: typeof dispatchErrorEvent;
|
|
26
|
+
dispatchSearch: (id?: string) => void;
|
|
27
|
+
onSearch: (callback: (id?: string) => void) => () => void;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=use-error-notification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-error-notification.d.ts","sourceRoot":"","sources":["../../src/hooks/use-error-notification.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMvD,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,WAAW,EAAE,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,QAK7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,IAAI,QA4BhD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,CAuBtE;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;0BAKD,MAAM;yBAMP,CAAC,EAAE,CAAC,EAAE,MAAM,KAAK,IAAI;CAO3C,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Notification Hook - KOMCA 패턴
|
|
3
|
+
* API 에러 발생시 자동 알림
|
|
4
|
+
*/
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
// 에러 이벤트 이름
|
|
7
|
+
const ERROR_EVENT_NAME = 'mfa:api-error';
|
|
8
|
+
/**
|
|
9
|
+
* 에러 이벤트 발생
|
|
10
|
+
*/
|
|
11
|
+
export function dispatchErrorEvent(errorDetails) {
|
|
12
|
+
const event = new CustomEvent(ERROR_EVENT_NAME, {
|
|
13
|
+
detail: { errorDetails },
|
|
14
|
+
});
|
|
15
|
+
window.dispatchEvent(event);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 에러 알림 Hook
|
|
19
|
+
*/
|
|
20
|
+
export function useErrorNotification(onError) {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const handleError = (event) => {
|
|
23
|
+
const { errorDetails } = event.detail;
|
|
24
|
+
if (onError) {
|
|
25
|
+
onError(errorDetails);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// 기본 에러 처리
|
|
29
|
+
const messages = errorDetails.map((detail) => {
|
|
30
|
+
if (detail.field) {
|
|
31
|
+
return `[${detail.field}] ${detail.message || detail.code}`;
|
|
32
|
+
}
|
|
33
|
+
return detail.message || detail.code;
|
|
34
|
+
});
|
|
35
|
+
console.error('[API Error]', messages.join('\n'));
|
|
36
|
+
// alert(messages.join('\n'));
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
window.addEventListener(ERROR_EVENT_NAME, handleError);
|
|
40
|
+
return () => {
|
|
41
|
+
window.removeEventListener(ERROR_EVENT_NAME, handleError);
|
|
42
|
+
};
|
|
43
|
+
}, [onError]);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 에러 메시지 포맷팅
|
|
47
|
+
*/
|
|
48
|
+
export function formatErrorDetails(errorDetails) {
|
|
49
|
+
return errorDetails
|
|
50
|
+
.map((detail) => {
|
|
51
|
+
switch (detail.code) {
|
|
52
|
+
case 'NotBlank':
|
|
53
|
+
return `${detail.field || '필드'}은(는) 필수 입력입니다.`;
|
|
54
|
+
case 'NotNull':
|
|
55
|
+
return `${detail.field || '필드'}은(는) 필수 입력입니다.`;
|
|
56
|
+
case 'Pattern':
|
|
57
|
+
return `${detail.field || '필드'} 형식이 올바르지 않습니다.`;
|
|
58
|
+
case 'Min':
|
|
59
|
+
return `${detail.field || '값'}이 최소값보다 작습니다.`;
|
|
60
|
+
case 'Max':
|
|
61
|
+
return `${detail.field || '값'}이 최대값을 초과했습니다.`;
|
|
62
|
+
case 'Size':
|
|
63
|
+
return `${detail.field || '값'} 길이가 허용 범위를 벗어났습니다.`;
|
|
64
|
+
case 'TYPE_MISMATCH':
|
|
65
|
+
return `${detail.field || '필드'} 타입이 올바르지 않습니다.`;
|
|
66
|
+
default:
|
|
67
|
+
return detail.message || '알 수 없는 오류가 발생했습니다.';
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.join('\n');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Custom Event 유틸리티
|
|
74
|
+
*/
|
|
75
|
+
export const customEvents = {
|
|
76
|
+
// 에러 이벤트 발생
|
|
77
|
+
dispatchError: dispatchErrorEvent,
|
|
78
|
+
// 검색 이벤트 발생
|
|
79
|
+
dispatchSearch: (id) => {
|
|
80
|
+
const event = new CustomEvent('mfa:search', { detail: { id } });
|
|
81
|
+
window.dispatchEvent(event);
|
|
82
|
+
},
|
|
83
|
+
// 검색 이벤트 구독
|
|
84
|
+
onSearch: (callback) => {
|
|
85
|
+
const handler = (event) => {
|
|
86
|
+
callback(event.detail.id);
|
|
87
|
+
};
|
|
88
|
+
window.addEventListener('mfa:search', handler);
|
|
89
|
+
return () => window.removeEventListener('mfa:search', handler);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Loading Hook - KOMCA 패턴
|
|
3
|
+
* API 호출 시 전역 로딩 표시
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Global Loading Title 설정
|
|
7
|
+
*/
|
|
8
|
+
export declare function useShowGlobalLoading(): <T>(promise: Promise<T>, title?: string) => Promise<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Loading 상태 직접 제어
|
|
11
|
+
*/
|
|
12
|
+
export declare function useLoadingControl(): {
|
|
13
|
+
showLoading: (title?: string) => void;
|
|
14
|
+
hideLoading: () => void;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=use-global-loading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-global-loading.d.ts","sourceRoot":"","sources":["../../src/hooks/use-global-loading.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,wBAAgB,oBAAoB,KACd,CAAC,WAAW,OAAO,CAAC,CAAC,CAAC,UAAU,MAAM,KAAG,OAAO,CAAC,CAAC,CAAC,CA6BxE;AAED;;GAEG;AACH,wBAAgB,iBAAiB;0BACU,MAAM;;EA+BhD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Loading Hook - KOMCA 패턴
|
|
3
|
+
* API 호출 시 전역 로딩 표시
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
import { getHostStore } from '../store/store-access';
|
|
7
|
+
/**
|
|
8
|
+
* Global Loading Title 설정
|
|
9
|
+
*/
|
|
10
|
+
export function useShowGlobalLoading() {
|
|
11
|
+
return useCallback((promise, title) => {
|
|
12
|
+
const store = getHostStore();
|
|
13
|
+
if (store) {
|
|
14
|
+
// 로딩 시작
|
|
15
|
+
store.dispatch({
|
|
16
|
+
type: 'app/setGlobalLoadingTitle',
|
|
17
|
+
payload: title || '처리 중...',
|
|
18
|
+
});
|
|
19
|
+
store.dispatch({
|
|
20
|
+
type: 'app/setLoading',
|
|
21
|
+
payload: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return promise.finally(() => {
|
|
25
|
+
if (store) {
|
|
26
|
+
// 로딩 종료
|
|
27
|
+
store.dispatch({
|
|
28
|
+
type: 'app/setLoading',
|
|
29
|
+
payload: false,
|
|
30
|
+
});
|
|
31
|
+
store.dispatch({
|
|
32
|
+
type: 'app/setGlobalLoadingTitle',
|
|
33
|
+
payload: '',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}, []);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Loading 상태 직접 제어
|
|
41
|
+
*/
|
|
42
|
+
export function useLoadingControl() {
|
|
43
|
+
const showLoading = useCallback((title) => {
|
|
44
|
+
const store = getHostStore();
|
|
45
|
+
if (store) {
|
|
46
|
+
store.dispatch({
|
|
47
|
+
type: 'app/setLoading',
|
|
48
|
+
payload: true,
|
|
49
|
+
});
|
|
50
|
+
if (title) {
|
|
51
|
+
store.dispatch({
|
|
52
|
+
type: 'app/setGlobalLoadingTitle',
|
|
53
|
+
payload: title,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}, []);
|
|
58
|
+
const hideLoading = useCallback(() => {
|
|
59
|
+
const store = getHostStore();
|
|
60
|
+
if (store) {
|
|
61
|
+
store.dispatch({
|
|
62
|
+
type: 'app/setLoading',
|
|
63
|
+
payload: false,
|
|
64
|
+
});
|
|
65
|
+
store.dispatch({
|
|
66
|
+
type: 'app/setGlobalLoadingTitle',
|
|
67
|
+
payload: '',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}, []);
|
|
71
|
+
return { showLoading, hideLoading };
|
|
72
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize Hook - KOMCA 패턴
|
|
3
|
+
* 앱 시작시 초기화 (토큰 갱신, 사용자 정보 로드)
|
|
4
|
+
*/
|
|
5
|
+
import { User } from '../types';
|
|
6
|
+
export interface InitializeOptions {
|
|
7
|
+
refreshToken?: () => Promise<string | null>;
|
|
8
|
+
fetchUserInfo?: () => Promise<User | null>;
|
|
9
|
+
onInitialized?: () => void;
|
|
10
|
+
onError?: (error: Error) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 앱 초기화 Hook
|
|
14
|
+
*/
|
|
15
|
+
export declare function useInitialize(options?: InitializeOptions): {
|
|
16
|
+
initialized: boolean;
|
|
17
|
+
loading: boolean;
|
|
18
|
+
error: Error | null;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 간단한 초기화 Hook (토큰/사용자 정보 복구만)
|
|
22
|
+
*/
|
|
23
|
+
export declare function useSimpleInitialize(): {
|
|
24
|
+
initialized: boolean;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=use-initialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-initialize.d.ts","sourceRoot":"","sources":["../../src/hooks/use-initialize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,WAAW,iBAAiB;IAEhC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE3C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAE3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,GAAE,iBAAsB;;;;EAyE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB;;EA4BlC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize Hook - KOMCA 패턴
|
|
3
|
+
* 앱 시작시 초기화 (토큰 갱신, 사용자 정보 로드)
|
|
4
|
+
*/
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
import { dispatchToHost } from '../store/store-access';
|
|
7
|
+
import { storage } from '../utils/storage';
|
|
8
|
+
/**
|
|
9
|
+
* 앱 초기화 Hook
|
|
10
|
+
*/
|
|
11
|
+
export function useInitialize(options = {}) {
|
|
12
|
+
const [initialized, setInitialized] = useState(false);
|
|
13
|
+
const [loading, setLoading] = useState(true);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const initialize = async () => {
|
|
17
|
+
try {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
// 1. Storage에서 기존 토큰 복구
|
|
20
|
+
const savedToken = storage.getAccessToken();
|
|
21
|
+
const savedUser = storage.getUser();
|
|
22
|
+
if (savedToken) {
|
|
23
|
+
// Store에 복구
|
|
24
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: savedToken });
|
|
25
|
+
if (savedUser) {
|
|
26
|
+
dispatchToHost({ type: 'app/setUser', payload: savedUser });
|
|
27
|
+
}
|
|
28
|
+
// 2. 토큰 갱신 시도 (옵션)
|
|
29
|
+
if (options.refreshToken) {
|
|
30
|
+
try {
|
|
31
|
+
const newToken = await options.refreshToken();
|
|
32
|
+
if (newToken) {
|
|
33
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: newToken });
|
|
34
|
+
storage.setAccessToken(newToken);
|
|
35
|
+
console.log('[Initialize] 토큰 갱신 성공');
|
|
36
|
+
// 3. 사용자 정보 갱신 (옵션)
|
|
37
|
+
if (options.fetchUserInfo) {
|
|
38
|
+
const userInfo = await options.fetchUserInfo();
|
|
39
|
+
if (userInfo) {
|
|
40
|
+
dispatchToHost({ type: 'app/setUser', payload: userInfo });
|
|
41
|
+
storage.setUser(userInfo);
|
|
42
|
+
console.log('[Initialize] 사용자 정보 갱신:', userInfo.email);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (refreshError) {
|
|
48
|
+
console.warn('[Initialize] 토큰 갱신 실패, 기존 토큰 사용');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 4. Recent Menu 복구
|
|
53
|
+
const savedRecentMenu = storage.getRecentMenu();
|
|
54
|
+
if (savedRecentMenu.length > 0) {
|
|
55
|
+
dispatchToHost({
|
|
56
|
+
type: 'recentMenu/setRecentMenu',
|
|
57
|
+
payload: { list: savedRecentMenu },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
setInitialized(true);
|
|
61
|
+
options.onInitialized?.();
|
|
62
|
+
console.log('[Initialize] 앱 초기화 완료');
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const error = err instanceof Error ? err : new Error('초기화 실패');
|
|
66
|
+
setError(error);
|
|
67
|
+
options.onError?.(error);
|
|
68
|
+
console.error('[Initialize] 초기화 에러:', error);
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
setLoading(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
initialize();
|
|
75
|
+
}, []);
|
|
76
|
+
return { initialized, loading, error };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 간단한 초기화 Hook (토큰/사용자 정보 복구만)
|
|
80
|
+
*/
|
|
81
|
+
export function useSimpleInitialize() {
|
|
82
|
+
const [initialized, setInitialized] = useState(false);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
// Storage에서 복구
|
|
85
|
+
const savedToken = storage.getAccessToken();
|
|
86
|
+
const savedUser = storage.getUser();
|
|
87
|
+
if (savedToken) {
|
|
88
|
+
dispatchToHost({ type: 'app/setAccessToken', payload: savedToken });
|
|
89
|
+
}
|
|
90
|
+
if (savedUser) {
|
|
91
|
+
dispatchToHost({ type: 'app/setUser', payload: savedUser });
|
|
92
|
+
}
|
|
93
|
+
// Recent Menu 복구
|
|
94
|
+
const savedRecentMenu = storage.getRecentMenu();
|
|
95
|
+
if (savedRecentMenu.length > 0) {
|
|
96
|
+
dispatchToHost({
|
|
97
|
+
type: 'recentMenu/setRecentMenu',
|
|
98
|
+
payload: { list: savedRecentMenu },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
setInitialized(true);
|
|
102
|
+
}, []);
|
|
103
|
+
return { initialized };
|
|
104
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Hooks - KOMCA 패턴
|
|
3
|
+
* Alert, Confirm 모달 관리
|
|
4
|
+
*/
|
|
5
|
+
export interface ModalHookOptions {
|
|
6
|
+
title?: string;
|
|
7
|
+
message: string;
|
|
8
|
+
confirmText?: string;
|
|
9
|
+
cancelText?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AlertModalResult {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
options: ModalHookOptions | null;
|
|
14
|
+
show: (options: ModalHookOptions | string) => Promise<void>;
|
|
15
|
+
close: () => void;
|
|
16
|
+
}
|
|
17
|
+
export interface ConfirmModalResult {
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
options: ModalHookOptions | null;
|
|
20
|
+
show: (options: ModalHookOptions | string) => Promise<boolean>;
|
|
21
|
+
close: (confirmed: boolean) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Alert 모달 Hook
|
|
25
|
+
*/
|
|
26
|
+
export declare function useAlertModal(): AlertModalResult;
|
|
27
|
+
/**
|
|
28
|
+
* Confirm 모달 Hook
|
|
29
|
+
*/
|
|
30
|
+
export declare function useConfirmModal(): ConfirmModalResult;
|
|
31
|
+
/**
|
|
32
|
+
* 비동기 Alert 모달 - 간편 사용
|
|
33
|
+
*/
|
|
34
|
+
export declare function useAsyncAlertModal(): (message: string, title?: string) => Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* 비동기 Confirm 모달 - 간편 사용
|
|
37
|
+
*/
|
|
38
|
+
export declare function useAsyncConfirmModal(): (message: string, title?: string) => Promise<boolean>;
|
|
39
|
+
//# sourceMappingURL=use-modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-modal.d.ts","sourceRoot":"","sources":["../../src/hooks/use-modal.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAGD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,gBAAgB,CA4BhD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,kBAAkB,CA4BpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,cACH,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,cACL,MAAM,UAAU,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAOvE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Hooks - KOMCA 패턴
|
|
3
|
+
* Alert, Confirm 모달 관리
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback, useState } from 'react';
|
|
6
|
+
/**
|
|
7
|
+
* Alert 모달 Hook
|
|
8
|
+
*/
|
|
9
|
+
export function useAlertModal() {
|
|
10
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
11
|
+
const [options, setOptions] = useState(null);
|
|
12
|
+
const [resolver, setResolver] = useState(null);
|
|
13
|
+
const show = useCallback((opts) => {
|
|
14
|
+
const modalOptions = typeof opts === 'string'
|
|
15
|
+
? { message: opts }
|
|
16
|
+
: opts;
|
|
17
|
+
setOptions(modalOptions);
|
|
18
|
+
setIsOpen(true);
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
setResolver(() => resolve);
|
|
21
|
+
});
|
|
22
|
+
}, []);
|
|
23
|
+
const close = useCallback(() => {
|
|
24
|
+
setIsOpen(false);
|
|
25
|
+
setOptions(null);
|
|
26
|
+
if (resolver) {
|
|
27
|
+
resolver();
|
|
28
|
+
setResolver(null);
|
|
29
|
+
}
|
|
30
|
+
}, [resolver]);
|
|
31
|
+
return { isOpen, options, show, close };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Confirm 모달 Hook
|
|
35
|
+
*/
|
|
36
|
+
export function useConfirmModal() {
|
|
37
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
38
|
+
const [options, setOptions] = useState(null);
|
|
39
|
+
const [resolver, setResolver] = useState(null);
|
|
40
|
+
const show = useCallback((opts) => {
|
|
41
|
+
const modalOptions = typeof opts === 'string'
|
|
42
|
+
? { message: opts, confirmText: '확인', cancelText: '취소' }
|
|
43
|
+
: { confirmText: '확인', cancelText: '취소', ...opts };
|
|
44
|
+
setOptions(modalOptions);
|
|
45
|
+
setIsOpen(true);
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
setResolver(() => resolve);
|
|
48
|
+
});
|
|
49
|
+
}, []);
|
|
50
|
+
const close = useCallback((confirmed) => {
|
|
51
|
+
setIsOpen(false);
|
|
52
|
+
setOptions(null);
|
|
53
|
+
if (resolver) {
|
|
54
|
+
resolver(confirmed);
|
|
55
|
+
setResolver(null);
|
|
56
|
+
}
|
|
57
|
+
}, [resolver]);
|
|
58
|
+
return { isOpen, options, show, close };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 비동기 Alert 모달 - 간편 사용
|
|
62
|
+
*/
|
|
63
|
+
export function useAsyncAlertModal() {
|
|
64
|
+
return useCallback((message, title) => {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
// 브라우저 기본 alert 사용 (커스텀 모달로 교체 가능)
|
|
67
|
+
alert(title ? `${title}\n\n${message}` : message);
|
|
68
|
+
resolve();
|
|
69
|
+
});
|
|
70
|
+
}, []);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 비동기 Confirm 모달 - 간편 사용
|
|
74
|
+
*/
|
|
75
|
+
export function useAsyncConfirmModal() {
|
|
76
|
+
return useCallback((message, title) => {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
// 브라우저 기본 confirm 사용 (커스텀 모달로 교체 가능)
|
|
79
|
+
const result = confirm(title ? `${title}\n\n${message}` : message);
|
|
80
|
+
resolve(result);
|
|
81
|
+
});
|
|
82
|
+
}, []);
|
|
83
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MFA Navigate Hook - KOMCA 패턴
|
|
3
|
+
* 서비스 인식 네비게이션
|
|
4
|
+
*/
|
|
5
|
+
import { NavigateOptions } from 'react-router-dom';
|
|
6
|
+
import { ServiceType } from '../types/service';
|
|
7
|
+
export interface MfaNavigateOptions extends NavigateOptions {
|
|
8
|
+
service?: ServiceType;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* MFA Navigate Hook
|
|
12
|
+
* 서비스 prefix를 자동으로 처리하는 네비게이션
|
|
13
|
+
*/
|
|
14
|
+
export declare function useMfaNavigate(): (to: string | {
|
|
15
|
+
pathname?: string;
|
|
16
|
+
search?: string;
|
|
17
|
+
hash?: string;
|
|
18
|
+
}, options?: MfaNavigateOptions) => void;
|
|
19
|
+
/**
|
|
20
|
+
* 현재 위치 정보 Hook
|
|
21
|
+
*/
|
|
22
|
+
export declare function useCurrentLocation(): {
|
|
23
|
+
pathname: string;
|
|
24
|
+
search: string;
|
|
25
|
+
hash: string;
|
|
26
|
+
state: any;
|
|
27
|
+
service: ServiceType | null;
|
|
28
|
+
isHostApp: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 경로 빌더
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildPath(pathname: string, service?: ServiceType, params?: Record<string, string>): string;
|
|
34
|
+
//# sourceMappingURL=use-navigate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-navigate.d.ts","sourceRoot":"","sources":["../../src/hooks/use-navigate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAA4B,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EACL,WAAW,EAKZ,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,cAAc,SAUtB,MAAM,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,YACxD,kBAAkB,UA4C/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB;;;;;;;EAWjC;AAED;;GAEG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,WAAW,EACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAkBR"}
|