From ffbdd6037af8212c202d072358a33fea5099e245 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Sat, 22 Nov 2025 11:27:27 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20=EB=93=B1=EB=A1=9D=20=EC=8B=A4=ED=8C=A8=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20TDD=20=EC=A0=91=EA=B7=BC=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...calendar_event_registration_failure_tdd.md | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 journey/troubleshooting/251122_calendar_event_registration_failure_tdd.md diff --git a/journey/troubleshooting/251122_calendar_event_registration_failure_tdd.md b/journey/troubleshooting/251122_calendar_event_registration_failure_tdd.md new file mode 100644 index 0000000..705d8bf --- /dev/null +++ b/journey/troubleshooting/251122_calendar_event_registration_failure_tdd.md @@ -0,0 +1,231 @@ +# 캘린더 일정 등록 실패 문제 TDD 접근 + +**작성일**: 2025-11-22 +**작성자**: Claude +**목적**: "11월 24일 월요일 오후 1시부터 4시까지 컴퍼니엑스 세미나" 등록 실패 문제 해결 + +--- + +## 1. 문제 상황 + +### 사용자 시나리오 +``` +사용자: "11월 24일 월요일 오후 1시부터 4시까지 컴퍼니엑스 세미나" +로빙: "네, 11월 24일 월요일 오후 1시부터 4시까지 컴퍼니엑스 세미나 일정을 등록해 드릴게요. 구글 캘린더에 등록해드릴까요?" +사용자: "ㅇㅇ" +로빙: "일정 등록에 실패했습니다. 날짜/시간 형식을 다시 확인해 주세요." ❌ +``` + +### 로그 분석 (2025-11-22 11:23) + +**rb8001 로그**: +``` +[Calendar] Extracted - date: 2025-11-24, time_range: 13:00 ~ 16:00, location: None, title: 컴퍼니엑스 세미나 +``` +✅ **파싱 성공**: 날짜, 시간, 제목 모두 정상 추출 + +**skill-calendar 로그**: +``` +Failed to create event: ('invalid_grant: Token has been expired or revoked.', {'error': 'invalid_grant', 'error_description': 'Token has been expired or revoked.'}) +``` +❌ **문제**: Google OAuth 토큰 만료/취소 + +--- + +## 2. 문제 원인 분석 + +### 2.1 rb8001 파싱 로직 (✅ 정상) +- 자연어 시간 범위 파싱: "오후 1시부터 4시까지" → "13:00 ~ 16:00" ✅ +- 제목 추출: "컴퍼니엑스 세미나" ✅ +- 날짜 추출: "11월 24일" → "2025-11-24" ✅ + +### 2.2 skill-calendar OAuth 토큰 문제 (❌ 실패) +- Google OAuth 토큰이 만료되었거나 취소됨 +- 토큰 갱신 로직이 있지만 실패 (`invalid_grant` 에러) +- skill-email과 동일한 패턴이지만 skill-calendar에는 제대로 적용되지 않음 + +--- + +## 3. TDD 계획 + +### 3.1 테스트 작성 (Red Phase) + +#### 테스트 1: 자연어 시간 범위 파싱 +```python +def test_natural_language_time_range_parsing(): + test_cases = [ + ("오후 1시부터 4시까지", "13:00 ~ 16:00"), + ("오전 9시부터 오후 5시까지", "09:00 ~ 17:00"), + ("1시부터 4시까지", "01:00 ~ 04:00"), + ] + # 검증: extract_time_range_generic() 함수 +``` + +#### 테스트 2: 제목 추출 +```python +def test_title_extraction_from_original_message(): + test_cases = [ + ("11월 24일 월요일 오후 1시부터 4시까지 컴퍼니엑스 세미나", "컴퍼니엑스 세미나"), + ] + # 검증: 원본 메시지에서 날짜/시간 제거 후 제목 추출 +``` + +#### 테스트 3: 전체 파싱 워크플로우 +```python +def test_full_parsing_workflow(): + # 실제 사용자 입력 시나리오 전체 테스트 + # 날짜 추출 → 시간 범위 추출 → ISO 변환 +``` + +#### 테스트 4: skill-calendar 토큰 갱신 +```python +def test_token_refresh_on_expired(): + # 만료된 토큰에 대해 자동 갱신 시도 + # skill-email 패턴 참고 +``` + +### 3.2 구현 (Green Phase) + +#### 단계 1: rb8001 파싱 로직 개선 (✅ 완료) +- `extract_time_range_generic()`: 자연어 시간 범위 파싱 추가 +- 제목 추출 로직 개선: 원본 메시지에서 직접 추출 +- `calendar_approval` 이후 `calendar_confirm` 찾기 로직 개선 + +#### 단계 2: skill-calendar 토큰 갱신 로직 확인 +- `skill-email/services/gmail_service.py` 패턴 참고 +- `skill-calendar/services/google_calendar_service.py`에 동일한 로직 적용 +- 토큰 갱신 실패 시 사용자에게 재인증 안내 + +### 3.3 리팩터링 (Refactor Phase) +- 중복 코드 제거 +- 에러 처리 개선 +- 로깅 개선 + +--- + +## 4. 기존 코드 확인 + +### 4.1 잘 되던 것 확인 + +#### skill-email 토큰 갱신 패턴 (참고) +```python +# skill_email/services/gmail_service.py:64-75 +if creds and hasattr(creds, 'expired') and creds.expired and creds.refresh_token: + logger.info(f"Refreshing expired token for user: {user_id}") + from google.auth.transport.requests import Request + creds.refresh(Request()) + + # 갱신된 토큰 저장 + if hasattr(self.creds_provider, 'save_credentials'): + self.creds_provider.save_credentials(user_id, creds) +``` + +#### skill-calendar 현재 상태 +- `get_credentials()`: 토큰 로드만 수행 +- `create_event()`: 토큰 갱신 없이 바로 API 호출 +- ❌ **문제**: 만료된 토큰으로 API 호출 시 `invalid_grant` 에러 + +### 4.2 중복/충돌 확인 + +#### 중복 코드 +- `skill-email`과 `skill-calendar`의 토큰 갱신 로직이 중복될 수 있음 +- 공통 모듈로 분리 고려 (선택사항) + +#### 충돌 없음 +- rb8001 파싱 로직과 skill-calendar API는 독립적 +- 문제는 skill-calendar의 OAuth 토큰 관리만 해당 + +--- + +## 5. 해결 방안 + +### 5.1 즉시 해결 (우선순위 1) + +#### skill-calendar 토큰 갱신 로직 추가 +```python +# skill-calendar/services/google_calendar_service.py +async def get_credentials(self, user_id: str) -> Optional[Credentials]: + creds = await self._load_credentials_from_db(user_id) + + if not creds: + return None + + # 토큰 갱신 (skill-email 패턴 참고) + try: + if creds and hasattr(creds, 'expired') and creds.expired and creds.refresh_token: + logger.info(f"Refreshing expired token for user {user_id}") + from google.auth.transport.requests import Request + creds.refresh(Request()) + + # 갱신된 토큰 저장 (DB 업데이트) + await self._save_credentials_to_db(user_id, creds) + logger.info(f"Refreshed token saved for user {user_id}") + except Exception as e: + logger.warning(f"Token refresh failed for user {user_id}: {e}") + # Refresh 실패해도 계속 진행 (401 에러가 나면 그때 처리) + + return creds +``` + +### 5.2 사용자 안내 개선 (우선순위 2) + +#### 에러 메시지 개선 +```python +# rb8001/app/router/calendar_handler.py +if not result: + logger.error(f"[Calendar] Event creation failed - OAuth token may be expired") + return { + "success": False, + "message": "일정 등록에 실패했습니다. Google 캘린더 연동을 다시 확인해 주세요." + } +``` + +### 5.3 장기 개선 (우선순위 3) + +#### OAuth 재인증 플로우 +- 토큰 갱신 실패 시 사용자에게 재인증 안내 +- auth-server의 OAuth 재인증 URL 생성 API 활용 + +--- + +## 6. 테스트 실행 계획 + +### 6.1 단위 테스트 +```bash +cd /home/admin/ivada_project/rb8001 +docker exec -it rb8001 python3 tests/test_calendar_natural_language_parsing.py +``` + +### 6.2 통합 테스트 +```bash +# 1. rb8001에서 일정 등록 요청 +# 2. skill-calendar API 호출 확인 +# 3. Google Calendar API 호출 확인 +``` + +### 6.3 E2E 테스트 +```bash +# 실제 사용자 시나리오 테스트 +# "11월 24일 월요일 오후 1시부터 4시까지 컴퍼니엑스 세미나" 입력 +# "ㅇㅇ" 승인 +# Google Calendar에 일정 등록 확인 +``` + +--- + +## 7. 참고 문서 + +- `DOCS/journey/plans/251114_skill_calendar_multiplatform_integration.md`: 캘린더 통합 계획 +- `DOCS/journey/troubleshooting/250828_gmail_token_auto_refresh_RESOLVED.md`: Gmail 토큰 갱신 해결 사례 +- `skill_email/services/gmail_service.py`: 토큰 갱신 패턴 참고 +- `rb8001/tests/test_calendar_query_vs_create.py`: 기존 캘린더 테스트 + +--- + +## 8. 다음 단계 + +1. ✅ TDD 테스트 작성 완료 (`test_calendar_natural_language_parsing.py`) +2. ⏳ skill-calendar 토큰 갱신 로직 추가 +3. ⏳ 테스트 실행 및 검증 +4. ⏳ 배포 및 E2E 테스트 +