On this page
Google Meet 링크 생성
프로그래밍 방식으로 Google Meet 링크를 생성하면서 배운 교훈이에요.
Google에는 Meet 링크를 만드는 API가 두 개 있어요. 그중 하나는 절반의 사용자에게 동작하지 않는데, 문서에는 그 사실이 언급되어 있지 않아요.
문제 상황
Google Meet 링크를 프로그래밍 방식으로 생성해야 했어요. 무료 Gmail 계정과 유료 Google Workspace 계정 모두에서 동작해야 하는 기능이었죠. Google은 이걸 할 수 있는 API를 두 개 제공하는데, 잘못된 걸 선택하는 바람에 하루를 디버깅에 날렸어요.
검토한 옵션
| 옵션 | 장점 | 단점 |
|---|---|---|
| Calendar API + conferenceData | 범용(Gmail + Workspace), 검증된 방식 | 캘린더 쓰기 권한 필요, 생성/삭제 오버헤드 |
| Meet REST API(spaces.create) | 직접 생성, 더 많은 제어 | Workspace 전용 - 무료 Gmail에서 동작 안 함 |
| 미리 생성된 Meet 링크 | 단순함 | 확장 불가, 보안 우려 |
핵심 발견
Google Meet REST API는 Google Workspace가 필수예요. 무료 Gmail 계정에서는 spaces.create를 호출할 수 없어요. API가 permission 에러를 반환하는데, 계정 유형이 원인이라는 힌트는 전혀 없었어요.
Workspace 테스트 계정으로 작동하는 통합을 완성한 후에야 이 사실을 알게 됐어요. QA를 위해 무료 Gmail 계정으로 전환하자마자 모든 게 깨졌죠.
해결책: Calendar API + conferenceData
Calendar API 방식은 conferenceData가 포함된 임시 캘린더 이벤트를 만들고, Meet 링크를 추출한 다음, 이벤트를 삭제해요. 해킹처럼 들리지만, 이게 업계 표준 접근 방식이에요.
// 1. Use the calendar's integration for OAuth
// 2. Create event with conferenceData
// 3. Extract Meet link
// 4. Delete temporary event
// 5. Return persistent Meet link (survives event deletion) 왜 이 방식이 동작하는가
- 범용 호환성: 무료 Gmail과 유료 Workspace 계정 모두에서 동작해요 — 계정 유형에 따른 제한이 없어요
- 이벤트 삭제 후에도 Meet 링크 유지: 임시 캘린더 이벤트를 삭제해도 회의실은 계속 접근 가능해요
- 명확한 소유권: 캘린더 소유자가 회의 호스트가 되고, 캘린더 쓰기 권한이 회의 생성 권한으로 매핑돼요
- 최소 오버헤드: 이벤트 생성, 링크 추출, 이벤트 삭제 — 1초 안에 끝나는 API 호출 세 번이에요
Rate Limit에 대해 알게 된 것
두 API 모두 무료예요. Calendar API는 이 패턴에 대해 넉넉한 할당량을 제공해요. 한 가지 알아둘 점은 무료 Gmail 계정에서 3명 이상 참여하는 회의에 60분 제한이 있다는 거예요.
Meet 링크 제거하기 (conferenceData = null)
나중에 기존 이벤트에서 Meet 링크를 제거해야 할 일이 생겼어요. 이것도 별도의 디버깅 세션이 됐죠.
hangoutLink는 Google Calendar API에서 읽기 전용이에요 — conferenceData에서 파생되는 값이라 직접 설정할 수 없어요. Meet 링크를 제거하려면 conferenceDataVersion: 1과 함께 conferenceData: null을 보내야 해요.
세 가지 상태 의미론
API는 conferenceData의 값과 conferenceDataVersion 설정 여부에 따라 다르게 동작해요:
| conferenceData 값 | conferenceDataVersion | Google 동작 |
|---|---|---|
{...} (truthy) | 1 | conference data 설정/업데이트 |
null | 1 | conference data 제거 (Meet 링크 삭제) |
undefined (생략됨) | 0 | 무시 — 기존 conference data 유지 |
무음 실패 함정
conferenceDataVersion: 1 없이 보내면 Google은 모든 conference 변경을 조용히 무시해요. API는 200 OK를 반환해요. 응답도 정상처럼 보여요. 하지만 아무것도 안 바뀌어요.
이 때문에 제거 관련 버그가 특히 진단하기 어려워요. 코드가 잘 동작한다고 생각하게 되거든요 — API가 그렇다고 말하니까요 — 근데 Meet 링크는 그대로 남아 있는 거예요.
TypeScript 타입 갭
Google의 googleapis npm 패키지 타입은 conferenceData를 Schema$ConferenceData | undefined로 정의해요 — union에 null이 없어요. 하지만 REST API는 conference data를 제거하기 위해 null을 실제로 받아들여요. type assertion으로 우회해야 해요:
// Google's types don't model null for request semantics
(event as Record<string, unknown>).conferenceData = null; TypeScript 타입과 실제 API 의미론이 맞지 않는 경우예요. REST API 문서가 진짜 소스 오브 트루스에요.
핵심 교훈
계정 유형 요구사항을 먼저 확인하세요 — API마다 무료/유료 계정에서 다른 기능을 제공할 수 있어요. 접근 방식을 확정하기 전에 둘 다 테스트하세요.
“새롭고 반짝이는” 것이 항상 좋은 건 아니에요 — Meet REST API(2024년 2월 출시)가 이상적으로 보였지만, 더 오래된 Calendar API 방식에는 없는 치명적인 제한이 있었어요.
우회 방법이 영구적인 해결책이 될 수 있어요 — 임시 이벤트를 만들고 삭제하는 건 이상하게 느껴지지만, Google이 직접 권장하는 방식이에요.
200 OK 응답이 거짓말할 수 있어요 —
conferenceDataVersion이 빠지면 Google은 요청을 받아들이고 아무것도 안 해요. 상태 변경은 항상 후속 조회로 확인하세요.타입 정의가 API 현실보다 뒤처질 수 있어요 — 공식 타입이 유효한 API 상태(예: 제거를 위한
null)를 모델링하지 않을 때는 REST 문서를 확인하고 타겟 type assertion을 사용하세요.