@blastlabs/utils 1.12.0 → 1.13.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.
Files changed (36) hide show
  1. package/README.md +20 -13
  2. package/bin/entity-generator-guide.md +126 -0
  3. package/bin/generate-entity.cjs +282 -0
  4. package/dist/components/auth/AuthGuard.d.ts +95 -0
  5. package/dist/components/auth/AuthGuard.d.ts.map +1 -0
  6. package/dist/components/auth/AuthGuard.js +103 -0
  7. package/dist/components/auth/index.d.ts +5 -0
  8. package/dist/components/auth/index.d.ts.map +1 -0
  9. package/dist/components/auth/index.js +4 -0
  10. package/dist/components/dev/ApiLogger.d.ts +1 -1
  11. package/dist/components/dev/ApiLogger.js +1 -1
  12. package/dist/components/index.d.ts +1 -0
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/components/index.js +1 -0
  15. package/dist/hooks/auth/__tests__/useAuth.test.d.ts +2 -0
  16. package/dist/hooks/auth/__tests__/useAuth.test.d.ts.map +1 -0
  17. package/dist/hooks/auth/__tests__/useAuth.test.js +139 -0
  18. package/dist/hooks/auth/index.d.ts +5 -0
  19. package/dist/hooks/auth/index.d.ts.map +1 -0
  20. package/dist/hooks/auth/index.js +4 -0
  21. package/dist/hooks/auth/useAuth.d.ts +275 -0
  22. package/dist/hooks/auth/useAuth.d.ts.map +1 -0
  23. package/dist/hooks/auth/useAuth.js +384 -0
  24. package/dist/hooks/form/useCRUDForm.d.ts +7 -28
  25. package/dist/hooks/form/useCRUDForm.d.ts.map +1 -1
  26. package/dist/hooks/index.d.ts +1 -0
  27. package/dist/hooks/index.d.ts.map +1 -1
  28. package/dist/hooks/index.js +2 -0
  29. package/dist/hooks/ui/index.d.ts +1 -0
  30. package/dist/hooks/ui/index.d.ts.map +1 -1
  31. package/dist/hooks/ui/index.js +1 -0
  32. package/dist/hooks/ui/useTabs.d.ts +33 -0
  33. package/dist/hooks/ui/useTabs.d.ts.map +1 -0
  34. package/dist/hooks/ui/useTabs.js +117 -0
  35. package/dist/index.js +1 -1
  36. package/package.json +6 -2
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { useEffect } from 'react';
3
+ import { useAuth as useAuthHook } from '../../hooks/auth';
4
+ /**
5
+ * 인증 가드 컴포넌트
6
+ * 인증이 필요한 페이지를 보호하는 컴포넌트
7
+ *
8
+ * 내부적으로 useAuth 훅을 호출하여 localStorage/sessionStorage에서 인증 상태를 확인합니다.
9
+ * 각 AuthGuard는 독립적으로 동작하며, storage를 통해 자동으로 동기화됩니다.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // 기본 사용 (localStorage 자동 로드)
14
+ * import { AuthGuard } from '@blastlabs/utils';
15
+ * import { useRouter } from 'next/navigation';
16
+ *
17
+ * function ProtectedPage() {
18
+ * const router = useRouter();
19
+ *
20
+ * return (
21
+ * <AuthGuard
22
+ * authOptions={{ autoLoadUser: true }}
23
+ * onUnauthenticated={() => router.push('/login')}
24
+ * fallback={<LoadingSpinner />}
25
+ * >
26
+ * <YourProtectedContent />
27
+ * </AuthGuard>
28
+ * );
29
+ * }
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * // React Router에서 사용
35
+ * import { useNavigate } from 'react-router-dom';
36
+ *
37
+ * function ProtectedPage() {
38
+ * const navigate = useNavigate();
39
+ *
40
+ * return (
41
+ * <AuthGuard
42
+ * authOptions={{ autoLoadUser: true }}
43
+ * onUnauthenticated={() => navigate('/login')}
44
+ * >
45
+ * <YourProtectedContent />
46
+ * </AuthGuard>
47
+ * );
48
+ * }
49
+ * ```
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // 리다이렉트 없이 대체 UI 표시
54
+ * <AuthGuard
55
+ * authOptions={{ autoLoadUser: true }}
56
+ * unauthenticatedFallback={
57
+ * <div>
58
+ * <h1>로그인이 필요합니다</h1>
59
+ * <Link to="/login">로그인 페이지로 이동</Link>
60
+ * </div>
61
+ * }
62
+ * >
63
+ * <YourProtectedContent />
64
+ * </AuthGuard>
65
+ * ```
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * // 여러 페이지에서 사용해도 storage로 자동 동기화
70
+ * // Page1.tsx
71
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
72
+ * <Page1Content />
73
+ * </AuthGuard>
74
+ *
75
+ * // Page2.tsx - 동일한 인증 상태 공유 (localStorage 사용)
76
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
77
+ * <Page2Content />
78
+ * </AuthGuard>
79
+ * ```
80
+ */
81
+ export function AuthGuard({ children, fallback, onUnauthenticated, unauthenticatedFallback, authOptions, }) {
82
+ const { isAuthenticated, isLoading } = useAuthHook(authOptions);
83
+ useEffect(() => {
84
+ if (!isLoading && !isAuthenticated && onUnauthenticated) {
85
+ onUnauthenticated();
86
+ }
87
+ }, [isAuthenticated, isLoading, onUnauthenticated]);
88
+ // 로딩 중이면 fallback 표시
89
+ if (isLoading) {
90
+ return React.createElement(React.Fragment, null, fallback || null);
91
+ }
92
+ // 인증되지 않았으면
93
+ if (!isAuthenticated) {
94
+ // onUnauthenticated 콜백이 있으면 리다이렉트 중이므로 null
95
+ if (onUnauthenticated) {
96
+ return null;
97
+ }
98
+ // 콜백이 없으면 unauthenticatedFallback 표시
99
+ return React.createElement(React.Fragment, null, unauthenticatedFallback || null);
100
+ }
101
+ // 인증되었으면 자식 컴포넌트 렌더링
102
+ return React.createElement(React.Fragment, null, children);
103
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Authentication Components
3
+ */
4
+ export * from './AuthGuard';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Authentication Components
3
+ */
4
+ export * from './AuthGuard';
@@ -92,7 +92,7 @@ export declare function clearApiLogs(): void;
92
92
  * @example
93
93
  * ```tsx
94
94
  * // fetch wrapper 사용
95
- * import { addApiLog } from 'goodchuck-utils/components/dev';
95
+ * import { addApiLog } from '@blastlabs/utils/components/dev';
96
96
  *
97
97
  * const originalFetch = window.fetch;
98
98
  * window.fetch = async (...args) => {
@@ -89,7 +89,7 @@ export function clearApiLogs() {
89
89
  * @example
90
90
  * ```tsx
91
91
  * // fetch wrapper 사용
92
- * import { addApiLog } from 'goodchuck-utils/components/dev';
92
+ * import { addApiLog } from '@blastlabs/utils/components/dev';
93
93
  *
94
94
  * const originalFetch = window.fetch;
95
95
  * window.fetch = async (...args) => {
@@ -5,4 +5,5 @@
5
5
  * Note: Uses Tailwind CSS classes - ensure Tailwind is configured in your project.
6
6
  */
7
7
  export * from './dev';
8
+ export * from './auth';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC"}
@@ -5,3 +5,4 @@
5
5
  * Note: Uses Tailwind CSS classes - ensure Tailwind is configured in your project.
6
6
  */
7
7
  export * from './dev';
8
+ export * from './auth';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useAuth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuth.test.d.ts","sourceRoot":"","sources":["../../../../src/hooks/auth/__tests__/useAuth.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,139 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { renderHook, act, waitFor } from '@testing-library/react';
3
+ import { useAuth } from '../useAuth';
4
+ describe('useAuth', () => {
5
+ beforeEach(() => {
6
+ localStorage.clear();
7
+ sessionStorage.clear();
8
+ vi.clearAllMocks();
9
+ });
10
+ afterEach(() => {
11
+ localStorage.clear();
12
+ sessionStorage.clear();
13
+ });
14
+ describe('Initialization', () => {
15
+ it('should initialize with no user', () => {
16
+ const { result } = renderHook(() => useAuth({ autoLoadUser: false }));
17
+ expect(result.current.user).toBeNull();
18
+ expect(result.current.isAuthenticated).toBe(false);
19
+ expect(result.current.isLoading).toBe(false);
20
+ expect(result.current.token).toBeNull();
21
+ });
22
+ it('should load user from localStorage on mount', async () => {
23
+ const mockUser = { id: '1', name: 'Test User' };
24
+ localStorage.setItem('auth_token', 'test-token');
25
+ localStorage.setItem('auth_user', JSON.stringify(mockUser));
26
+ const { result } = renderHook(() => useAuth({ autoLoadUser: true }));
27
+ await waitFor(() => {
28
+ expect(result.current.user).toEqual(mockUser);
29
+ expect(result.current.isAuthenticated).toBe(true);
30
+ });
31
+ });
32
+ });
33
+ describe('Login', () => {
34
+ it('should login successfully', async () => {
35
+ const mockUser = { id: '1', name: 'Test User' };
36
+ const loginFn = vi.fn().mockResolvedValue({ user: mockUser, token: 'test-token' });
37
+ const onLoginSuccess = vi.fn();
38
+ const { result } = renderHook(() => useAuth({ loginFn, onLoginSuccess, autoLoadUser: false }));
39
+ await act(async () => {
40
+ await result.current.login({ email: 'test@example.com', password: 'password' });
41
+ });
42
+ expect(result.current.user).toEqual(mockUser);
43
+ expect(result.current.isAuthenticated).toBe(true);
44
+ expect(result.current.token).toBe('test-token');
45
+ expect(onLoginSuccess).toHaveBeenCalledWith(mockUser);
46
+ expect(localStorage.getItem('auth_token')).toBe('test-token');
47
+ });
48
+ it('should handle login error', async () => {
49
+ const loginError = new Error('Invalid credentials');
50
+ const loginFn = vi.fn().mockRejectedValue(loginError);
51
+ const onError = vi.fn();
52
+ const { result } = renderHook(() => useAuth({ loginFn, onError, autoLoadUser: false }));
53
+ let thrownError;
54
+ try {
55
+ await act(async () => {
56
+ await result.current.login({ email: 'test@example.com', password: 'wrong' });
57
+ });
58
+ }
59
+ catch (error) {
60
+ thrownError = error;
61
+ }
62
+ expect(thrownError).toEqual(loginError);
63
+ expect(result.current.user).toBeNull();
64
+ expect(result.current.isAuthenticated).toBe(false);
65
+ expect(onError).toHaveBeenCalledWith(loginError, 'login');
66
+ });
67
+ });
68
+ describe('Logout', () => {
69
+ it('should logout successfully', async () => {
70
+ const mockUser = { id: '1', name: 'Test User' };
71
+ const loginFn = vi.fn().mockResolvedValue({ user: mockUser, token: 'test-token' });
72
+ const logoutFn = vi.fn().mockResolvedValue(undefined);
73
+ const onLogoutSuccess = vi.fn();
74
+ const { result } = renderHook(() => useAuth({ loginFn, logoutFn, onLogoutSuccess, autoLoadUser: false }));
75
+ await act(async () => {
76
+ await result.current.login({ email: 'test@example.com', password: 'password' });
77
+ });
78
+ expect(result.current.isAuthenticated).toBe(true);
79
+ await act(async () => {
80
+ await result.current.logout();
81
+ });
82
+ expect(result.current.user).toBeNull();
83
+ expect(result.current.isAuthenticated).toBe(false);
84
+ expect(result.current.token).toBeNull();
85
+ expect(onLogoutSuccess).toHaveBeenCalled();
86
+ expect(localStorage.getItem('auth_token')).toBeNull();
87
+ });
88
+ });
89
+ describe('Register', () => {
90
+ it('should register successfully', async () => {
91
+ const mockUser = { id: '1', name: 'New User' };
92
+ const registerFn = vi.fn().mockResolvedValue({ user: mockUser, token: 'new-token' });
93
+ const onRegisterSuccess = vi.fn();
94
+ const { result } = renderHook(() => useAuth({ registerFn, onRegisterSuccess, autoLoadUser: false }));
95
+ await act(async () => {
96
+ await result.current.register({ email: 'new@example.com', password: 'password', name: 'New User' });
97
+ });
98
+ expect(result.current.user).toEqual(mockUser);
99
+ expect(result.current.isAuthenticated).toBe(true);
100
+ expect(result.current.token).toBe('new-token');
101
+ expect(onRegisterSuccess).toHaveBeenCalledWith(mockUser);
102
+ });
103
+ });
104
+ describe('Token Management', () => {
105
+ it('should manually set token', async () => {
106
+ const { result } = renderHook(() => useAuth({ autoLoadUser: false }));
107
+ await waitFor(() => {
108
+ expect(result.current.isLoading).toBe(false);
109
+ });
110
+ act(() => {
111
+ result.current.setToken('manual-token');
112
+ });
113
+ expect(result.current.token).toBe('manual-token');
114
+ expect(localStorage.getItem('auth_token')).toBe('manual-token');
115
+ });
116
+ it('should manually set user', async () => {
117
+ const mockUser = { id: '1', name: 'Manual User' };
118
+ const { result } = renderHook(() => useAuth({ autoLoadUser: false }));
119
+ await waitFor(() => {
120
+ expect(result.current.isLoading).toBe(false);
121
+ });
122
+ act(() => {
123
+ result.current.setUser(mockUser);
124
+ });
125
+ expect(result.current.user).toEqual(mockUser);
126
+ expect(result.current.isAuthenticated).toBe(true);
127
+ });
128
+ it('should use sessionStorage when useSessionStorage is true', async () => {
129
+ const mockUser = { id: '1', name: 'Test User' };
130
+ const loginFn = vi.fn().mockResolvedValue({ user: mockUser, token: 'session-token' });
131
+ const { result } = renderHook(() => useAuth({ loginFn, useSessionStorage: true, autoLoadUser: false }));
132
+ await act(async () => {
133
+ await result.current.login({ email: 'test@example.com', password: 'password' });
134
+ });
135
+ expect(sessionStorage.getItem('auth_token')).toBe('session-token');
136
+ expect(localStorage.getItem('auth_token')).toBeNull();
137
+ });
138
+ });
139
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Authentication Hooks
3
+ */
4
+ export * from './useAuth';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,WAAW,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Authentication Hooks
3
+ */
4
+ export * from './useAuth';
@@ -0,0 +1,275 @@
1
+ /**
2
+ * 인증 상태
3
+ */
4
+ export interface AuthUser {
5
+ [key: string]: any;
6
+ }
7
+ /**
8
+ * 로그인 credentials
9
+ */
10
+ export interface LoginCredentials {
11
+ [key: string]: any;
12
+ }
13
+ /**
14
+ * 회원가입 데이터
15
+ */
16
+ export interface RegisterData {
17
+ [key: string]: any;
18
+ }
19
+ /**
20
+ * useAuth 옵션
21
+ */
22
+ export interface UseAuthOptions<TUser extends AuthUser = AuthUser> {
23
+ /** 로그인 API 함수 */
24
+ loginFn?: (credentials: LoginCredentials) => Promise<{
25
+ user: TUser;
26
+ token?: string;
27
+ }>;
28
+ /** 로그아웃 API 함수 */
29
+ logoutFn?: () => Promise<void>;
30
+ /** 회원가입 API 함수 */
31
+ registerFn?: (data: RegisterData) => Promise<{
32
+ user: TUser;
33
+ token?: string;
34
+ }>;
35
+ /** 현재 사용자 정보 가져오기 */
36
+ getUserFn?: () => Promise<TUser>;
37
+ /** 토큰 갱신 함수 */
38
+ refreshTokenFn?: () => Promise<{
39
+ token: string;
40
+ }>;
41
+ /** 로그인 성공 시 콜백 */
42
+ onLoginSuccess?: (user: TUser) => void;
43
+ /** 로그아웃 성공 시 콜백 */
44
+ onLogoutSuccess?: () => void;
45
+ /** 회원가입 성공 시 콜백 */
46
+ onRegisterSuccess?: (user: TUser) => void;
47
+ /** 에러 발생 시 콜백 */
48
+ onError?: (error: Error, action: 'login' | 'logout' | 'register' | 'refresh') => void;
49
+ /**
50
+ * localStorage에 저장할 토큰 키 (기본값: 'auth_token')
51
+ *
52
+ * @example
53
+ * ```
54
+ * // localStorage 저장 예시:
55
+ * localStorage.setItem('auth_token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
56
+ * ```
57
+ */
58
+ storageKey?: string;
59
+ /**
60
+ * 사용자 정보 저장 키 (기본값: 'auth_user')
61
+ *
62
+ * @example
63
+ * ```
64
+ * // localStorage 저장 예시:
65
+ * localStorage.setItem('auth_user', '{"id":"123","name":"John Doe","email":"john@example.com"}')
66
+ * ```
67
+ */
68
+ userStorageKey?: string;
69
+ /**
70
+ * localStorage 대신 sessionStorage 사용 여부 (기본값: false)
71
+ *
72
+ * - false (기본값): localStorage 사용 → 브라우저 종료 후에도 유지
73
+ * - true: sessionStorage 사용 → 브라우저/탭 종료 시 삭제
74
+ *
75
+ * 참고: login() 함수의 rememberMe 옵션으로 동적 선택도 가능합니다.
76
+ */
77
+ useSessionStorage?: boolean;
78
+ /** 자동 토큰 갱신 여부 (기본값: false) */
79
+ autoRefresh?: boolean;
80
+ /** 토큰 갱신 간격 (밀리초, 기본값: 300000 = 5분) */
81
+ refreshInterval?: number;
82
+ /** 초기화 시 사용자 정보 자동 로드 여부 (기본값: true) */
83
+ autoLoadUser?: boolean;
84
+ }
85
+ /**
86
+ * useAuth 반환 타입
87
+ */
88
+ export interface UseAuthReturn<TUser extends AuthUser = AuthUser> {
89
+ /** 현재 로그인한 사용자 */
90
+ user: TUser | null;
91
+ /** 인증 여부 */
92
+ isAuthenticated: boolean;
93
+ /** 로딩 중 여부 */
94
+ isLoading: boolean;
95
+ /** 로그인 진행 중 여부 */
96
+ isLoggingIn: boolean;
97
+ /** 로그아웃 진행 중 여부 */
98
+ isLoggingOut: boolean;
99
+ /** 회원가입 진행 중 여부 */
100
+ isRegistering: boolean;
101
+ /** 에러 */
102
+ error: Error | null;
103
+ /** 로그인 함수 */
104
+ login: (credentials: LoginCredentials, options?: {
105
+ rememberMe?: boolean;
106
+ }) => Promise<void>;
107
+ /** 로그아웃 함수 */
108
+ logout: () => Promise<void>;
109
+ /** 회원가입 함수 */
110
+ register: (data: RegisterData, options?: {
111
+ rememberMe?: boolean;
112
+ }) => Promise<void>;
113
+ /** 토큰 갱신 함수 */
114
+ refreshToken: () => Promise<void>;
115
+ /** 사용자 정보 새로고침 */
116
+ refetchUser: () => Promise<void>;
117
+ /** 현재 토큰 */
118
+ token: string | null;
119
+ /** 토큰 수동 설정 */
120
+ setToken: (token: string | null) => void;
121
+ /** 사용자 정보 수동 설정 */
122
+ setUser: (user: TUser | null) => void;
123
+ }
124
+ /**
125
+ * 인증 관리 Hook
126
+ *
127
+ * 로그인, 로그아웃, 회원가입, 토큰 관리 등 인증 관련 기능을 제공합니다.
128
+ *
129
+ * @example
130
+ * ```tsx
131
+ * // 기본 사용 (로그인 유지 옵션 포함)
132
+ * function LoginPage() {
133
+ * const [rememberMe, setRememberMe] = useState(false);
134
+ * const { login, isLoggingIn, error, isAuthenticated } = useAuth({
135
+ * loginFn: (credentials) => api.login(credentials),
136
+ * onLoginSuccess: (user) => {
137
+ * console.log('로그인 성공:', user);
138
+ * navigate('/dashboard');
139
+ * },
140
+ * });
141
+ *
142
+ * const handleSubmit = async (e) => {
143
+ * e.preventDefault();
144
+ * // rememberMe가 true면 localStorage, false면 sessionStorage 사용
145
+ * await login({ email, password }, { rememberMe });
146
+ * };
147
+ *
148
+ * if (isAuthenticated) {
149
+ * return <Navigate to="/dashboard" />;
150
+ * }
151
+ *
152
+ * return (
153
+ * <form onSubmit={handleSubmit}>
154
+ * <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
155
+ * <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
156
+ * <label>
157
+ * <input type="checkbox" checked={rememberMe} onChange={(e) => setRememberMe(e.target.checked)} />
158
+ * 로그인 유지
159
+ * </label>
160
+ * <button type="submit" disabled={isLoggingIn}>
161
+ * {isLoggingIn ? '로그인 중...' : '로그인'}
162
+ * </button>
163
+ * {error && <div>{error.message}</div>}
164
+ * </form>
165
+ * );
166
+ * }
167
+ * ```
168
+ *
169
+ * @example
170
+ * ```tsx
171
+ * // 자동 토큰 갱신과 함께 사용
172
+ * function App() {
173
+ * const { user, isAuthenticated, isLoading, logout } = useAuth({
174
+ * getUserFn: () => api.getMe(),
175
+ * logoutFn: () => api.logout(),
176
+ * refreshTokenFn: () => api.refreshToken(),
177
+ * autoRefresh: true,
178
+ * refreshInterval: 300000, // 5분마다 갱신
179
+ * onLogoutSuccess: () => navigate('/login'),
180
+ * });
181
+ *
182
+ * if (isLoading) return <div>로딩중...</div>;
183
+ *
184
+ * return (
185
+ * <div>
186
+ * {isAuthenticated ? (
187
+ * <>
188
+ * <div>환영합니다, {user?.name}님</div>
189
+ * <button onClick={logout}>로그아웃</button>
190
+ * </>
191
+ * ) : (
192
+ * <LoginPage />
193
+ * )}
194
+ * </div>
195
+ * );
196
+ * }
197
+ * ```
198
+ *
199
+ * @example
200
+ * ```tsx
201
+ * // 회원가입 포함
202
+ * function RegisterPage() {
203
+ * const { register, isRegistering, error } = useAuth({
204
+ * registerFn: (data) => api.register(data),
205
+ * onRegisterSuccess: (user) => {
206
+ * toast.success('회원가입 성공!');
207
+ * navigate('/');
208
+ * },
209
+ * });
210
+ *
211
+ * const handleSubmit = async (data) => {
212
+ * await register(data);
213
+ * };
214
+ *
215
+ * return (
216
+ * <form onSubmit={handleSubmit}>
217
+ * <input name="email" />
218
+ * <input name="password" type="password" />
219
+ * <input name="name" />
220
+ * <button type="submit" disabled={isRegistering}>
221
+ * 회원가입
222
+ * </button>
223
+ * </form>
224
+ * );
225
+ * }
226
+ * ```
227
+ *
228
+ * @example
229
+ * ```tsx
230
+ * // Storage에 저장되는 값 확인하기
231
+ * function DebugAuth() {
232
+ * const { login, user, token } = useAuth({
233
+ * loginFn: api.login,
234
+ * storageKey: 'my_app_token', // 커스텀 키 사용 가능
235
+ * userStorageKey: 'my_app_user',
236
+ * });
237
+ *
238
+ * const handleLogin = async () => {
239
+ * await login({ email, password }, { rememberMe: true });
240
+ *
241
+ * // 로그인 후 storage 확인
242
+ * console.log('Token:', localStorage.getItem('my_app_token'));
243
+ * // 출력: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
244
+ *
245
+ * console.log('User:', localStorage.getItem('my_app_user'));
246
+ * // 출력: {"id":"123","name":"John Doe","email":"john@example.com"}
247
+ * };
248
+ * }
249
+ * ```
250
+ *
251
+ * @example
252
+ * ```tsx
253
+ * // rememberMe에 따른 storage 동작
254
+ * function LoginWithRememberMe() {
255
+ * const [rememberMe, setRememberMe] = useState(false);
256
+ * const { login } = useAuth({ loginFn: api.login });
257
+ *
258
+ * const handleLogin = async () => {
259
+ * await login({ email, password }, { rememberMe });
260
+ *
261
+ * if (rememberMe) {
262
+ * // localStorage에 저장 (브라우저 종료해도 유지)
263
+ * console.log(localStorage.getItem('auth_token')); // "eyJ..."
264
+ * console.log(sessionStorage.getItem('auth_token')); // null
265
+ * } else {
266
+ * // sessionStorage에 저장 (브라우저 종료 시 삭제)
267
+ * console.log(localStorage.getItem('auth_token')); // null
268
+ * console.log(sessionStorage.getItem('auth_token')); // "eyJ..."
269
+ * }
270
+ * };
271
+ * }
272
+ * ```
273
+ */
274
+ export declare function useAuth<TUser extends AuthUser = AuthUser>(options?: UseAuthOptions<TUser>): UseAuthReturn<TUser>;
275
+ //# sourceMappingURL=useAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../../../src/hooks/auth/useAuth.ts"],"names":[],"mappings":"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"}