بنقرة واحدة
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에 보관된 무효화된 토큰 정보를 삭제 처리합니다.