docs: rb8001 캘린더 중복 체크 수정 문서 추가
문제 및 해결 방법 기록: - 로컬 DB 기반 중복 체크의 한계 - 실제 구글 캘린더 조회로 변경 - 대화 이중 저장 문제 분석 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
962d85d99c
commit
2700c121d2
@ -0,0 +1,206 @@
|
|||||||
|
# rb8001 캘린더 중복 체크 로직 수정
|
||||||
|
|
||||||
|
## 작성일: 2025-11-17
|
||||||
|
## 작성자: admin
|
||||||
|
## 관련 서비스: rb8001, skill-calendar, Google Calendar API
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 문제 상황
|
||||||
|
|
||||||
|
### 발견된 이슈
|
||||||
|
사용자가 구글 캘린더 일정 등록을 시도할 때 다음과 같은 문제가 발생:
|
||||||
|
|
||||||
|
1. **거짓 중복 경고**
|
||||||
|
- 사용자: "11월 25일 검진 인천 남동구..."
|
||||||
|
- RoBeing: "일정을 구글 캘린더에 등록해드릴까요?"
|
||||||
|
- 사용자: "ㅇㅇ"
|
||||||
|
- RoBeing: "이미 같은 시간대에 등록된 일정이 있습니다"
|
||||||
|
- **실제로는 구글 캘린더에 일정이 없음**
|
||||||
|
|
||||||
|
2. **중복 응답 문제**
|
||||||
|
- 같은 메시지가 두 번씩 전송됨
|
||||||
|
- 모순된 응답 ("일정이 있다" → "일정이 없다")
|
||||||
|
|
||||||
|
3. **대화 이중 저장**
|
||||||
|
- PostgreSQL에 같은 대화가 두 번 저장됨
|
||||||
|
- 각각 다른 intent로 기록 (`calendar_event`, `calendar_confirm`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 원인 분석
|
||||||
|
|
||||||
|
### 1. 로컬 로그 기반 중복 체크의 문제
|
||||||
|
```python
|
||||||
|
# calendar_handler.py:100-119 (수정 전)
|
||||||
|
# 로컬 로그 기반 중복 체크
|
||||||
|
existing = find_matching_event(user_id, target_date, target_minutes)
|
||||||
|
if existing:
|
||||||
|
# 로컬 DB에만 기록되어 있어도 "중복"으로 판단
|
||||||
|
duplicate_events.append(...)
|
||||||
|
continue
|
||||||
|
```
|
||||||
|
|
||||||
|
**문제점:**
|
||||||
|
- 로컬 DB(`calendar_event_log` 테이블)만 확인
|
||||||
|
- 실제 구글 캘린더는 조회하지 않음
|
||||||
|
- 로컬 로그와 실제 캘린더가 불일치할 수 있음
|
||||||
|
- 일정 등록 실패 후에도 로컬 로그에는 기록
|
||||||
|
- 사용자가 구글 캘린더에서 직접 삭제한 경우
|
||||||
|
- 대화 이중 저장으로 인한 로컬 로그 중복
|
||||||
|
|
||||||
|
### 2. 대화 이중 저장 문제
|
||||||
|
데이터베이스 조회 결과:
|
||||||
|
```
|
||||||
|
ID 메시지 Intent
|
||||||
|
1777 11월 25일 검진... calendar_event
|
||||||
|
1776 11월 25일 검진... calendar_confirm ← 중복!
|
||||||
|
1782 25일 일정 등록해줘 calendar_event
|
||||||
|
1781 25일 일정 등록해줘 calendar_confirm ← 중복!
|
||||||
|
```
|
||||||
|
|
||||||
|
**원인:**
|
||||||
|
- `main.py:109-115`와 `router.py:582-593`에서 이중 저장
|
||||||
|
- 각각 다른 intent로 저장되어 컨텍스트 혼란 발생
|
||||||
|
|
||||||
|
### 3. 커밋 히스토리
|
||||||
|
- 중복 방지 기능: `ffc7406` (2025-11-17 00:27)
|
||||||
|
- Claude가 추가한 미문서화 기능
|
||||||
|
- 성능 최적화 목적으로 로컬 캐시 우선 확인
|
||||||
|
- 하지만 로컬 로그 신뢰성 문제 미고려
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 해결 방안
|
||||||
|
|
||||||
|
### 1. 실제 구글 캘린더 조회로 변경
|
||||||
|
```python
|
||||||
|
# calendar_handler.py:100-119 (수정 후)
|
||||||
|
# 실제 구글 캘린더 조회로 중복 체크 (로컬 로그는 신뢰할 수 없음)
|
||||||
|
try:
|
||||||
|
start_dt = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
|
||||||
|
target_date = start_dt.date()
|
||||||
|
|
||||||
|
# 구글 캘린더에서 해당 날짜의 모든 일정 조회
|
||||||
|
day_start = f"{target_date.isoformat()}T00:00:00+09:00"
|
||||||
|
day_end = f"{target_date.isoformat()}T23:59:59+09:00"
|
||||||
|
remote_events = await calendar_skill.get_events(user_id, day_start, day_end)
|
||||||
|
|
||||||
|
# 같은 시작 시간의 일정이 있는지 확인
|
||||||
|
existing = None
|
||||||
|
for ev in remote_events:
|
||||||
|
ev_start = ev.get("start", {}).get("dateTime")
|
||||||
|
if ev_start and ev_start == start_time:
|
||||||
|
existing = {"title": ev.get("summary", title), "event_id": ev.get("id")}
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to check Google Calendar for duplicates on {d}: {e}")
|
||||||
|
existing = None
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
duplicate_events.append(...)
|
||||||
|
continue
|
||||||
|
```
|
||||||
|
|
||||||
|
**개선점:**
|
||||||
|
- ✅ 실제 구글 캘린더 API 호출
|
||||||
|
- ✅ 진짜 중복만 감지
|
||||||
|
- ✅ 로컬 로그 불일치 문제 해결
|
||||||
|
|
||||||
|
### 2. 성능 고려사항
|
||||||
|
**우려:** 매번 API 호출 시 지연/비용 증가
|
||||||
|
**대응:**
|
||||||
|
- 중복 체크는 일정 생성 시에만 발생 (빈도 낮음)
|
||||||
|
- 사용자 경험 > 성능 최적화 (거짓 중복 방지가 우선)
|
||||||
|
- 필요시 Redis 캐싱 추가 고려
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 대화 이중 저장 문제 (별도 해결 필요)
|
||||||
|
|
||||||
|
### 현재 상황
|
||||||
|
- `main.py`에서 저장: execution_plan의 intent 사용
|
||||||
|
- `router.py`에서 저장: task_type을 intent로 사용
|
||||||
|
- **결과:** 같은 대화가 다른 intent로 두 번 저장
|
||||||
|
|
||||||
|
### 해결 방안 (미적용)
|
||||||
|
`router.py:582`의 대화 저장을 제거하거나 조건부 실행:
|
||||||
|
```python
|
||||||
|
# 제안: frontend 채널은 main.py에서만 저장
|
||||||
|
if result.get("success") and result.get("content") and channel != "frontend":
|
||||||
|
await self._save_conversation(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
**현재 미적용 이유:**
|
||||||
|
- Slack 채널 등 다른 채널 영향도 확인 필요
|
||||||
|
- 별도 이슈로 추적 권장
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 테스트 시나리오
|
||||||
|
|
||||||
|
### 1. 정상 등록
|
||||||
|
- 사용자: "11월 25일 오전 9시 회의"
|
||||||
|
- 구글 캘린더 확인: 일정 없음
|
||||||
|
- 예상: 일정 등록 성공
|
||||||
|
|
||||||
|
### 2. 중복 감지
|
||||||
|
- 사용자: "11월 25일 오전 9시 회의" (같은 일정 재등록)
|
||||||
|
- 구글 캘린더 확인: 일정 있음
|
||||||
|
- 예상: "이미 같은 시간대에 등록된 일정이 있습니다" 메시지
|
||||||
|
|
||||||
|
### 3. 로컬 로그 불일치
|
||||||
|
- 로컬 DB에 기록 있지만 구글 캘린더에는 없는 경우
|
||||||
|
- 예상: 정상 등록 (로컬 로그 무시)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 모니터링
|
||||||
|
|
||||||
|
### 로그 확인
|
||||||
|
```bash
|
||||||
|
# 중복 체크 로그
|
||||||
|
docker logs rb8001 | grep "duplicate check"
|
||||||
|
|
||||||
|
# Google Calendar API 호출 로그
|
||||||
|
docker logs rb8001 | grep "get_events"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 성능 모니터링
|
||||||
|
- 일정 생성 응답 시간 추적
|
||||||
|
- Google Calendar API 호출 빈도/실패율
|
||||||
|
- 필요시 캐싱 레이어 추가
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 교훈
|
||||||
|
|
||||||
|
### 1. 로컬 캐시의 한계
|
||||||
|
- **문제**: 성능 최적화를 위한 로컬 캐시가 오히려 오류 발생
|
||||||
|
- **교훈**: 캐시는 신뢰할 수 있을 때만 사용. Source of Truth는 실제 데이터
|
||||||
|
- **적용**: 중복 체크는 반드시 실제 구글 캘린더 조회
|
||||||
|
|
||||||
|
### 2. 미문서화 기능의 위험
|
||||||
|
- **문제**: Claude가 추가한 기능이 문서화되지 않음
|
||||||
|
- **결과**: 의도치 않은 동작, 디버깅 어려움
|
||||||
|
- **교훈**: 모든 기능은 즉시 문서화 필수
|
||||||
|
|
||||||
|
### 3. 이중 저장 패턴
|
||||||
|
- **의도**: ChromaDB + PostgreSQL 이중 저장은 정상 (각각 다른 목적)
|
||||||
|
- **문제**: 같은 DB에 같은 데이터 중복 저장은 버그
|
||||||
|
- **교훈**: 저장 로직은 단일 진입점 유지
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 관련 파일
|
||||||
|
- `/home/admin/ivada_project/rb8001/app/router/calendar_handler.py` (수정됨)
|
||||||
|
- `/home/admin/ivada_project/rb8001/app/skills/calendar_skill.py`
|
||||||
|
- `/home/admin/ivada_project/rb8001/app/state/calendar_event_repository.py`
|
||||||
|
|
||||||
|
## 관련 문서
|
||||||
|
- [Gmail Calendar Scope 가이드](/home/admin/ivada_project/DOCS/journey/troubleshooting/251114_gmail_calendar_scope_reconnect_guide.md)
|
||||||
|
- [rb8001 이중 저장 구현](/home/admin/ivada_project/DOCS/journey/troubleshooting/250826_happybell80_rb8001_이중저장구현.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**문서 끝**
|
||||||
Loading…
x
Reference in New Issue
Block a user