refactor: 코드베이스 정리 - Appwrite/Lovable 완전 제거

주요 변경사항:
• Appwrite SDK 및 관련 의존성 완전 제거
• Lovable 관련 도구 및 설정 제거
• 기존 Appwrite 기반 컴포넌트 및 훅 삭제
• Login/Register 페이지를 Clerk 기반으로 완전 전환

제거된 구성요소:
• src/lib/appwrite/ - 전체 디렉토리
• src/contexts/auth/ - 기존 인증 컨텍스트
• 구형 auth 컴포넌트들 (RegisterForm, LoginForm 등)
• useAuthQueries, useTransactionQueries 훅
• Appwrite 기반 테스트 파일들

설정 변경:
• package.json - appwrite, lovable-tagger 의존성 제거
• .env 파일 - Appwrite 환경변수 제거
• vercel.json - Supabase/Clerk 환경변수로 교체
• vite.config.ts - 청크 분할 설정 업데이트

성능 개선:
• 번들 크기 최적화 (Appwrite → Clerk + Supabase)
• 불필요한 코드 및 타입 정의 제거
• 테스트 설정을 Clerk/Supabase 모킹으로 업데이트

Task 11.4 완료: 기존 Appwrite 코드 완전 제거

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
hansoo
2025-07-13 14:13:28 +09:00
parent c231d5be65
commit 0a8b028a4c
72 changed files with 115 additions and 6808 deletions

View File

