Refactor title suggestion logic

Implement new logic for title suggestions based on usage frequency.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-22 13:12:52 +00:00
parent 3229913bde
commit 74f7282fac
3 changed files with 82 additions and 161 deletions

View File

@@ -5,8 +5,11 @@ import { CATEGORY_TITLE_SUGGESTIONS } from '@/constants/categoryIcons';
// 지출 제목 사용 빈도를 저장하는 로컬 스토리지 키
const TITLE_PREFERENCES_KEY = 'userTitlePreferences';
// 기본 분석 대상 트랜잭션 수
const DEFAULT_ANALYSIS_COUNT = 50;
// 최대 저장 제목 개수 (카테고리별)
const MAX_TITLES_PER_CATEGORY = 15;
// 최소 사용 횟수 (이 횟수 미만이면 삭제 대상)
const MIN_USAGE_COUNT = 2;
// 사용자 제목 선호도 타입 정의
export interface TitlePreference {
@@ -77,8 +80,9 @@ export const updateTitleUsage = (transaction: Transaction): void => {
preferences[category] = {};
}
// 해당 제목이 없으면 초기화
// 해당 제목이 없으면 새로 추가 (새 제목 삽입)
if (!preferences[category][title]) {
console.log(`새 제목 추가: "${title}" (${category} 카테고리)`);
preferences[category][title] = {
count: 0,
lastUsed: new Date().toISOString()
@@ -89,58 +93,34 @@ export const updateTitleUsage = (transaction: Transaction): void => {
preferences[category][title].count += 1;
preferences[category][title].lastUsed = new Date().toISOString();
// 저장
saveUserTitlePreferences(preferences);
};
/**
* 트랜잭션 목록에서 제목 사용 빈도 분석하여 업데이트
* 앱 초기화 시 또는 주기적으로 호출하여 최신 데이터 반영
*/
export const analyzeTransactionTitles = (
transactions: Transaction[],
analysisCount: number = DEFAULT_ANALYSIS_COUNT
): void => {
// 지출 항목만 필터링하고 최신 순으로 정렬
const recentTransactions = transactions
.filter(tx => tx.type === 'expense')
.sort((a, b) => {
const dateA = a.localTimestamp ? new Date(a.localTimestamp).getTime() : 0;
const dateB = b.localTimestamp ? new Date(b.localTimestamp).getTime() : 0;
return dateB - dateA;
})
.slice(0, analysisCount); // 최신 N개만 분석
// 선호도 데이터 초기화 (기존 데이터 유지를 원하면 이 부분 제거)
let preferences: UserTitlePreferences = {
: {},
: {},
: {},
: {}
};
// 트랜잭션 분석
recentTransactions.forEach(tx => {
if (!tx.category || !tx.title) return;
// 카테고리별 최대 제목 수 관리 (사용 빈도가 낮은 제목 제거)
const titles = Object.entries(preferences[category]);
if (titles.length > MAX_TITLES_PER_CATEGORY) {
// 사용 횟수 및 최근 사용일 기준으로 정렬
const sortedTitles = titles.sort((a, b) => {
// 먼저 사용 횟수로 비교 (내림차순)
const countDiff = b[1].count - a[1].count;
if (countDiff !== 0) return countDiff;
// 사용 횟수가 같으면 최근 사용일로 비교 (내림차순)
return new Date(b[1].lastUsed).getTime() - new Date(a[1].lastUsed).getTime();
});
if (!preferences[tx.category]) {
preferences[tx.category] = {};
// 정렬 후 하위 항목 제거 (기준: MIN_USAGE_COUNT 미만 & 가장 적게 사용됨)
const titlesToRemove = sortedTitles
.slice(MAX_TITLES_PER_CATEGORY)
.filter(([_, pref]) => pref.count < MIN_USAGE_COUNT)
.map(([title]) => title);
if (titlesToRemove.length > 0) {
console.log(`사용 빈도가 낮은 제목 제거: ${titlesToRemove.length}`);
// 제거할 제목들을 선호도에서 삭제
titlesToRemove.forEach(title => {
delete preferences[category][title];
});
}
if (!preferences[tx.category][tx.title]) {
preferences[tx.category][tx.title] = {
count: 0,
lastUsed: tx.localTimestamp || new Date().toISOString()
};
}
preferences[tx.category][tx.title].count += 1;
// 가장 최근 사용일자 업데이트
if (tx.localTimestamp) {
preferences[tx.category][tx.title].lastUsed = tx.localTimestamp;
}
});
}
// 저장
saveUserTitlePreferences(preferences);
@@ -184,3 +164,22 @@ export const getPersonalizedTitleSuggestions = (category: string): string[] => {
return defaultSuggestions;
}
};
/**
* 트랜잭션 추가 시 제목 사용 빈도 업데이트 및 관리 함수
* AddTransactionButton에서 호출하기 위한 래퍼 함수
*/
export const manageTitleSuggestions = (transaction: Transaction): void => {
// 제목 사용 업데이트 (추가 및 카운트 증가)
updateTitleUsage(transaction);
// 개발 모드에서 저장된 제목 선호도 로깅
if (process.env.NODE_ENV === 'development') {
const preferences = loadUserTitlePreferences();
const category = transaction.category;
if (preferences[category]) {
const count = Object.keys(preferences[category]).length;
console.log(`${category} 카테고리 저장된 제목 수: ${count}`);
}
}
};