@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
640 lines (564 loc) • 16.7 kB
text/typescript
"use client";
import { useState, useEffect, useCallback } from "react";
import {
UserProfile,
CreateUserRequest,
UpdateUserRequest,
UserListFilters,
UserListSort,
PaginationParams,
UserListResponse,
UserStats,
BulkUserOperation,
BulkOperationResult,
PasswordResetRequest,
PasswordResetConfirm,
EmailVerificationRequest,
EmailVerificationConfirm,
ProfileUpdateRequest,
} from "./types";
// 사용자 목록 관리 훅
export function useUsers(initialFilters: UserListFilters = {}) {
const [users, setUsers] = useState<UserProfile[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [filters, setFilters] = useState<UserListFilters>(initialFilters);
const [sort, setSort] = useState<UserListSort>({
field: "createdAt",
direction: "desc",
});
const [pagination, setPagination] = useState<PaginationParams>({
page: 1,
limit: 20,
});
const [totalPages, setTotalPages] = useState(0);
const [total, setTotal] = useState(0);
const fetchUsers = useCallback(async () => {
try {
setLoading(true);
setError(null);
const queryParams = new URLSearchParams({
page: pagination.page.toString(),
limit: pagination.limit.toString(),
sortField: sort.field,
sortDirection: sort.direction,
...Object.fromEntries(
Object.entries(filters).filter(
([, value]) => value !== undefined && value !== ""
)
),
});
const response = await fetch(`/api/users?${queryParams}`);
if (!response.ok) {
throw new Error("사용자 목록을 불러오는데 실패했습니다");
}
const data: UserListResponse = await response.json();
setUsers(data.users);
setTotal(data.pagination.total);
setTotalPages(data.pagination.totalPages);
} catch (err) {
setError(
err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다"
);
} finally {
setLoading(false);
}
}, [filters, sort, pagination]);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
const updateFilters = useCallback((newFilters: Partial<UserListFilters>) => {
setFilters((prev) => ({ ...prev, ...newFilters }));
setPagination((prev) => ({ ...prev, page: 1 })); // 필터 변경 시 첫 페이지로
}, []);
const updateSort = useCallback((field: UserListSort["field"]) => {
setSort((prev) => ({
field,
direction:
prev.field === field && prev.direction === "asc" ? "desc" : "asc",
}));
setPagination((prev) => ({ ...prev, page: 1 })); // 정렬 변경 시 첫 페이지로
}, []);
const updatePagination = useCallback(
(newPagination: Partial<PaginationParams>) => {
setPagination((prev) => ({ ...prev, ...newPagination }));
},
[]
);
const refresh = useCallback(() => {
fetchUsers();
}, [fetchUsers]);
return {
users,
loading,
error,
filters,
sort,
pagination: { ...pagination, total, totalPages },
updateFilters,
updateSort,
updatePagination,
refresh,
};
}
// 개별 사용자 관리 훅
export function useUser(userId?: string) {
const [user, setUser] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchUser = useCallback(
async (
id: string,
options: { includeTenant?: boolean; includeStats?: boolean } = {}
) => {
try {
setLoading(true);
setError(null);
const queryParams = new URLSearchParams();
if (options.includeTenant) queryParams.set("includeTenant", "true");
if (options.includeStats) queryParams.set("includeStats", "true");
const response = await fetch(`/api/users/${id}?${queryParams}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error("사용자를 찾을 수 없습니다");
}
throw new Error("사용자 정보를 불러오는데 실패했습니다");
}
const userData = await response.json();
setUser(userData);
return userData;
} catch (err) {
setError(
err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다"
);
throw err;
} finally {
setLoading(false);
}
},
[]
);
useEffect(() => {
if (userId) {
fetchUser(userId);
}
}, [userId, fetchUser]);
return {
user,
loading,
error,
fetchUser,
setUser,
};
}
// 사용자 생성 훅
export function useCreateUser() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const createUser = useCallback(
async (userData: CreateUserRequest): Promise<UserProfile> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userData),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "사용자 생성에 실패했습니다");
}
const newUser = await response.json();
return newUser;
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "사용자 생성 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
createUser,
loading,
error,
};
}
// 사용자 업데이트 훅
export function useUpdateUser() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const updateUser = useCallback(
async (
userId: string,
userData: UpdateUserRequest
): Promise<UserProfile> => {
try {
setLoading(true);
setError(null);
const response = await fetch(`/api/users/${userId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userData),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "사용자 업데이트에 실패했습니다");
}
const updatedUser = await response.json();
return updatedUser;
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "사용자 업데이트 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
updateUser,
loading,
error,
};
}
// 사용자 삭제 훅
export function useDeleteUser() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const deleteUser = useCallback(async (userId: string): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch(`/api/users/${userId}`, {
method: "DELETE",
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "사용자 삭제에 실패했습니다");
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "사용자 삭제 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
}, []);
return {
deleteUser,
loading,
error,
};
}
// 사용자 통계 훅
export function useUserStats() {
const [stats, setStats] = useState<UserStats | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchStats = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/users/stats");
if (!response.ok) {
throw new Error("사용자 통계를 불러오는데 실패했습니다");
}
const statsData = await response.json();
setStats(statsData);
} catch (err) {
setError(
err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다"
);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchStats();
}, [fetchStats]);
return {
stats,
loading,
error,
refresh: fetchStats,
};
}
// 대량 사용자 작업 훅
export function useBulkUserOperation() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const executeBulkOperation = useCallback(
async (operation: BulkUserOperation): Promise<BulkOperationResult> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/users/bulk", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(operation),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "대량 작업에 실패했습니다");
}
const result = await response.json();
return result;
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "대량 작업 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
executeBulkOperation,
loading,
error,
};
}
// 비밀번호 재설정 훅
export function usePasswordReset() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const requestPasswordReset = useCallback(
async (data: PasswordResetRequest): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/auth/password-reset/request", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(
errorData.error || "비밀번호 재설정 요청에 실패했습니다"
);
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "비밀번호 재설정 요청 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
const confirmPasswordReset = useCallback(
async (data: PasswordResetConfirm): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/auth/password-reset/confirm", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "비밀번호 재설정에 실패했습니다");
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "비밀번호 재설정 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
requestPasswordReset,
confirmPasswordReset,
loading,
error,
};
}
// 이메일 인증 훅
export function useEmailVerification() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const requestEmailVerification = useCallback(
async (data: EmailVerificationRequest): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/auth/email-verification/request", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "이메일 인증 요청에 실패했습니다");
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "이메일 인증 요청 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
const confirmEmailVerification = useCallback(
async (data: EmailVerificationConfirm): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/auth/email-verification/confirm", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "이메일 인증에 실패했습니다");
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "이메일 인증 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
requestEmailVerification,
confirmEmailVerification,
loading,
error,
};
}
// 프로필 업데이트 훅
export function useProfileUpdate() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const updateProfile = useCallback(
async (data: ProfileUpdateRequest): Promise<UserProfile> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/auth/profile", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "프로필 업데이트에 실패했습니다");
}
const updatedProfile = await response.json();
return updatedProfile;
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "프로필 업데이트 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
updateProfile,
loading,
error,
};
}
// 계정 잠금 해제 훅
export function useAccountUnlock() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const unlockAccount = useCallback(
async (userId: string, reason?: string): Promise<void> => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/users/unlock", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ userId, reason }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "계정 잠금 해제에 실패했습니다");
}
} catch (err) {
const errorMessage =
err instanceof Error
? err.message
: "계정 잠금 해제 중 오류가 발생했습니다";
setError(errorMessage);
throw new Error(errorMessage);
} finally {
setLoading(false);
}
},
[]
);
return {
unlockAccount,
loading,
error,
};
}