Release v1.3.2#570
Conversation
* refactor(mobile): 알림 코드 아키텍처 가이드 준수 리팩토링 (#552) - notification-item: 핸들러 훅(useNotificationPress) 추출, return 전 9줄→2줄 - unread-notification-header: useCallback 핸들러 → 인라인화 - notification-provider: cold start router.replace → router.navigate (뒤로가기 복원) - use-notification-handler: replace 옵션 제거, NotificationResponseOptions 삭제 - 전체 알림 코드 한줄 if문 → 중괄호 3줄 통일 * fix(mobile): unused import/variable 린트 에러 수정 - _layout.tsx: 미사용 DocsIconSvg import 제거 - memo/create.tsx: 미사용 navigation 변수 및 useNavigation import 제거
* feat(mobile): 로봇 아이콘 추가 (outline + pixel 2종)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(mobile): AI 메모 파싱 리뷰 화면 구현
- AI 파싱 결과를 TodoCard 리스트로 표시, 편집 후 할 일로 변환
- TodoCard를 Context 기반으로 리팩토링 — props는 index만, 데이터는 자체 접근
- 메모 상세 화면 삭제 다이얼로그를 useOverlay로 통일
- [id].tsx → [id]/index.tsx + [id]/ai-review.tsx 분리
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(mobile): 메모 화면 공통 헤더 레이아웃으로 통합
- (tabs)/memo 하위 중복 화면 삭제, memo 그룹으로 일원화
- memo/_layout.tsx에 공통 헤더 (back, 타이틀, 폰트 스케일) 적용
- [id]/index, ai-review에서 커스텀 헤더 제거 후 Stack.Screen options 활용
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(mobile): 메모 생성 화면 키보드 두 번 올라오는 버그 수정
- transitionEnd 리스너로 화면 열릴 때만 포커스 실행
- useNavigation 제네릭으로 타입 안전하게 처리
* feat(mobile): AI 사용 횟수 초과 시 구독 유도 UI 및 쿼리 캐시 키 개선
- AI_1303 에러 시 구독 페이지로 이동하는 전용 Result 화면 추가
- parseMemo 쿼리 키에 content 포함하여 내용 변경 시 재요청되도록 수정
* fix(mobile): AI 리뷰 화면 picker onExit race condition 수정
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(mobile): AI 파싱 전 확인 다이얼로그 추가
- AiParseConfirmDialog 컴포넌트 분리 (남은 횟수 + 경고 표시)
- AiUsagePolicy.getRemainingCount() 도메인 규칙 추가
- AI 버튼 로딩 중 스피너 표시
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(mobile): 리베이스 충돌 해결 시 누락된 useNavigation 복원
* fix(mobile): AI 파싱 다이얼로그 문구 개선
* feat(mobile): AI 파싱 일일 한도 소진 시 구독 유도로 개선
* feat(mobile): AI 파싱 일일 한도 소진 시 구독 유도로 개선
- 다이얼로그 타이틀을 액션 중심으로 변경 (AI로 메모를 할 일로 만들게요)
- 횟수 소진 시 구독 유도 분기 추가 (구독하기 버튼 → 구독 화면 이동)
- 프리미엄 유저는 횟수 표시 없이 경고만 노출
- ai-review 에러: AI_1303 발생 시 구독 화면으로 이동
- 문구 토스 보이스톤으로 통일 (해요체, 닫기 버튼)
* fix(mobile): 메모 빈 상태 DocsIcon SVG 복원
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(mobile): AI 메모 파싱 — mutation 기반으로 전환 및 중복 차감 방지
- parseMemo를 query → mutation으로 전환, onSuccess에서 setQueryData로 캐싱
- AiParseConfirmDialog에서 직접 parseMutation 실행 및 navigate
- 캐시 있으면 mutation 없이 ai-review로 바로 이동 (중복 차감 방지)
- 메모 수정 저장 시 parseMemo 캐시 제거
- use-parse-memo-query-options.ts 삭제
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(api,mobile): AI 파싱 프롬프트 품질 개선 및 카테고리 자동 유추
- 프롬프트 system/user 메시지 분리 (parse-memo, parse-todo)
- 공유 시간 규칙 모듈 추출 (time-rules.ts)
- Few-shot 예시 3개 추가 및 분리 규칙 강화
- Sanitization 버그 수정: ~ 제거로 범위 표현 훼손 방지
- 메모/줄바꿈 구조 보존 (newline → semicolon)
- AI 카테고리 자동 유추: 유저 카테고리 목록을 프롬프트에 포함
- 클라이언트 전달 categoryId 우선, AI 유추값 fallback
- AI 리뷰 화면 X 버튼을 HeroUI CloseButton으로 교체 및 우상단 배치
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(api): AI 제안 프롬프트 품질 개선 — 구체적 행동 중심으로 전환
- system/user 메시지 분리 (기존 단일 prompt → system 규칙 + user 데이터)
- title 작성 규칙 강화: "오후 집중 업무 시간" 같은 추상적 이름 금지
- 바로 실행 가능한 구체적 행동 강제 ("아침 스트레칭 10분", "달리기 3km")
- reason 규칙 강화: 숫자 근거 필수, 동기부여형 톤
- 모든 유형에 구체적 title 예시 추가 (밸런스, 습관 회복 등)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Matthew Kim <dydals3440@gmail.com>
API 로깅 정리: - Controller 로깅 제거 (가이드: Controller는 로깅 불필요) - AI Service: userId 포함, 에러 로그 중복 제거, warn 레벨 추가 - AI Suggestion Service: 프리미엄 차단 시 warn 추가 - Memo Controller: 불필요한 debug 로깅 제거 모바일 analytics 추가: - 메모 AI 파싱 성공/실패 이벤트 (ai_parse_used) - 사용량 초과 프리미엄 게이트 이벤트 (premium_gate_shown) - AI 리뷰 할일 생성 확정 이벤트 (memo_converted_to_todos) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(api,mobile): AI 파싱 월 5회 전환 + AI 전반 품질 개선 (#555) * refactor(api): remove Anthropic + fix E2E + strengthen AI quality (#555) Gemini Code Review + CI 실패 + Live QA 결과 피드백 반영 ## Anthropic 전체 제거 - anthropic.provider.ts 삭제, @ai-sdk/anthropic 의존성 제거 - AiRouterProvider 단일 Gemini pass-through 로 단순화 (향후 확장성은 유지) - AI_PROVIDER_ANTHROPIC DI/심볼 제거 - modelHint 타입에서 "report" 제거 - report-generator 의 modelHint 호출 제거 - 환경변수 예시/주석에서 ANTHROPIC_API_KEY, Claude 언급 제거 - ai-router.provider.spec.ts 재작성 (pass-through 검증) ## E2E 2건 수정 (응답 구조 이중 중첩) - ai.e2e-spec.ts 349/350/365: usageResponse.body.data → .data.data ## parse-todo 인젝션 방어 강화 (Live QA A4 실패 대응) - PROMPT_SECURITY_GUARD 에 예시 추가 ("HACKED" 케이스 명시적 금지) - parse-todo 프롬프트에 "특수 입력 처리" 섹션 추가 (영문 명령/JSON 입력 시 "입력 확인 필요" 로 고정) ## Suggestion 품질 개선 - 중복 필터: 제목 앞 2어절 + daysOfWeek 동일하면 최고 confidence 만 남김 (Live QA 에서 "오전 자기계발 30분"/"오전 자기계발 1시간" 중복 관찰) - cap + 정렬: confidence 내림차순으로 MAX_SUGGESTIONS_PER_USER 상한 적용 (Gemini review medium) - categoryId 정합성: 제안 제목 단어가 사용자 카테고리명과 일치하면 우선 할당 (Live QA 에서 "운동 30분" 제안이 자기계발 카테고리로 할당되던 문제 개선) ## 검증 - API 단위 2190 pass, mobile 597 pass, typecheck + lint + build 통과 - ai.e2e-spec 단독 35/35 통과 (이전 실패 2건 해소) - 기존 앱 크래시 위험 0: suggestedCategoryId 스키마 nullable 유지, 모바일 3단계 null fallback Closes #555
$executeRaw + GREATEST 대신 Prisma updateMany 를 사용해 동일한 음수 방지
효과를 얻으면서 타입 안전성과 프로젝트 컨벤션을 유지한다.
- where: { aiUsageCount: { gt: 0 } } 조건으로 count=0 일 때 no-op
- Prisma 타입 체크 + 쿼리 로깅 일관성 회복
- 다른 모든 repository 와 동일한 Prisma 스타일 유지
검증:
- SQL 레벨 실측: count=0 → UPDATE 0 rows, count=3 → 2 로 감소
- ai.service.spec / user.repository.spec 59 passed
- lint / typecheck / build / test / ai.e2e-spec 35 passed
…recoverableError (#559) (#560) * refactor(api): enforce SRP + centralize log masking + adopt BullMQ UnrecoverableError (#559) * refactor(api): address Gemini review on PR #560 - mask.util: split('@') → lastIndexOf('@') for RFC 5321 compliance (quoted local-part can contain @, preserves domain accuracy) - category-resolver: O(N*M) → O(N) via Set lookup for matchedTitles - mask.util.spec: add quoted local-part + empty domain cases
- useRepeatSetting에 toggleWeekdays/toggleWeekends 액션 추가 - DayOfWeekSelector 칩 배치를 매일/주중/주말/요일 순으로 재배치 - DayOfWeekSelector props를 repeatSetting 객체 하나로 단순화
* feat(mobile): SwipePager 공통 컴포넌트 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mobile): 캘린더 스와이프 페이저 적용 - CalendarMonthView/WeekView 삭제, CalendarPager로 통합 - CalendarDateCell 월 외 날짜 opacity 처리 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test(mobile): getMonthWeeks 단위 테스트 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mobile): 캘린더 관련 date 유틸 추가 - addWeeks, addMonths: n주/n달 이동 - isSameWeek: 같은 주 판정 - withDayOfWeek, withDayOfMonth: 같은 주/달 내 요일·날짜 이동 (월말 clamp) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mobile): 캘린더 페이저/viewMode 구조 재설계 - CalendarPager: controlled 패턴 (data/value/onChange) + day 보존 동작 - WeekPager/MonthPager 분리, Row/Grid 역할 명확화 - viewMode 상태를 CalendarProvider에 단일 소유로 통합 (sync 버그 제거) - CalendarNavigation 순수 UI 전환, Toggle/DateCell context 직접 구독 - SwipePager 공용 컴포넌트 제거 (Calendar 내부로 흡수) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(mobile): 피드 날씨 뱃지가 selectedDate 대신 오늘 기준으로 조회 - 캘린더 스와이프 시 날씨 쿼리가 매번 재호출되어 깜빡이던 문제 해결 - Memo 탭과 동일하게 useToday() 기준으로 통일 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mobile): 캘린더 페이저 고정 윈도우 101페이지로 확장 - CalendarPager data를 Date 배열 → 페이지 번호 배열로 전환 (lazy Date 생성) - 9 → 101 페이지로 범위 확장 (±50주/월, PR 리뷰 대응) - weekAtPage/pageOfWeek 등 도메인 네이밍 적용 - shared/utils/date.ts 에 diffWeeks/diffMonths 추가 + 단위 테스트 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(mobile): 캘린더 페이저 범위를 ±5년으로 확장 주간 뷰 ±1년 boundary 가 실사용에서 도달 가능해 "조용한 멈춤" UX 문제가 있었음. 101 → 521페이지로 넓혀 실질 도달 불가화 (주 ±5년 / 월 ±21년). 메모리 3KB 증가, virtualization 덕에 렌더 비용 동일. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v1.3.0 squash merge 커밋을 develop에 흡수. 충돌은 develop 우선으로 해결.
- 모든 .md(33개)에 Version/Last Updated/Owner 메타 블록 추가 - 루트·apps CLAUDE.md를 2026-04 권장 포맷(얇은 인덱스)으로 재작성 - apps/mobile/CLAUDE.md 409 → 123줄로 축약 (세부 내용은 .claude/architecture.md에 위임) - 불일치 정정: pnpm 9.15→10.29, Node 22+→20+, Swagger 경로 /api-docs 통일, .env.docker.development→.env.docker.dev, PUSH_NOTIFICATION_GUIDE 깨진 링크 제거
* chore(mobile): SDK 55 패치 정렬 및 안전 라이브러리 업데이트 (#568) SDK 55 패치 정렬: - expo ~55.0.5 → ~55.0.24, expo-* 패치 일괄 - react-native 0.83.2 → 0.83.6 - react-native-worklets 0.7.2 → 0.7.4 - jest-expo ~55.0.13 → ~55.0.17 - @expo/dom-webview 55.0.6, @expo/metro-runtime 55.0.11 명시 - @shopify/react-native-skia를 SDK bundle ^2.4.18로 정렬 Monorepo entry 정리: - apps/mobile/index.js 신규 (expo-router/entry import) - package.json#main을 "index.js"로 전환 - metro.config.js에 워크스페이스 루트 resolveRequest 추가 루트 pnpm.packageExtensions: - uniwind@*에 metro/metro-cache 0.83.7 peer 보강 Group A 안전 라이브러리 minor/patch: - heroui-native 1.0.1 → 1.0.3 (beta exact pin) - tailwindcss ^4.2.1 → ^4.3.0 - tailwind-merge ^3.5.0 → ^3.6.0 - uniwind ^1.5.0 → ^1.6.5 - @tanstack/react-query ^5.90.21 → ^5.100.10 - react-hook-form ^7.71.2 → ^7.76.0 - @react-navigation/native ^7.1.28 → ^7.1.33 (SDK 권장) - @react-navigation/bottom-tabs 7.10.1 → ^7.15.5 (SDK 권장 + 캐럿) - es-toolkit ^1.45.1 → ^1.46.1 - react-native-svg-transformer ^1.5.2 → ^1.5.3 (dev) * chore(mobile): bump version to 1.3.2
# Conflicts: # apps/mobile/app.config.ts # apps/mobile/package.json # pnpm-lock.yaml
There was a problem hiding this comment.
Code Review
This pull request transitions AI usage tracking from a daily to a monthly limit across the API and mobile applications, while also standardizing AI prompts for security and consistency. It introduces PII masking for logs and refactors the mobile calendar to a pager-based system. Feedback focuses on improving category matching logic to support multi-word names, fixing a regression in the weather badge date selection, and refining the calendar pager implementation to avoid UI flickering by using virtualization instead of dynamic re-centering.
I am having trouble creating individual review comments. Click here to see my feedback.
apps/api/src/modules/ai-suggestion/utils/category-resolver.util.ts (53-58)
현재 구현된 matchTitleToCategory는 제목을 단어 단위로 쪼개어 카테고리명과 일치하는지 확인합니다. 이 방식은 '유산소 운동'과 같이 공백이 포함된 다중 단어 카테고리명을 처리하지 못하는 한계가 있습니다. 또한 긴 카테고리명을 우선적으로 매칭(예: '운동'보다 '유산소 운동'을 먼저 확인)하도록 개선하는 것이 정확도를 높이는 데 도움이 됩니다.
const sortedNames = Array.from(categoryByName.keys()).sort((a, b) => b.length - a.length);
for (const name of sortedNames) {
if (new RegExp("(^|\\s)" + name + "(\\s|$)").test(title)) {
return categoryByName.get(name)!;
}
}
apps/mobile/app/(app)/(tabs)/feed/_layout.tsx (42-43)
날씨 배지(WeatherForecastBadge)가 현재 선택된 날짜(selectedDate)가 아닌 오늘(today)의 날씨만 표시하도록 변경되었습니다. 피드 탭에서 사용자가 캘린더를 통해 다른 날짜를 조회할 때 해당 날짜의 날씨 정보가 함께 업데이트되지 않는 기능적 회귀가 발생합니다. 의도된 변경이 아니라면 기존처럼 useFeedDate를 사용하여 선택된 날짜의 날씨를 보여주도록 수정이 필요합니다. (상단의 useToday 임포트도 useFeedDate로 복구해야 합니다.)
const [selectedDate] = useFeedDate();
return <WeatherForecastBadge date={formatDate(selectedDate)} />;
apps/mobile/src/features/todo/presentations/components/Calendar/Calendar.tsx (302)
현재 initialScrollIndex를 value로 설정하여 깜빡임을 해결하려는 방식은, 안드로이드 등에서 데이터 윈도우를 동적으로 재설정할 때 발생하는 UI 깜빡임 문제를 야기할 수 있습니다. 저장소 규칙(Rule 13)에 따라, 페이저 컴포넌트 구현 시 동적 재중심화 대신 가상화(virtualization)와 함께 매우 큰 정적 데이터 범위를 사용하여 경계 문제와 깜빡임을 방지하는 구조로 개선해 주세요.
References
- For pagers or carousels, instead of dynamically re-centering the data window, which can cause UI flickering on Android, consider using a very large, practically infinite static data range combined with virtualization to avoid boundary issues.
Release v1.3.2
⚙️ Chores
Expo SDK 55 패치 정렬 + 안전 라이브러리 업데이트 (#569)
expo ~55.0.5 → ~55.0.24,react-native 0.83.2 → 0.83.6,react-native-worklets 0.7.2 → 0.7.4,jest-expo ~55.0.13 → ~55.0.17,@expo/dom-webview 55.0.6/@expo/metro-runtime 55.0.11명시 추가,@shopify/react-native-skia를 SDK 55 bundle^2.4.18로 정렬apps/mobile/index.js신규(import 'expo-router/entry';),package.json#main을"index.js"로 전환,metro.config.js에 워크스페이스 루트 진입점 요청을 Uniwind 리졸버보다 먼저 처리하는resolveRequest추가pnpm.packageExtensions보강 —uniwind@*에metro@0.83.7,metro-cache@0.83.7peer 보강heroui-native 1.0.1 → 1.0.3,tailwindcss ^4.2.1 → ^4.3.0,tailwind-merge ^3.5.0 → ^3.6.0,uniwind ^1.5.0 → ^1.6.5,@tanstack/react-query ^5.90.21 → ^5.100.10,react-hook-form ^7.71.2 → ^7.76.0,@react-navigation/native ^7.1.28 → ^7.1.33,@react-navigation/bottom-tabs 7.10.1 → ^7.15.5,es-toolkit ^1.45.1 → ^1.46.1,react-native-svg-transformer ^1.5.2 → ^1.5.3📝 Docs
문서 메타데이터 표준화 및 CLAUDE.md 구조 개선
.md문서(33개)에Version/Last Updated/Owner메타 블록 추가CLAUDE.md를 2026-04 권장 포맷(얇은 인덱스 +.claude/*.md위임)으로 재작성apps/mobile/CLAUDE.md420 → 123줄로 축약 (세부 내용은apps/mobile/.claude/architecture.md로 위임)/api-docs통일,.env.docker.development→.env.docker.dev, 깨진PUSH_NOTIFICATION_GUIDE링크 제거정적 검사(typecheck/단위 테스트)는 통과했으나 다음 항목은 시각적 확인 권장:
heroui-native1.0.2/1.0.3 픽스: field 패딩, input primary border 색상, scroll-shadowinverted, select trigger, tabs RTLuniwind1.6.5: themed variable resolution 픽스 — 다크/라이트 토글 시 미세 변화 가능tailwindcss4.2 → 4.3: CSS 출력 미세 차이🧪 테스트
pnpm install정합 ✅pnpm lint통과pnpm typecheck통과pnpm -F @aido/mobile test— 610 / 610 ✅npx expo-doctor— 18 / 18 ✅📦 버전
@aido/mobile: 1.3.1 → 1.3.2@aido/api: 변경 없음 (서버 코드 변경 없음, 문서 메타데이터만 수정)