@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
@@ -0,0 +1,136 @@
1
+ /**
2
+ * QueryString 유틸리티
3
+ * URL 쿼리 파라미터 파싱 및 생성
4
+ * KOMCA 패턴 업그레이드 버전 (lodash 의존성 제거)
5
+ */
6
+ /**
7
+ * URL 쿼리 스트링을 객체로 파싱
8
+ * @example
9
+ * parseQueryString('?name=홍길동&age=30')
10
+ * // { name: '홍길동', age: '30' }
11
+ */
12
+ export function parseQueryString(searchStr) {
13
+ const search = searchStr
14
+ ? searchStr.replace(/^\?/, '')
15
+ : (typeof window !== 'undefined' ? window.location.search.replace(/^\?/, '') : '');
16
+ if (!search) {
17
+ return {};
18
+ }
19
+ return search.split('&').reduce((acc, token) => {
20
+ const [key, value] = token.split('=');
21
+ if (key) {
22
+ acc[decodeURIComponent(key)] = value !== undefined
23
+ ? decodeURIComponent(value)
24
+ : undefined;
25
+ }
26
+ return acc;
27
+ }, {});
28
+ }
29
+ /**
30
+ * 객체를 쿼리 스트링으로 변환
31
+ * @example
32
+ * createQueryString({ name: '홍길동', age: 30 })
33
+ * // 'name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=30'
34
+ */
35
+ export function createQueryString(params) {
36
+ const entries = Object.entries(params)
37
+ .filter(([_, value]) => value !== undefined && value !== null && value !== '')
38
+ .map(([key, value]) => {
39
+ return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
40
+ });
41
+ return entries.join('&');
42
+ }
43
+ /**
44
+ * 현재 URL에 쿼리 파라미터 추가/업데이트
45
+ * @example
46
+ * updateQueryParams({ page: 2, sort: 'name' })
47
+ * // 현재 URL에 ?page=2&sort=name 추가
48
+ */
49
+ export function updateQueryParams(params, options) {
50
+ if (typeof window === 'undefined')
51
+ return;
52
+ const currentParams = parseQueryString();
53
+ const newParams = { ...currentParams };
54
+ // 새 파라미터 추가/업데이트
55
+ Object.entries(params).forEach(([key, value]) => {
56
+ if (value === undefined || value === null) {
57
+ delete newParams[key];
58
+ }
59
+ else {
60
+ newParams[key] = String(value);
61
+ }
62
+ });
63
+ const queryString = createQueryString(newParams);
64
+ const newUrl = `${window.location.pathname}${queryString ? `?${queryString}` : ''}`;
65
+ if (options?.replace) {
66
+ window.history.replaceState(null, '', newUrl);
67
+ }
68
+ else {
69
+ window.history.pushState(null, '', newUrl);
70
+ }
71
+ }
72
+ /**
73
+ * 특정 쿼리 파라미터 값 가져오기
74
+ * @example
75
+ * getQueryParam('page') // '1'
76
+ * getQueryParam('page', '1') // 기본값 '1'
77
+ */
78
+ export function getQueryParam(key, defaultValue) {
79
+ const params = parseQueryString();
80
+ return params[key] ?? defaultValue;
81
+ }
82
+ /**
83
+ * 특정 쿼리 파라미터 삭제
84
+ * @example removeQueryParam('page')
85
+ */
86
+ export function removeQueryParam(key, options) {
87
+ updateQueryParams({ [key]: undefined }, options);
88
+ }
89
+ /**
90
+ * 모든 쿼리 파라미터 삭제
91
+ */
92
+ export function clearQueryParams(options) {
93
+ if (typeof window === 'undefined')
94
+ return;
95
+ const newUrl = window.location.pathname;
96
+ if (options?.replace) {
97
+ window.history.replaceState(null, '', newUrl);
98
+ }
99
+ else {
100
+ window.history.pushState(null, '', newUrl);
101
+ }
102
+ }
103
+ /**
104
+ * URL에 쿼리 스트링 추가
105
+ * @example
106
+ * appendQueryString('/users', { page: 1, size: 10 })
107
+ * // '/users?page=1&size=10'
108
+ */
109
+ export function appendQueryString(url, params) {
110
+ const queryString = createQueryString(params);
111
+ if (!queryString)
112
+ return url;
113
+ const separator = url.includes('?') ? '&' : '?';
114
+ return `${url}${separator}${queryString}`;
115
+ }
116
+ /**
117
+ * 쿼리 파라미터를 숫자로 파싱
118
+ * @example getQueryParamAsNumber('page', 1) // 1
119
+ */
120
+ export function getQueryParamAsNumber(key, defaultValue = 0) {
121
+ const value = getQueryParam(key);
122
+ if (value === undefined)
123
+ return defaultValue;
124
+ const parsed = parseInt(value, 10);
125
+ return isNaN(parsed) ? defaultValue : parsed;
126
+ }
127
+ /**
128
+ * 쿼리 파라미터를 boolean으로 파싱
129
+ * @example getQueryParamAsBoolean('active', false) // true
130
+ */
131
+ export function getQueryParamAsBoolean(key, defaultValue = false) {
132
+ const value = getQueryParam(key);
133
+ if (value === undefined)
134
+ return defaultValue;
135
+ return value === 'true' || value === '1';
136
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Storage 유틸리티 - KOMCA 패턴
2
+ * Storage 유틸리티
3
3
  * localStorage/sessionStorage 관리
4
4
  */
5
5
  import { User, RecentMenu } from '../types';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Storage 유틸리티 - KOMCA 패턴
2
+ * Storage 유틸리티
3
3
  * localStorage/sessionStorage 관리
4
4
  */
5
5
  // Storage 키 상수
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Validation 유틸리티
3
+ * 이메일, 비밀번호, 전화번호 등 검증 함수
4
+ * KOMCA 패턴 업그레이드 버전
5
+ */
6
+ /**
7
+ * 이메일 형식 검증
8
+ * @example isValidEmail('test@example.com') // true
9
+ */
10
+ export declare function isValidEmail(email: string): boolean;
11
+ export interface PasswordValidationResult {
12
+ isValid: boolean;
13
+ errors: string[];
14
+ strength: 'weak' | 'medium' | 'strong';
15
+ }
16
+ export interface PasswordOptions {
17
+ minLength?: number;
18
+ maxLength?: number;
19
+ requireUppercase?: boolean;
20
+ requireLowercase?: boolean;
21
+ requireNumber?: boolean;
22
+ requireSpecialChar?: boolean;
23
+ }
24
+ /**
25
+ * 비밀번호 검증 (상세 결과)
26
+ * @example
27
+ * const result = validatePassword('Test1234!');
28
+ * // { isValid: true, errors: [], strength: 'strong' }
29
+ */
30
+ export declare function validatePassword(password: string, options?: PasswordOptions): PasswordValidationResult;
31
+ /**
32
+ * 비밀번호 형식 검증 (간단)
33
+ * @example isValidPassword('Test1234!') // true
34
+ */
35
+ export declare function isValidPassword(password: string, options?: PasswordOptions): boolean;
36
+ /**
37
+ * 한국 전화번호 검증
38
+ * @example isValidPhone('010-1234-5678') // true
39
+ * @example isValidPhone('01012345678') // true
40
+ */
41
+ export declare function isValidPhone(phone: string): boolean;
42
+ /**
43
+ * 휴대폰 번호만 검증
44
+ * @example isValidMobile('010-1234-5678') // true
45
+ */
46
+ export declare function isValidMobile(phone: string): boolean;
47
+ /**
48
+ * URL 형식 검증
49
+ * @example isValidUrl('https://example.com') // true
50
+ */
51
+ export declare function isValidUrl(url: string): boolean;
52
+ /**
53
+ * IPv4 주소 검증
54
+ * @example isValidIPv4('192.168.0.1') // true
55
+ */
56
+ export declare function isValidIPv4(ip: string): boolean;
57
+ /**
58
+ * 사업자등록번호 검증 (한국)
59
+ * @example isValidBusinessNumber('123-45-67890') // true
60
+ */
61
+ export declare function isValidBusinessNumber(number: string): boolean;
62
+ /**
63
+ * 주민등록번호 형식 검증 (실제 유효성은 검증하지 않음)
64
+ * @example isValidResidentNumber('900101-1234567') // 형식만 검증
65
+ */
66
+ export declare function isValidResidentNumberFormat(number: string): boolean;
67
+ /**
68
+ * 빈 값 검증
69
+ * @example isEmpty('') // true
70
+ * @example isEmpty(null) // true
71
+ * @example isEmpty([]) // true
72
+ */
73
+ export declare function isEmpty(value: any): boolean;
74
+ /**
75
+ * 빈 값이 아닌지 검증
76
+ */
77
+ export declare function isNotEmpty(value: any): boolean;
78
+ /**
79
+ * 숫자만 포함하는지 검증
80
+ * @example isNumericOnly('12345') // true
81
+ */
82
+ export declare function isNumericOnly(value: string): boolean;
83
+ /**
84
+ * 한글만 포함하는지 검증
85
+ * @example isKoreanOnly('홍길동') // true
86
+ */
87
+ export declare function isKoreanOnly(value: string): boolean;
88
+ /**
89
+ * 영문만 포함하는지 검증
90
+ * @example isAlphaOnly('abc') // true
91
+ */
92
+ export declare function isAlphaOnly(value: string): boolean;
93
+ /**
94
+ * 영문+숫자만 포함하는지 검증
95
+ * @example isAlphanumeric('abc123') // true
96
+ */
97
+ export declare function isAlphanumeric(value: string): boolean;
98
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAInD;AAMD,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAWD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,eAAoB,GAC5B,wBAAwB,CAsD1B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAEpF;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAcnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIpD;AAMD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQ/C;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAI/C;AAMD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB7D;AAMD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAUnE;AAMD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAM3C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAE9C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGlD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGrD"}
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Validation 유틸리티
3
+ * 이메일, 비밀번호, 전화번호 등 검증 함수
4
+ * KOMCA 패턴 업그레이드 버전
5
+ */
6
+ // ============================================
7
+ // 이메일 검증
8
+ // ============================================
9
+ /**
10
+ * 이메일 형식 검증
11
+ * @example isValidEmail('test@example.com') // true
12
+ */
13
+ export function isValidEmail(email) {
14
+ if (!email || typeof email !== 'string')
15
+ return false;
16
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
17
+ return emailRegex.test(email.trim());
18
+ }
19
+ const DEFAULT_PASSWORD_OPTIONS = {
20
+ minLength: 8,
21
+ maxLength: 50,
22
+ requireUppercase: true,
23
+ requireLowercase: true,
24
+ requireNumber: true,
25
+ requireSpecialChar: true,
26
+ };
27
+ /**
28
+ * 비밀번호 검증 (상세 결과)
29
+ * @example
30
+ * const result = validatePassword('Test1234!');
31
+ * // { isValid: true, errors: [], strength: 'strong' }
32
+ */
33
+ export function validatePassword(password, options = {}) {
34
+ const opts = { ...DEFAULT_PASSWORD_OPTIONS, ...options };
35
+ const errors = [];
36
+ if (!password || typeof password !== 'string') {
37
+ return { isValid: false, errors: ['비밀번호를 입력해주세요.'], strength: 'weak' };
38
+ }
39
+ // 길이 검증
40
+ if (opts.minLength && password.length < opts.minLength) {
41
+ errors.push(`비밀번호는 ${opts.minLength}자 이상이어야 합니다.`);
42
+ }
43
+ if (opts.maxLength && password.length > opts.maxLength) {
44
+ errors.push(`비밀번호는 ${opts.maxLength}자 이하여야 합니다.`);
45
+ }
46
+ // 대문자 검증
47
+ if (opts.requireUppercase && !/[A-Z]/.test(password)) {
48
+ errors.push('대문자를 포함해야 합니다.');
49
+ }
50
+ // 소문자 검증
51
+ if (opts.requireLowercase && !/[a-z]/.test(password)) {
52
+ errors.push('소문자를 포함해야 합니다.');
53
+ }
54
+ // 숫자 검증
55
+ if (opts.requireNumber && !/[0-9]/.test(password)) {
56
+ errors.push('숫자를 포함해야 합니다.');
57
+ }
58
+ // 특수문자 검증
59
+ if (opts.requireSpecialChar && !/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
60
+ errors.push('특수문자를 포함해야 합니다.');
61
+ }
62
+ // 강도 계산
63
+ let strength = 'weak';
64
+ let score = 0;
65
+ if (password.length >= 8)
66
+ score++;
67
+ if (password.length >= 12)
68
+ score++;
69
+ if (/[A-Z]/.test(password))
70
+ score++;
71
+ if (/[a-z]/.test(password))
72
+ score++;
73
+ if (/[0-9]/.test(password))
74
+ score++;
75
+ if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password))
76
+ score++;
77
+ if (score >= 5)
78
+ strength = 'strong';
79
+ else if (score >= 3)
80
+ strength = 'medium';
81
+ return {
82
+ isValid: errors.length === 0,
83
+ errors,
84
+ strength,
85
+ };
86
+ }
87
+ /**
88
+ * 비밀번호 형식 검증 (간단)
89
+ * @example isValidPassword('Test1234!') // true
90
+ */
91
+ export function isValidPassword(password, options) {
92
+ return validatePassword(password, options).isValid;
93
+ }
94
+ // ============================================
95
+ // 전화번호 검증
96
+ // ============================================
97
+ /**
98
+ * 한국 전화번호 검증
99
+ * @example isValidPhone('010-1234-5678') // true
100
+ * @example isValidPhone('01012345678') // true
101
+ */
102
+ export function isValidPhone(phone) {
103
+ if (!phone || typeof phone !== 'string')
104
+ return false;
105
+ const cleaned = phone.replace(/[^0-9]/g, '');
106
+ // 휴대폰 (010, 011, 016, 017, 018, 019)
107
+ if (/^01[0-9]\d{7,8}$/.test(cleaned))
108
+ return true;
109
+ // 서울 지역번호
110
+ if (/^02\d{7,8}$/.test(cleaned))
111
+ return true;
112
+ // 기타 지역번호
113
+ if (/^0[3-6][0-9]\d{7,8}$/.test(cleaned))
114
+ return true;
115
+ return false;
116
+ }
117
+ /**
118
+ * 휴대폰 번호만 검증
119
+ * @example isValidMobile('010-1234-5678') // true
120
+ */
121
+ export function isValidMobile(phone) {
122
+ if (!phone || typeof phone !== 'string')
123
+ return false;
124
+ const cleaned = phone.replace(/[^0-9]/g, '');
125
+ return /^01[016789]\d{7,8}$/.test(cleaned);
126
+ }
127
+ // ============================================
128
+ // URL 검증
129
+ // ============================================
130
+ /**
131
+ * URL 형식 검증
132
+ * @example isValidUrl('https://example.com') // true
133
+ */
134
+ export function isValidUrl(url) {
135
+ if (!url || typeof url !== 'string')
136
+ return false;
137
+ try {
138
+ new URL(url);
139
+ return true;
140
+ }
141
+ catch {
142
+ return false;
143
+ }
144
+ }
145
+ // ============================================
146
+ // IP 주소 검증
147
+ // ============================================
148
+ /**
149
+ * IPv4 주소 검증
150
+ * @example isValidIPv4('192.168.0.1') // true
151
+ */
152
+ export function isValidIPv4(ip) {
153
+ if (!ip || typeof ip !== 'string')
154
+ return false;
155
+ const ipv4Regex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
156
+ return ipv4Regex.test(ip);
157
+ }
158
+ // ============================================
159
+ // 사업자등록번호 검증
160
+ // ============================================
161
+ /**
162
+ * 사업자등록번호 검증 (한국)
163
+ * @example isValidBusinessNumber('123-45-67890') // true
164
+ */
165
+ export function isValidBusinessNumber(number) {
166
+ if (!number || typeof number !== 'string')
167
+ return false;
168
+ const cleaned = number.replace(/[^0-9]/g, '');
169
+ if (cleaned.length !== 10)
170
+ return false;
171
+ const checkSum = [1, 3, 7, 1, 3, 7, 1, 3, 5];
172
+ let sum = 0;
173
+ for (let i = 0; i < 9; i++) {
174
+ sum += parseInt(cleaned[i]) * checkSum[i];
175
+ }
176
+ sum += Math.floor((parseInt(cleaned[8]) * 5) / 10);
177
+ const remainder = (10 - (sum % 10)) % 10;
178
+ return remainder === parseInt(cleaned[9]);
179
+ }
180
+ // ============================================
181
+ // 주민등록번호 검증
182
+ // ============================================
183
+ /**
184
+ * 주민등록번호 형식 검증 (실제 유효성은 검증하지 않음)
185
+ * @example isValidResidentNumber('900101-1234567') // 형식만 검증
186
+ */
187
+ export function isValidResidentNumberFormat(number) {
188
+ if (!number || typeof number !== 'string')
189
+ return false;
190
+ const cleaned = number.replace(/[^0-9]/g, '');
191
+ if (cleaned.length !== 13)
192
+ return false;
193
+ // 성별 코드 검증 (1~4)
194
+ const genderCode = parseInt(cleaned[6]);
195
+ if (genderCode < 1 || genderCode > 4)
196
+ return false;
197
+ return true;
198
+ }
199
+ // ============================================
200
+ // 공통 유틸리티
201
+ // ============================================
202
+ /**
203
+ * 빈 값 검증
204
+ * @example isEmpty('') // true
205
+ * @example isEmpty(null) // true
206
+ * @example isEmpty([]) // true
207
+ */
208
+ export function isEmpty(value) {
209
+ if (value === null || value === undefined)
210
+ return true;
211
+ if (typeof value === 'string')
212
+ return value.trim() === '';
213
+ if (Array.isArray(value))
214
+ return value.length === 0;
215
+ if (typeof value === 'object')
216
+ return Object.keys(value).length === 0;
217
+ return false;
218
+ }
219
+ /**
220
+ * 빈 값이 아닌지 검증
221
+ */
222
+ export function isNotEmpty(value) {
223
+ return !isEmpty(value);
224
+ }
225
+ /**
226
+ * 숫자만 포함하는지 검증
227
+ * @example isNumericOnly('12345') // true
228
+ */
229
+ export function isNumericOnly(value) {
230
+ if (!value || typeof value !== 'string')
231
+ return false;
232
+ return /^\d+$/.test(value);
233
+ }
234
+ /**
235
+ * 한글만 포함하는지 검증
236
+ * @example isKoreanOnly('홍길동') // true
237
+ */
238
+ export function isKoreanOnly(value) {
239
+ if (!value || typeof value !== 'string')
240
+ return false;
241
+ return /^[가-힣]+$/.test(value);
242
+ }
243
+ /**
244
+ * 영문만 포함하는지 검증
245
+ * @example isAlphaOnly('abc') // true
246
+ */
247
+ export function isAlphaOnly(value) {
248
+ if (!value || typeof value !== 'string')
249
+ return false;
250
+ return /^[a-zA-Z]+$/.test(value);
251
+ }
252
+ /**
253
+ * 영문+숫자만 포함하는지 검증
254
+ * @example isAlphanumeric('abc123') // true
255
+ */
256
+ export function isAlphanumeric(value) {
257
+ if (!value || typeof value !== 'string')
258
+ return false;
259
+ return /^[a-zA-Z0-9]+$/.test(value);
260
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sonhoseong/mfa-lib",
3
- "version": "1.3.7",
4
- "description": "MFA 공통 라이브러리 - KOMCA 패턴",
3
+ "version": "1.3.10",
4
+ "description": "MFA 공통 라이브러리",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [
@@ -9,16 +9,18 @@
9
9
  "README.md"
10
10
  ],
11
11
  "scripts": {
12
- "build": "tsc",
12
+ "build": "tsc && copyfiles -u 1 \"src/**/*.css\" dist",
13
13
  "watch": "tsc --watch"
14
14
  },
15
15
  "dependencies": {
16
16
  "@reduxjs/toolkit": "^2.11.2",
17
+ "@supabase/supabase-js": "^2.93.3",
17
18
  "axios": "^1.7.9",
18
19
  "uuid": "^11.1.0"
19
20
  },
20
21
  "devDependencies": {
21
22
  "@types/uuid": "^10.0.0",
23
+ "copyfiles": "^2.4.1",
22
24
  "typescript": "^5.0.0"
23
25
  },
24
26
  "peerDependencies": {