一键导入
add-calendar-integration
차량운행일지와 Google Calendar API 간의 양방향/단방향 동기화, Service Account 및 사용자별 OAuth2 인증 패턴 가이드.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
차량운행일지와 Google Calendar API 간의 양방향/단방향 동기화, Service Account 및 사용자별 OAuth2 인증 패턴 가이드.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
프로젝트 구조와 네이밍 컨벤션에 맞게 새 React 컴포넌트를 추가하는 가이드
PDF(인쇄 레이아웃) 및 Excel(xlsx 동적 로딩) 파일 내보내기 구현 및 갱신 패턴 가이드
Gemini 3.1 Flash Lite API를 사용한 계기판 OCR 및 증빙 서류 AI 판별 연동 패턴 가이드
Sentry에 잡힌 환경/브라우저/외부 의존성 발 노이즈 에러를 필터링한다. 사용자가 "Sentry 노이즈", "Sentry 에러 필터", "이 에러 무시", "Sentry에 자꾸 뜨는 X 막아줘" 등을 요청할 때 발동한다.
React 단위 테스트(Vitest) 및 E2E 테스트(Playwright) 작성 컨벤션과 Mocking 가이드
Cloud Functions, 프론트엔드 빌드 및 배포 시 발생하는 다양한 오류(Node 버전, 의존성 충돌, 소스맵)의 해결 패턴을 모아둔 가이드.
| name | add-calendar-integration |
| description | 차량운행일지와 Google Calendar API 간의 양방향/단방향 동기화, Service Account 및 사용자별 OAuth2 인증 패턴 가이드. |
차량운행일지는 차량별/조직별 공용 구글 캘린더 연동 외에도, 개별 사용자의 개인 구글 캘린더와 예약을 연동하는 OAuth2 기반 양방향 동기화 기능을 제공합니다. 캘린더 연동 로직 수정이나 추가 시 아래 구조를 따릅니다.
캘린더 연동은 대상과 시나리오에 따라 두 가지 인증 방식을 사용합니다.
googleapis의 google.auth.JWT 객체를 생성하여 액세스 권한을 획득합니다. GOOGLE_CALENDAR_SERVICE_ACCOUNT 환경변수(JSON)를 사용합니다.import { google } from 'googleapis';
const credentials = JSON.parse(process.env.GOOGLE_CALENDAR_SERVICE_ACCOUNT || '{}');
const auth = new google.auth.JWT(
credentials.client_email,
undefined,
credentials.private_key,
['https://www.googleapis.com/auth/calendar.events']
);
const calendar = google.calendar({ version: 'v3', auth });
googleapis의 google.auth.OAuth2 객체를 사용합니다. 사용자별 동의화면을 거쳐 획득한 refresh_token을 활용해 통신합니다.oauthCallback.ts 등 리디렉션 처리 핸들러 및 토큰 관리 헬퍼.import { google } from 'googleapis';
export function getOAuth2Client() {
return new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI // 예: http://localhost:5001/{project}/asia-northeast3/oauthCallback
);
}
사용자의 OAuth2 refresh_token은 보안이 확보된 안전한 영역에 저장되어야 하며, 만료 시 자동으로 access_token이 갱신되어야 합니다.
import { getOAuth2Client } from "./oauth";
import { db } from "../core/firestore"; // Firestore 인스턴스
export async function getAuthenticatedClient(uid: string) {
const oauth2Client = getOAuth2Client();
// 1. Firestore의 보안 서브콜렉션에서 refresh_token 조회
const tokenDoc = await db.doc(`users/${uid}/secure/oauth`).get();
if (!tokenDoc.exists) {
throw new Error("구글 캘린더가 연동되어 있지 않습니다.");
}
const { refresh_token } = tokenDoc.data()!;
// 2. OAuth 클라이언트에 자격증명 설정
oauth2Client.setCredentials({ refresh_token });
// (선택) access_token 자동 갱신 리스너 등록
oauth2Client.on('tokens', async (tokens) => {
if (tokens.refresh_token) {
// 새로 갱신된 refresh_token이 있다면 보안 저장소 갱신
await db.doc(`users/${uid}/secure/oauth`).update({
refresh_token: tokens.refresh_token,
updatedAt: new Date()
});
}
});
return oauth2Client;
}
onReservationCreated 등)를 통해 동기화합니다.calendarEventId를 예약 문서(reservations/{resId})에 보관하여 연관 관계를 맺어야 합니다.watch 기능을 등록하여 구글 측에서 이벤트 변경 발생 시 Webhook을 수신합니다.syncSource: 'google-calendar' 플래그를 Firestore 문서 변경 옵션에 포함해야 합니다.구글 API 호출 중 401 Unauthorized 또는 403 Forbidden 에러가 발생한 경우, 사용자의 연동 권한이 취소되었거나 토큰이 무효화되었음을 의미합니다.
users/{uid} 문서의 calendarConnected 필드를 false로 변경합니다.users/{uid}/secure/oauth에 보관된 무효화된 토큰 정보를 삭제 처리합니다.