@@ -1,276 +0,0 @@
# Appwrite 전환 가이드
## 개요
Zellyy Finance는 백엔드 서비스를 Supabase에서 Appwrite로 전환합니다. 이 문서는 전환 과정과 새로운 코드 구조에 대한 가이드를 제공합니다.
## 전환 이유
1. **더 나은 성능**: Appwrite는 경량화된 서비스로 더 빠른 응답 시간 제공
2. **확장성**: 사용자 증가에 따른 확장성 개선
3. **기능 세트**: Appwrite의 실시간 데이터베이스와 인증 시스템 활용
4. **유지보수 용이성**: 단일 백엔드 서비스로 통합하여 유지보수 간소화
## 코드 구조
```
src/
├── lib/
│ ├── appwrite/ (Appwrite 서비스)
│ │ ├── index.ts (단일 진입점)
│ │ ├── client.ts (클라이언트 설정)
│ │ ├── config.ts (환경 설정)
│ │ └── setup.ts (데이터베이스 설정)
│ └── capacitor/ (네이티브 기능)
│ ├── index.ts (단일 진입점)
│ ├── buildInfo.ts (빌드 정보 관련)
│ ├── notification.ts (알림 관련)
│ └── permissions.ts (권한 관련)
├── hooks/
│ ├── auth/
│ │ └── useAppwriteAuth.ts (인증 관련 훅)
│ └── transactions/
│ └── useAppwriteTransactions.ts (트랜잭션 관련 훅)
├── components/
│ ├── auth/
│ │ └── AppwriteConnectionStatus.tsx (연결 상태 표시)
│ ├── migration/
│ │ └── SupabaseToAppwriteMigration.tsx (마이그레이션 도구)
│ └── native/
│ ├── PermissionRequest.tsx (권한 요청 UI)
│ └── NotificationSettings.tsx (알림 설정 UI)
└── utils/
└── appwriteTransactionUtils.ts (트랜잭션 유틸리티)
```
## 환경 설정
`.env` 파일에 다음 환경 변수를 설정합니다:
```
# Appwrite 설정
VITE_APPWRITE_ENDPOINT=https://a11.ism.kr/v1
VITE_APPWRITE_PROJECT_ID=zellyy-finance
VITE_APPWRITE_DATABASE_ID=zellyy-finance
VITE_APPWRITE_TRANSACTIONS_COLLECTION_ID=transactions
# 네이티브 설정
VITE_ANDROID_MIN_API_LEVEL=21
VITE_ANDROID_TARGET_API_LEVEL=33
VITE_ANDROID_NOTIFICATION_CHANNEL_ID=zellyy_finance_notifications
```
## 마이그레이션 단계
1. **데이터베이스 설정**
- Appwrite 데이터베이스 및 컬렉션 생성
- 필요한 인덱스 및 권한 설정
2. **인증 시스템 전환**
- Appwrite 인증 시스템 설정
- 사용자 계정 마이그레이션
3. **데이터 마이그레이션**
- 트랜잭션 데이터 마이그레이션
- 데이터 무결성 검증
4. **Supabase 코드 제거**
- 마이그레이션 완료 후 Supabase 관련 코드 제거
- 환경 변수 정리
## 주요 컴포넌트 및 훅
### 1. Appwrite 클라이언트 설정
```typescript
// src/lib/appwrite/client.ts
import { Client, Account, Databases, Storage } from 'appwrite';
import { config } from './config';
// 클라이언트 초기화
export const client = new Client()
.setEndpoint(config.endpoint)
.setProject(config.projectId);
// 서비스 초기화
export const account = new Account(client);
export const databases = new Databases(client);
export const storage = new Storage(client);
```
### 2. 인증 훅
```typescript
// src/hooks/auth/useAppwriteAuth.ts
import { useState, useEffect } from 'react';
import { account } from '../../lib/appwrite';
export const useAppwriteAuth = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// 사용자 세션 확인
useEffect(() => {
const checkSession = async () => {
try {
const session = await account.getSession('current');
if (session) {
const currentUser = await account.get();
setUser(currentUser);
}
} catch (error) {
console.error('세션 확인 오류:', error);
} finally {
setLoading(false);
}
};
checkSession();
}, []);
// 로그인 함수
const login = async (email, password) => {
try {
await account.createEmailSession(email, password);
const currentUser = await account.get();
setUser(currentUser);
return { success: true };
} catch (error) {
console.error('로그인 오류:', error);
return { success: false, error };
}
};
// 로그아웃 함수
const logout = async () => {
try {
await account.deleteSession('current');
setUser(null);
return { success: true };
} catch (error) {
console.error('로그아웃 오류:', error);
return { success: false, error };
}
};
return { user, loading, login, logout };
};
```
### 3. 트랜잭션 훅
```typescript
// src/hooks/transactions/useAppwriteTransactions.ts
import { useState, useEffect, useCallback } from 'react';
import { databases } from '../../lib/appwrite';
import { config } from '../../lib/appwrite/config';
import { Query } from 'appwrite';
export const useAppwriteTransactions = (userId) => {
const [transactions, setTransactions] = useState([]);
const [loading, setLoading] = useState(true);
// 트랜잭션 불러오기
const fetchTransactions = useCallback(async () => {
if (!userId) return;
try {
setLoading(true);
const response = await databases.listDocuments(
config.databaseId,
config.transactionsCollectionId,
[Query.equal('userId', userId)]
);
setTransactions(response.documents);
} catch (error) {
console.error('트랜잭션 불러오기 오류:', error);
} finally {
setLoading(false);
}
}, [userId]);
// 초기 데이터 로드
useEffect(() => {
fetchTransactions();
}, [fetchTransactions]);
// 트랜잭션 추가
const addTransaction = async (transaction) => {
try {
const newTransaction = await databases.createDocument(
config.databaseId,
config.transactionsCollectionId,
'unique()',
{
...transaction,
userId,
createdAt: new Date().toISOString(),
}
);
setTransactions((prev) => [...prev, newTransaction]);
return { success: true, transaction: newTransaction };
} catch (error) {
console.error('트랜잭션 추가 오류:', error);
return { success: false, error };
}
};
// 트랜잭션 업데이트
const updateTransaction = async (id, data) => {
try {
const updatedTransaction = await databases.updateDocument(
config.databaseId,
config.transactionsCollectionId,
id,
data
);
setTransactions((prev) =>
prev.map((t) => (t.$id === id ? updatedTransaction : t))
);
return { success: true, transaction: updatedTransaction };
} catch (error) {
console.error('트랜잭션 업데이트 오류:', error);
return { success: false, error };
}
};
// 트랜잭션 삭제
const deleteTransaction = async (id) => {
try {
await databases.deleteDocument(
config.databaseId,
config.transactionsCollectionId,
id
);
setTransactions((prev) => prev.filter((t) => t.$id !== id));
return { success: true };
} catch (error) {
console.error('트랜잭션 삭제 오류:', error);
return { success: false, error };
}
};
return {
transactions,
loading,
fetchTransactions,
addTransaction,
updateTransaction,
deleteTransaction,
};
};
```
## 마이그레이션 도구 사용법
1. 설정 페이지에서 "Appwrite 설정" 메뉴 선택
2. "Supabase에서 Appwrite로 마이그레이션" 섹션에서 "마이그레이션 시작" 버튼 클릭
3. 마이그레이션 진행 상황 확인
4. 완료 후 데이터 검증
## 주의사항
1. 마이그레이션 중에는 데이터 변경을 최소화하세요.
2. 마이그레이션 전에 데이터 백업을 수행하세요.
3. 마이그레이션 후 모든 기능이 정상 작동하는지 테스트하세요.
4. 문제 발생 시 개발팀에 즉시 보고하세요.

