195 lines
5.6 KiB
Markdown
195 lines
5.6 KiB
Markdown
# Gmail Calendar Scope 및 Timezone 트러블슈팅
|
|
|
|
**작성일**: 2025-11-14 ~ 2025-11-16
|
|
**작성자**: Claude
|
|
|
|
---
|
|
|
|
## 1. Calendar Scope 추가 (2025-11-14)
|
|
|
|
### 문제
|
|
- skill-calendar가 Google Calendar API 호출 시 `invalid_grant` 에러
|
|
- 원인: Gmail Passport scope에 Calendar API 권한 없음
|
|
|
|
### 해결 절차
|
|
|
|
#### Step 1: auth-server scope 수정
|
|
```python
|
|
# app/providers/gmail_passport.py
|
|
GMAIL_API_SCOPES = [
|
|
"https://www.googleapis.com/auth/gmail.send",
|
|
"https://www.googleapis.com/auth/gmail.readonly",
|
|
"https://www.googleapis.com/auth/gmail.modify",
|
|
"https://www.googleapis.com/auth/userinfo.email",
|
|
"https://www.googleapis.com/auth/userinfo.profile",
|
|
"https://www.googleapis.com/auth/calendar", # 추가
|
|
"https://www.googleapis.com/auth/calendar.events" # 추가
|
|
]
|
|
```
|
|
|
|
```bash
|
|
cd /home/admin/auth-server
|
|
git add app/providers/gmail_passport.py
|
|
git commit -m "feat: Calendar scope 추가"
|
|
git push origin main
|
|
```
|
|
|
|
#### Step 2: Google Cloud Console 설정 (필수!)
|
|
**OAuth 동의 화면에서 Calendar Scope 수동 추가**:
|
|
1. https://console.cloud.google.com/apis/credentials/consent
|
|
2. "범위 추가 또는 삭제" 클릭
|
|
3. "calendar" 검색
|
|
4. 체크:
|
|
- ✅ `.../auth/calendar` (캘린더 보기, 수정, 공유, 영구 삭제)
|
|
- ✅ `.../auth/calendar.events` (캘린더 일정 보기 및 수정)
|
|
5. "업데이트" → "저장 후 계속"
|
|
|
|
**API 활성화**:
|
|
- "API 및 서비스" > "라이브러리" > "Google Calendar API" > "사용 설정"
|
|
|
|
#### Step 3: 사용자별 재인증 (필수!)
|
|
**웹 브라우저**:
|
|
1. https://ro-being.com 로그인
|
|
2. 설정 > 아이템 > Gmail > "재인증"
|
|
3. Google 권한 동의 화면에서 Calendar 권한 확인
|
|
4. "허용" 클릭
|
|
|
|
**중요**: OAuth 토큰은 발급 시 scope가 고정됨. DB 직접 수정으로는 해결 안 됨.
|
|
|
|
---
|
|
|
|
## 2. PostgreSQL 시퀀스 문제 (2025-11-14)
|
|
|
|
### 문제
|
|
- `conversation_log_pk` 중복 키 에러
|
|
- 대화 저장 실패 → approval 로직 작동 불가 ("네" 응답 인식 못함)
|
|
|
|
### 원인
|
|
- `conversation_log` 테이블 id 시퀀스가 out-of-sync
|
|
- 이미 존재하는 id를 insert 시도
|
|
|
|
### 해결
|
|
```sql
|
|
-- 51123 서버에서
|
|
SELECT MAX(id) FROM conversation_log;
|
|
SELECT nextval('conversation_log_id_seq');
|
|
|
|
-- 시퀀스 재정렬
|
|
SELECT setval('conversation_log_id_seq', (SELECT MAX(id) FROM conversation_log) + 1);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Calendar CRUD Timezone 이슈 (2025-11-16)
|
|
|
|
### 문제
|
|
- **CREATE**: 성공
|
|
- **READ**: 방금 등록한 일정 조회 실패
|
|
- **DELETE**: 성공하지만 잘못된 날짜 표시
|
|
|
|
### 원인
|
|
naive datetime (timezone 없음) 사용:
|
|
```python
|
|
# 문제
|
|
start_iso = f"{date}T{start_hour}:{start_min}:00" # "2025-11-17T15:00:00"
|
|
|
|
# Google Calendar API는 timezone 없으면 해석 불확실
|
|
# rb8001 → skill-calendar 전송 시 timezone 불일치
|
|
```
|
|
|
|
### 해결
|
|
**rb8001/app/router/calendar_handler.py 수정**:
|
|
|
|
**parse_time_range()** (Line 397):
|
|
```python
|
|
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
|
|
year, month, day = map(int, date.split('-'))
|
|
tz = ZoneInfo("Asia/Seoul")
|
|
|
|
start_dt = datetime(year, month, day, int(start_hour), int(start_min), 0, tzinfo=tz)
|
|
end_dt = datetime(year, month, day, int(end_hour), int(end_min), 0, tzinfo=tz)
|
|
|
|
return start_dt.isoformat(), end_dt.isoformat()
|
|
# "2025-11-17T15:00:00+09:00"
|
|
```
|
|
|
|
**handle_calendar_query()** (Line 228):
|
|
```python
|
|
today = datetime.now(ZoneInfo("Asia/Seoul"))
|
|
target_date = datetime(year, month, day, tzinfo=ZoneInfo("Asia/Seoul"))
|
|
```
|
|
|
|
### 테스트 결과
|
|
**Before (67%)**:
|
|
- ✅ CREATE
|
|
- ❌ READ (조회 실패)
|
|
- ✅ DELETE
|
|
|
|
**After (100%)**:
|
|
- ✅ CREATE
|
|
- ✅ READ (조회 성공)
|
|
- ✅ DELETE (정확한 날짜 표시)
|
|
|
|
---
|
|
|
|
## 4. 트러블슈팅
|
|
|
|
### invalid_grant 계속 발생
|
|
- Gmail Passport 재인증 확인 (Step 3)
|
|
- gmail_token.scopes 필드에 calendar 포함 확인
|
|
- Google Cloud Console OAuth 동의 화면에 Calendar scope 추가 확인 (Step 2)
|
|
|
|
### Calendar API 403 Forbidden
|
|
- Google Cloud Console에서 Calendar API 활성화 확인
|
|
- OAuth 동의 화면에 Calendar scope 추가 확인
|
|
|
|
### 일정 등록 성공했지만 Calendar에 없음
|
|
- 다른 Google 계정으로 로그인했을 가능성
|
|
- primary calendar 대신 다른 캘린더 ID 사용 여부 확인
|
|
|
|
### "네" 승인 응답 인식 못함
|
|
- PostgreSQL conversation_log 시퀀스 확인 (섹션 2)
|
|
- conversation_log에 대화 저장되는지 확인
|
|
|
|
---
|
|
|
|
## 5. E2E 테스트 시나리오
|
|
|
|
```
|
|
1. auth-server scope 수정 + Google Cloud Console 설정 완료
|
|
2. 김종태 계정 Gmail 재인증
|
|
3. ro-being.com 로그인
|
|
4. 로빙에게 메시지: "11월 24일 검진 07:40~12시 인천 연수구 갯벌로156"
|
|
5. 로빙 응답: "일정을 구글 캘린더에 등록해드릴까요?"
|
|
6. 사용자 응답: "그래"
|
|
7. 로빙 응답: "✅ 구글 캘린더에 일정을 등록했습니다!"
|
|
8. Google Calendar 웹에서 확인: 2025-11-24 07:40~12:00 일정 존재
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 교훈
|
|
|
|
### Datetime 규칙
|
|
- **절대 금지**: naive datetime (timezone 없음)
|
|
- **필수**: `datetime.now(ZoneInfo("Asia/Seoul"))`
|
|
- **전송**: ISO 8601 with timezone (`2025-11-17T15:00:00+09:00`)
|
|
|
|
### Microservice 간 통신
|
|
- 모든 datetime은 timezone offset 포함
|
|
- UTC 통일 또는 명시적 timezone 사용
|
|
- 컨테이너 시스템 timezone 확인: `docker exec [컨테이너] date`
|
|
|
|
### OAuth 토큰 관리
|
|
- 코드만 수정해도 소용없음 - Google Cloud Console 설정 필수
|
|
- 토큰은 발급 시 scope 고정 - 재인증 필수
|
|
- DB 직접 수정으로는 해결 안 됨
|
|
|
|
---
|
|
|
|
## 7. 관련 커밋
|
|
- `4cd0990`: fix: Add timezone awareness to calendar operations
|
|
- `fd7ec1c`: Revert README.md documentation changes
|