- Add GitHub Actions workflow for automated CI/CD - Configure Node.js 18.x and 20.x matrix testing - Add TypeScript type checking step - Add ESLint code quality checks with enhanced rules - Add Prettier formatting verification - Add production build validation - Upload build artifacts for deployment - Set up automated testing on push/PR - Replace console.log with environment-aware logger - Add pre-commit hooks for code quality - Exclude archive folder from linting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
187 lines
4.1 KiB
TypeScript
187 lines
4.1 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import { account } from "@/lib/appwrite";
|
|
import { ID } from "appwrite";
|
|
|
|
// 인증 상태 인터페이스
|
|
interface AuthState {
|
|
user: any | null;
|
|
loading: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
// 로그인 입력값 인터페이스
|
|
interface LoginCredentials {
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
// 회원가입 입력값 인터페이스
|
|
interface SignupCredentials extends LoginCredentials {
|
|
name?: string;
|
|
}
|
|
|
|
/**
|
|
* Appwrite 인증 관련 훅
|
|
* 로그인, 로그아웃, 회원가입 및 사용자 상태 관리
|
|
*/
|
|
export const useAppwriteAuth = () => {
|
|
// 인증 상태 관리
|
|
const [authState, setAuthState] = useState<AuthState>({
|
|
user: null,
|
|
loading: true,
|
|
error: null,
|
|
});
|
|
|
|
// 컴포넌트 마운트 상태 추적
|
|
const [isMounted, setIsMounted] = useState(true);
|
|
|
|
// 사용자 정보 가져오기
|
|
const getCurrentUser = useCallback(async () => {
|
|
try {
|
|
const user = await account.get();
|
|
if (isMounted) {
|
|
setAuthState({
|
|
user,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
}
|
|
return user;
|
|
} catch (error) {
|
|
if (isMounted) {
|
|
setAuthState({
|
|
user: null,
|
|
loading: false,
|
|
error: error as Error,
|
|
});
|
|
}
|
|
return null;
|
|
}
|
|
}, [isMounted]);
|
|
|
|
// 이메일/비밀번호로 로그인
|
|
const login = useCallback(
|
|
async ({ email, password }: LoginCredentials) => {
|
|
try {
|
|
setAuthState((prev) => ({ ...prev, loading: true, error: null }));
|
|
|
|
// 비동기 작업 시작 전 UI 스레드 차단 방지
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
|
|
const session = await account.createEmailPasswordSession(
|
|
email,
|
|
password
|
|
);
|
|
const user = await account.get();
|
|
|
|
if (isMounted) {
|
|
setAuthState({
|
|
user,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
}
|
|
|
|
return { user, session };
|
|
} catch (error) {
|
|
if (isMounted) {
|
|
setAuthState((prev) => ({
|
|
...prev,
|
|
loading: false,
|
|
error: error as Error,
|
|
}));
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
[isMounted]
|
|
);
|
|
|
|
// 회원가입
|
|
const signup = useCallback(
|
|
async ({ email, password, name }: SignupCredentials) => {
|
|
try {
|
|
setAuthState((prev) => ({ ...prev, loading: true, error: null }));
|
|
|
|
// 비동기 작업 시작 전 UI 스레드 차단 방지
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
|
|
const user = await account.create(ID.unique(), email, password, name);
|
|
|
|
// 회원가입 후 자동 로그인
|
|
await account.createEmailPasswordSession(email, password);
|
|
|
|
if (isMounted) {
|
|
setAuthState({
|
|
user,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
}
|
|
|
|
return user;
|
|
} catch (error) {
|
|
if (isMounted) {
|
|
setAuthState((prev) => ({
|
|
...prev,
|
|
loading: false,
|
|
error: error as Error,
|
|
}));
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
[isMounted]
|
|
);
|
|
|
|
// 로그아웃
|
|
const logout = useCallback(async () => {
|
|
try {
|
|
setAuthState((prev) => ({ ...prev, loading: true }));
|
|
|
|
// 현재 세션 삭제
|
|
await account.deleteSession("current");
|
|
|
|
if (isMounted) {
|
|
setAuthState({
|
|
user: null,
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
if (isMounted) {
|
|
setAuthState((prev) => ({
|
|
...prev,
|
|
loading: false,
|
|
error: error as Error,
|
|
}));
|
|
}
|
|
throw error;
|
|
}
|
|
}, [isMounted]);
|
|
|
|
// 초기 사용자 정보 로드
|
|
useEffect(() => {
|
|
setIsMounted(true);
|
|
getCurrentUser();
|
|
|
|
// 정리 함수
|
|
return () => {
|
|
setIsMounted(false);
|
|
};
|
|
}, [getCurrentUser]);
|
|
|
|
return {
|
|
user: authState.user,
|
|
loading: authState.loading,
|
|
error: authState.error,
|
|
login,
|
|
signup,
|
|
logout,
|
|
getCurrentUser,
|
|
};
|
|
};
|
|
|
|
export default useAppwriteAuth;
|