View File

@@ -1,396 +0,0 @@
# Lovable UI 컴포넌트 정리
## 개요
Zellyy Finance 앱은 Flutter에서 React와 Tailwind CSS를 사용한 Capacitor 기반 웹 앱으로 전환되었으며, UI 디자인은 뉴모피즘 스타일의 Lovable UI 컴포넌트를 적용하여 차별화된 사용자 경험을 제공하고자 합니다. 이 문서는 Lovable UI 컴포넌트와 관련된 내용을 정리합니다.
## Lovable UI 컴포넌트 관련 문서
### 1. UI/UX 설계 문서에서의 Lovable UI
**위치**: `/01_기획_및_설계/02_UI_UX_설계.md`
**주요 내용**:
- **컴포넌트 라이브러리**: Lovable UI 컴포넌트 라이브러리 구축
- **뉴모피즘 스타일**: 입체적이고 부드러운 UI 디자인 적용
- **디자인 철학**: 웹 기반 기술과 Capacitor를 활용하여 다양한 플랫폼에서 일관된 사용자 경험을 제공하면서도, 뉴모피즘 스타일의 Lovable UI 컴포넌트를 통해 차별화된 디자인을 구현
### 2. 시스템 아키텍처 문서에서의 Lovable UI
**위치**: `/02_기술_문서/01_시스템_아키텍처.md`
**주요 내용**:
- **프레젠테이션 계층**: 뉴모피즘 스타일 UI 컴포넌트 적용
### 3. 개발 로드맵 문서에서의 Lovable UI
**위치**: `/03_개발_단계/01_개발_로드맵.md`
**주요 내용**:
- **개발 작업**: 뉴모피즘 스타일 UI 컴포넌트 개발 계획
### 4. README 문서에서의 Lovable UI
**위치**: `/README.md`
**주요 내용**:
- **변경 사항**: 2025-03-09: 개발 방법 변경 - Flutter에서 React, Tailwind CSS, Capacitor 기반 웹 앱으로 전환, Lovable UI 컴포넌트 스타일 적용
## 필요한 Lovable UI 컴포넌트 목록
Zellyy Finance 앱의 홈 화면을 Lovable UI 컴포넌트로 변경하는 작업을 진행 중입니다. 기존 홈 화면의 기능을 유지하면서 Lovable UI 디자인 시스템의 컴포넌트로 UI를 개선하려고 합니다.
### 주요 변경 사항
1. FloatingActionButton을 LovableAddTransactionButton으로 교체
2. 기존 Card를 LovableCard로 교체
3. 거래 내역 리스트 아이템을 LovableTransactionCard로 교체
4. 전체적인 UI 디자인을 뉴모피즘 스타일로 변경
### 필요한 컴포넌트
- **LovableButton**: 기본 버튼 컴포넌트
- **LovableCard**: 카드 컴포넌트
- **LovableTransactionCard**: 거래 내역 표시용 카드 컴포넌트
- **LovableAddTransactionButton**: 거래 추가용 플로팅 액션 버튼
### 홈 화면 파일 경로
`/Users/hansoo./Dev/Zellyy_Finance/neumofinance/src/pages/Home.tsx`
## 뉴모피즘 디자인 특징
뉴모피즘(Neumorphism)은 다음과 같은 특징을 가진 UI 디자인 스타일입니다:
1. **입체감**: 요소가 배경에서 살짝 튀어나온 것처럼 보이는 효과
2. **부드러운 그림자**: 요소의 위쪽과 왼쪽에는 밝은 그림자, 아래쪽과 오른쪽에는 어두운 그림자를 적용
3. **단일 색상 사용**: 배경과 요소가 비슷한 색상을 사용하여 미묘한 차이로 구분
4. **미니멀리즘**: 깔끔하고 단순한 디자인 요소 사용
5. **낮은 대비**: 강한 색상 대비보다는 미묘한 그림자와 하이라이트로 구분
## 구현 방향
1. **Tailwind CSS 활용**: 웹 기반 앱에서는 Tailwind CSS를 활용하여 뉴모피즘 효과 구현
2. **React 컴포넌트 모듈화**: 재사용 가능한 React 컴포넌트로 설계
3. **반응형 디자인**: 다양한 화면 크기에 대응하는 디자인 적용
4. **접근성 고려**: 시각적 효과가 접근성을 해치지 않도록 주의
5. **성능 최적화**: 그림자 효과 등이 성능에 영향을 미치지 않도록 최적화
## React 기반 Lovable UI 컴포넌트 구현
### 1. LovableButton 컴포넌트
```tsx
import React from 'react';
import { twMerge } from 'tailwind-merge';
interface LovableButtonProps {
children: React.ReactNode;
onClick?: () => void;
className?: string;
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
}
const LovableButton: React.FC<LovableButtonProps> = ({
children,
onClick,
className = '',
variant = 'primary',
size = 'md',
disabled = false,
}) => {
const baseStyles = 'rounded-xl font-medium transition-all duration-200 focus:outline-none';
const variantStyles = {
primary: 'bg-primary-100 text-primary-900 shadow-neumo hover:shadow-neumo-pressed active:shadow-neumo-pressed',
secondary: 'bg-secondary-100 text-secondary-900 shadow-neumo hover:shadow-neumo-pressed active:shadow-neumo-pressed',
outline: 'bg-transparent border-2 border-primary-200 text-primary-900 hover:bg-primary-50 active:bg-primary-100',
};
const sizeStyles = {
sm: 'px-3 py-1 text-sm',
md: 'px-4 py-2',
lg: 'px-6 py-3 text-lg',
};
const disabledStyles = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer';
return (
<button
onClick={onClick}
disabled={disabled}
className={twMerge(
baseStyles,
variantStyles[variant],
sizeStyles[size],
disabledStyles,
className
)}
>
{children}
</button>
);
};
export default LovableButton;
```
### 2. LovableCard 컴포넌트
```tsx
import React from 'react';
import { twMerge } from 'tailwind-merge';
interface LovableCardProps {
children: React.ReactNode;
className?: string;
onClick?: () => void;
elevated?: boolean;
pressed?: boolean;
}
const LovableCard: React.FC<LovableCardProps> = ({
children,
className = '',
onClick,
elevated = false,
pressed = false,
}) => {
const baseStyles = 'bg-primary-50 rounded-2xl p-4 transition-all duration-200';
const shadowStyles = {
default: 'shadow-neumo',
elevated: 'shadow-neumo-elevated',
pressed: 'shadow-neumo-pressed',
};
const selectedShadow = pressed ? shadowStyles.pressed : (elevated ? shadowStyles.elevated : shadowStyles.default);
return (
<div
className={twMerge(
baseStyles,
selectedShadow,
onClick ? 'cursor-pointer' : '',
className
)}
onClick={onClick}
>
{children}
</div>
);
};
export default LovableCard;
```
### 3. LovableTransactionCard 컴포넌트
```tsx
import React from 'react';
import { twMerge } from 'tailwind-merge';
import LovableCard from './LovableCard';
import { formatCurrency } from '../utils/formatters';
interface TransactionType {
id: string;
title: string;
amount: number;
date: string;
category: string;
type: 'income' | 'expense';
}
interface LovableTransactionCardProps {
transaction: TransactionType;
className?: string;
onClick?: () => void;
}
const LovableTransactionCard: React.FC<LovableTransactionCardProps> = ({
transaction,
className = '',
onClick,
}) => {
const { title, amount, date, category, type } = transaction;
// 카테고리에 따른 아이콘 선택
const getCategoryIcon = (category: string) => {
switch (category.toLowerCase()) {
case '식비':
return '🍴';
case '교통':
return '🚗';
case '쇼핑':
return '🛍️';
case '여가':
return '⛲️';
case '금융':
return '💰';
case '급여':
return '💸';
default:
return '📃';
}
};
const formattedDate = new Date(date).toLocaleDateString('ko-KR', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
return (
<LovableCard
className={twMerge('flex items-center justify-between', className)}
onClick={onClick}
>
<div className="flex items-center">
<div className="w-10 h-10 rounded-full bg-primary-100 flex items-center justify-center mr-3 shadow-neumo-sm">
<span className="text-xl">{getCategoryIcon(category)}</span>
</div>
<div>
<h3 className="font-medium text-primary-900">{title}</h3>
<p className="text-sm text-primary-600">{formattedDate}</p>
</div>
</div>
<div className={`font-bold ${type === 'income' ? 'text-green-600' : 'text-red-600'}`}>
{type === 'income' ? '+' : '-'}{formatCurrency(amount)}
</div>
</LovableCard>
);
};
export default LovableTransactionCard;
```
### 4. LovableAddTransactionButton 컴포넌트
```tsx
import React, { useState } from 'react';
import { twMerge } from 'tailwind-merge';
interface LovableAddTransactionButtonProps {
onAddIncome?: () => void;
onAddExpense?: () => void;
className?: string;
}
const LovableAddTransactionButton: React.FC<LovableAddTransactionButtonProps> = ({
onAddIncome,
onAddExpense,
className = '',
}) => {
const [isOpen, setIsOpen] = useState(false);
const toggleOpen = () => {
setIsOpen(!isOpen);
};
const handleAddIncome = () => {
if (onAddIncome) {
onAddIncome();
}
setIsOpen(false);
};
const handleAddExpense = () => {
if (onAddExpense) {
onAddExpense();
}
setIsOpen(false);
};
return (
<div className={twMerge('fixed bottom-6 right-6 z-50', className)}>
{isOpen && (
<div className="flex flex-col items-end mb-4 space-y-3">
<button
onClick={handleAddIncome}
className="flex items-center bg-primary-50 text-green-600 px-4 py-2 rounded-xl shadow-neumo hover:shadow-neumo-pressed transition-all duration-200"
>
<span className="mr-2">💸</span>
<span> </span>
</button>
<button
onClick={handleAddExpense}
className="flex items-center bg-primary-50 text-red-600 px-4 py-2 rounded-xl shadow-neumo hover:shadow-neumo-pressed transition-all duration-200"
>
<span className="mr-2">💳</span>
<span> </span>
</button>
</div>
)}
<button
onClick={toggleOpen}
className={`w-14 h-14 rounded-full bg-primary-100 text-primary-900 flex items-center justify-center shadow-neumo-elevated hover:shadow-neumo transition-all duration-300 ${isOpen ? 'rotate-45' : ''}`}
>
<span className="text-2xl font-bold">+</span>
</button>
</div>
);
};
export default LovableAddTransactionButton;
```
### 5. Tailwind CSS 구성
```ts
// tailwind.config.ts
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
primary: {
50: '#f5f7fa',
100: '#e4e7eb',
200: '#cbd2d9',
300: '#9aa5b1',
400: '#7b8794',
500: '#616e7c',
600: '#52606d',
700: '#3e4c59',
800: '#323f4b',
900: '#1f2933',
},
secondary: {
50: '#e3f8ff',
100: '#b3ecff',
200: '#81defd',
300: '#5ed0fa',
400: '#40c3f7',
500: '#2bb0ed',
600: '#1992d4',
700: '#127fbf',
800: '#0b69a3',
900: '#035388',
},
},
boxShadow: {
'neumo': '5px 5px 10px #d1d9e6, -5px -5px 10px #ffffff',
'neumo-sm': '3px 3px 6px #d1d9e6, -3px -3px 6px #ffffff',
'neumo-pressed': 'inset 5px 5px 10px #d1d9e6, inset -5px -5px 10px #ffffff',
'neumo-elevated': '10px 10px 20px #d1d9e6, -10px -10px 20px #ffffff',
},
},
},
plugins: [],
};
export default config;
```
## 결론
Lovable UI 컴포넌트는 Zellyy Finance 앱의 차별화된 사용자 경험을 제공하기 위한 핵심 요소입니다. Flutter에서 React와 Tailwind CSS를 사용한 웹 기반 앱으로 전환하면서, 뉴모피즘 스타일을 적용하여 입체적이고 부드러운 디자인을 구현하였습니다.
React의 컴포넌트 기반 아키텍처와 Tailwind CSS의 유틸리티 클래스를 활용하여 재사용 가능한 컴포넌트를 구축함으로써, 개발 효율성을 높이고 일관된 사용자 경험을 제공할 수 있습니다. 특히 커스텀 그림자 효과를 활용한 뉴모피즘 스타일은 사용자에게 매력적인 인터페이스를 제공합니다.
이러한 Lovable UI 컴포넌트 라이브러리는 앞으로도 지속적으로 개선되고 확장될 예정이며, 사용자 피드백을 반영하여 더욱 향상된 경험을 제공할 계획입니다. 이를 통해 Zellyy Finance 앱은 사용자에게 차별화된 가치를 제공하고, 재무 관리에 도움이 되는 유용한 도구로 자리매김할 것입니다.