gws는 그러지 않는다 — Google의 Discovery Service(각 API가 스스로를 설명하는 JSON 문서)를 실행할 때마다 읽어서 명령 트리를 통째로 만들어낸다. 그래서 Google이 새 API 메서드를 추가하면 gws는 코드 수정 없이 그걸 자동으로 갖게 된다. 게다가 사람뿐 아니라 AI 에이전트를 1순위로 염두에 둔 설계라, 모든 출력이 구조화 JSON이고 95개의 에이전트 스킬을 함께 싣는다. (저장소: googleworkspace/cli · Rust(edition 2021) · npm·Cargo·Nix·Homebrew 배포 · Apache-2.0 · v0.22.5 · TrendShift Daily #6 · "공식 지원 제품 아님" 명시)이 레포가 무엇을 하는 물건인가.
대부분의 도구는 쓸 수 있는 명령을 코드 안에 미리 적어둔다. 그래서 Google이 API에 기능을 더하면 도구도 업데이트를 기다려야 한다. gws는 반대다 — Google이 공개한 "API 설명서(Discovery 문서)"를 실행 순간에 읽어 명령을 만들기 때문에, 새 엔드포인트가 생기면 자동으로 따라잡는다.
gws는 Google Workspace 전체를 다루는 단일 CLI다. Drive(파일), Gmail(메일), Calendar(일정), Sheets(스프레드시트), Docs(문서), Chat(채팅), Admin(관리), Tasks·Slides·Forms·Keep·Meet·Classroom·People까지 — 평소엔 각 API마다 따로 있던 도구들을 gws <서비스> <리소스> <메서드>라는 하나의 문법으로 통일한다.
가장 중요한 설계 결정은 "명령을 하드코딩하지 않는다"는 것이다. gws 바이너리 안에는 drive files list 같은 명령이 적혀 있지 않다. 대신 실행할 때 Google의 Discovery 문서를 받아와, 그 문서에 적힌 리소스·메서드·파라미터를 읽어 명령 트리를 즉석에서 조립한다. 도구가 "API를 외우는" 게 아니라 "API에게 매번 물어보는" 구조다.
gws ...를 친다). gws는 이 프로젝트가 설치하는 실행 파일 이름(=Google WorkSpace). 패키지 이름은 @googleworkspace/cli지만, 실제로 터미널에서 부르는 명령은 gws 하나다.트렌딩 이유 · 기존 방식 대비 장점.
화제의 핵심은 세 가지가 겹친 데 있다 — ① Google이 직접 낸 레포(단, "공식 지원 제품은 아님"을 명시), ② AI 에이전트를 1순위로 둔 설계, ③ Discovery 런타임 생성이라는 영리한 접근. 마침 2026년 트렌딩 상위권이 온통 "에이전트 스킬"로 채워진 흐름과 정확히 맞물렸다(같은 날 Daily 순위에 AI agent·AI skills 토픽이 1·3위).
| 항목 | 기존 방식(gcloud·언어별 SDK·수제 래퍼) | gws |
|---|---|---|
| 명령 출처 | 도구에 하드코딩(릴리스마다 갱신) | Discovery 문서를 런타임에 읽어 동적 생성 |
| 새 API 반영 | SDK/도구 업데이트 기다림 | 문서가 바뀌면 자동 반영(코드 수정 0) |
| 커버 범위 | 서비스마다 따로 | 19개 서비스를 한 문법으로 |
| 출력 | 사람이 읽는 표·텍스트 위주 | 전부 구조화 JSON(에이전트가 바로 소비) |
| 에이전트 통합 | 직접 툴링 작성 | 95개 SKILL.md + Gemini 확장 + Model Armor 내장 |
"AI 에이전트 우선"이 단순 홍보 문구가 아니라 코드 곳곳의 결정으로 드러난다 — 모든 응답이 JSON이고, 실패도 {exit code}로 분류되며(파싱 없이 분기 가능), 함께 실린 CONTEXT.md엔 에이전트를 위한 "교전 수칙"(필드 마스크로 토큰 아끼기, 변경 작업은 --dry-run 먼저)까지 적혀 있다.
Google Workspace는 API가 수십 개에 메서드가 수백 개다. 이걸 사람이 일일이 명령으로 옮겨 적으면, 새 기능이 나올 때마다 도구를 고치고 다시 배포해야 한다. 사용자는 그 사이 "도구는 아직 그 명령을 모른다"는 공백을 견뎌야 한다.
gws는 명령을 외우는 대신 Discovery 문서를 실행 시점에 읽는다. Google이 메서드를 추가하면 문서에 자동으로 반영되고, gws는 그걸 그대로 명령으로 노출한다. 도구를 고치지 않아도 API의 최신 표면을 항상 따라잡는, "스스로 갱신되는 CLI"가 된다.
라이브러리 크레이트 · CLI 바이너리 · 배포 채널.
코드는 Rust 워크스페이스(여러 패키지를 한 저장소에서 함께 빌드)로, 역할이 둘로 갈린다 — 순수 로직을 담은 라이브러리 크레이트 google-workspace와, 실제 명령줄 도구인 바이너리 크레이트 google-workspace-cli(=gws). 무거운 런타임은 전부 Rust 생태계의 검증된 크레이트를 쓴다.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 언어 | Rust (edition 2021, 워크스페이스 2 크레이트) | 전체 구현. 단일 정적 바이너리로 배포. |
| 명령 파싱 | clap 4 (derive·string) | Discovery 문서로 Command 트리를 동적 구성. |
| HTTP | reqwest 0.12 (rustls·stream·socks) | Google API 호출. OS 의존 OpenSSL 없이 rustls. |
| 비동기 런타임 | tokio 1 (full) | 네트워크 I/O·동시성. #[tokio::main]. |
| 직렬화 | serde · serde_json 1 | Discovery 문서·요청/응답 JSON 파싱·생성. |
| OAuth | yup-oauth2 12 | OAuth2 로그인·토큰·서비스 계정 흐름. |
| 자격증명 암호화 | aes-gcm 0.10 + keyring 3.6 + zeroize | 토큰을 AES-256-GCM으로 암호화, 키는 OS 키링에. |
| 설정 TUI | ratatui 0.30 + crossterm 0.29 | gws auth setup의 터미널 대화형 화면. |
| 시간/타임존 | chrono + chrono-tz + iana-time-zone | +agenda 등 시간 인식 헬퍼. |
| 로깅 | tracing (+subscriber·appender) | 구조화 로그, JSON 파일 일일 로테이션. |
| 메일/기타 | mail-builder · base64 · mime_guess2 · uuid · toml · dotenvy | Gmail 본문 작성·업로드·환경변수 로드 등. |
| 배포 채널 | npm · Cargo · Nix flake · Homebrew | npm은 GitHub Releases의 사전 빌드 바이너리를 내려받음. |
google-workspace(라이브러리)와 도구 gws(바이너리)로 나눠, 로직과 CLI 껍데기를 분리했다.clap::Command 트리를 런타임에 만든다. clap을 "동적으로" 쓰는 드문 사례라 그 자체가 학습거리다.내가 친 명령이 동적 명령 트리를 거쳐 Google API로 가기까지.
먼저 독수리 시점. gws의 핵심 트릭은 "두 번에 나눠 파싱한다(two-phase parsing)"는 것이다. 보통 CLI는 인자를 한 번에 다 파싱하지만, gws는 명령 트리 자체가 실행 시점에 결정되므로 그럴 수 없다. 그래서 ① 먼저 첫 단어만 보고 "어느 서비스인지"를 정한 뒤, ② 그 서비스의 Discovery 문서로 명령 트리를 만들고, ③ 그제서야 나머지 인자를 그 트리로 다시 파싱한다.
이제 대표 흐름 한 줄기를 입력부터 출력까지 따라가자 — "gws drive files list로 내 드라이브 파일을 나열한다". 번호가 곧 two-phase parsing의 단계다.
여기서 ③의 24시간 캐시가 핵심 최적화다. Discovery 문서를 매번 받아오면 명령 하나 칠 때마다 네트워크 왕복이 생긴다. 그래서 gws는 받은 문서를 ~/.config/gws/cache/에 저장해두고 하루 동안 재사용한다. "메뉴판을 매번 새로 받지 않고 하루치 복사본을 책상에 둔다"는 발상이다.
출력은 전부 구조화 JSON으로 통일된다 — 성공도, 에러도, 다운로드 메타데이터도. 게다가 종료 코드를 의미별로 나눠(1=API 에러, 2=인증, 3=잘못된 인자, 4=Discovery 실패, 5=내부 오류), 에러 메시지를 글자로 파싱하지 않고 종료 코드만 보고 스크립트가 분기할 수 있다. 에이전트·자동화 친화적 설계의 정수다.
gws schema drive.files.list로 그 스키마를 직접 들여다볼 수도 있다.한 가지 더, 에이전트를 위한 응답 살균(Model Armor) 통합이 있다. API 응답에 숨은 프롬프트 인젝션(악의적 지시문)이 섞여 에이전트를 오염시키는 걸 막기 위해, --sanitize 플래그로 응답을 Google Cloud Model Armor 템플릿에 통과시킨다. "택배 상자를 열기 전 금속 탐지기에 한 번 통과시키는" 안전장치다.
어디부터 열어봐야 하나.
두 크레이트의 역할 분담만 잡으면 길을 잃지 않는다. crates/google-workspace/는 "API를 이해하는 두뇌"(Discovery 타입·서비스 목록·HTTP 유틸), crates/google-workspace-cli/는 "사람·에이전트와 만나는 얼굴"(인자 파싱·인증·출력·헬퍼)이다. 처음 읽을 땐 main.rs → services.rs → discovery.rs 순서를 권한다.
| 위치 | 역할 |
|---|---|
services.rs | 읽기 출발점. "drive"→(drive, v3)처럼 별칭을 실제 API 이름·버전으로 매핑한 정적 테이블(19개 서비스). 새 서비스는 여기 한 줄 추가. |
discovery.rs(lib) | Discovery 문서의 Rust 타입(RestDescription·RestResource·RestMethod)과 fetch·캐시 로직. gws의 두뇌. |
main.rs | 2단계 파싱의 무대. 첫 인자로 서비스 식별, schema·--help·--version 특수 처리, 이후 동적 트리로 재파싱. |
executor.rs | 파싱된 명령을 실제 HTTP 요청으로 바꿔 보내고 응답을 JSON으로 출력. 페이지네이션·업로드·다운로드 처리. |
credential_store.rs | OAuth 토큰을 AES-256-GCM으로 암호화해 저장하고, 키는 OS 키링(또는 파일 백엔드)에 둔다. 보안 학습의 핵심 파일. |
helpers/ | Discovery로 자동 생성되지 않는, 사람이 손으로 만든 편의 명령(+send·+agenda 등). + 접두사로 자동 명령과 구분. |
skills/ | API마다 하나씩 + 워크플로 레시피까지 95개의 SKILL.md. LLM에게 "이 명령을 이렇게 쓰라"고 알려주는 설명서. |
이 레포에서 배울 만한 것 + 어느 파일을 펴면 되는지.
gws의 정체성. 명령을 하드코딩하는 대신 외부 JSON 스키마로 clap::Command 트리를 런타임에 조립한다. "도구가 API를 외우지 않고 매번 물어본다"는 발상이 어떻게 코드가 되는지를 discovery.rs(타입)→main.rs(2단계 파싱)에서 따라가 본다. 데이터 주도 설계의 교과서적 사례.
#[tokio::main] 비동기 진입, reqwest로 GET/POST·스트리밍 다운로드·멀티파트 업로드, 그리고 자동 페이지네이션을 NDJSON으로 흘려보내는 패턴(--page-all)을 본다. rustls로 OS 의존 없이 정적 바이너리를 만드는 선택도 함께.
실무에서 자주 어설프게 처리되는 부분이다. gws는 yup-oauth2로 로그인하고, 받은 토큰을 AES-256-GCM으로 암호화해 저장하며, 암호화 키는 OS 키링(맥 Keychain·윈도 Credential Manager)에 둔다. zeroize로 메모리에서 비밀을 지우는 처리까지. credential_store.rs가 보안 학습의 핵심.
전부 JSON 출력, 의미별 종료 코드, --dry-run으로 변경 전 검증, --fields 필드 마스크로 컨텍스트 토큰 절약, SKILL.md로 사용법을 LLM에 주입, Model Armor로 응답 살균까지 — 사람용 CLI와 에이전트용 CLI의 차이를 CONTEXT.md와 helpers/에서 구체적으로 배운다.
모든 걸 자동 생성하면 불편한 구석이 남는다(메일 보내기를 raw 본문으로?). 그래서 gws는 Discovery 자동 명령 위에 손으로 만든 + 헬퍼를 얹는다. services.rs의 정적 테이블과 helpers/의 수제 명령이 어떻게 충돌 없이(+ 접두사) 공존하는지가 설계 포인트.
예컨대 에이전트를 위한 CONTEXT.md의 "교전 수칙"은 이렇게 압축된다 — 읽기 전 스키마 확인, 큰 응답엔 필드 마스크, 변경엔 dry-run:
# 1) 구조를 모르면 먼저 스키마부터 본다
gws schema drive.files.list
# 2) 큰 응답은 --fields로 토큰을 아낀다 (에이전트 필수)
gws drive files list --params '{"pageSize":10}' --fields "files(id,name)"
# 3) 변경 작업은 --dry-run으로 먼저 검증한다
gws gmail +send --to a@b.com --subject "Hi" --body "..." --dry-run
설치는 가볍고, 진입 장벽은 'GCP 프로젝트'다.
순수 소프트웨어라 하드웨어 요구는 사실상 없다. 진짜 관문은 Google Cloud 프로젝트와 OAuth 설정이다 — gws는 사용자의 GCP 프로젝트를 통해 API를 호출하므로, 한 번은 OAuth 자격증명을 마련해야 한다(이 부분이 처음 쓰는 사람이 가장 헤매는 지점).
| 항목 | 요구사항 |
|---|---|
| 설치(권장) | GitHub Releases의 사전 빌드 바이너리를 $PATH에 두기. 의존성 없음. |
| 설치(npm) | Node.js 18+. npm i -g @googleworkspace/cli → 적합한 바이너리 자동 다운로드. |
| 설치(기타) | Cargo(cargo install --git …) · Nix(nix run) · Homebrew. |
| 소스 빌드 | Rust 툴체인(cargo). cargo build / cargo test / clippy. |
| 필수 계정 | Google Cloud 프로젝트(OAuth 자격증명용) + Workspace 접근 권한 Google 계정. |
| 인증 자동화(선택) | gcloud CLI가 있으면 gws auth setup이 프로젝트·API 활성화를 자동 처리. |
| 주의(테스트 모드) | 미검증 OAuth 앱은 스코프 ~25개 제한. recommended 프리셋(85+)은 실패 → 필요한 서비스만 선택. |
gws 설치는 '리모컨을 받는 것'이고, GCP 프로젝트 설정은 '그 리모컨을 내 TV에 페어링하는 것'이다. 리모컨(바이너리)은 그냥 주머니에 넣으면 되지만, 한 번은 TV(내 Google 계정/프로젝트)와 짝을 맞춰(OAuth) 줘야 버튼이 실제로 먹는다. 이 페어링이 끝나면 그 뒤로는 gws auth login 한 번이면 된다.
읽기만 하지 말고 손에 익히는 단계.
바이너리(또는 npm i -g @googleworkspace/cli)를 깔고 gws auth setup→gws auth login -s drive로 드라이브 스코프만 받은 뒤, gws drive files list --params '{"pageSize":5}'로 첫 JSON 응답을 받아본다. OAuth 페어링을 끝까지 한 번 통과하는 게 목표.
gws schema drive.files.list, gws schema sheets.spreadsheets.create를 쳐서 각 메서드가 받는 파라미터·요청 본문 모양을 들여다본다. Discovery 문서가 명령을 만든다는 말이 손에 잡힌다. gws <서비스> --help로 자동 명령과 +헬퍼가 함께 뜨는 것도 확인.
~/.config/gws/cache/를 열어 Discovery 문서가 저장되는 걸 확인하고, 파일을 지운 뒤 같은 명령을 다시 쳐 "다시 받아오는" 동작을 관찰한다. discovery.rs의 24h 캐시 로직과 대조하며 캐시 히트/미스를 눈으로 본다.
npx skills add https://github.com/googleworkspace/cli/tree/main/skills/gws-gmail로 Gmail 스킬을 설치하고, Claude Code/Gemini CLI 같은 에이전트가 gws를 호출해 메일을 정리하게 시켜본다. SKILL.md가 LLM의 행동을 어떻게 바꾸는지 체감하는 단계.
helpers/를 본떠 새로운 + 명령(예: gws drive +recent = 최근 수정 파일 N개)을 만든다. 인자 정의→Discovery 메서드 호출→JSON 가공→출력의 배선을 직접 깔고, cargo build로 빌드해 동작시킨다.
services.rs에 아직 없는 Workspace API를 한 줄 추가해 명령으로 떠오르게 만든 뒤, main.rs에 로그를 심어 "1차 파싱→문서 fetch→트리 빌드→2차 파싱"의 각 단계가 실제로 도는 순서를 출력해본다. 동적 CLI의 심장을 직접 해부.
한 주씩 따라가는 6주 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | Rust CLI 기초 + clap으로 명령 만들기 | The Rust Book · clap 공식 문서 + 실습 1 |
| 2주차 | Google API Discovery 문서 구조 이해 | Discovery Service 문서 · crates/google-workspace/src/discovery.rs + 실습 2 |
| 3주차 | 동적 명령 생성(2단계 파싱) | main.rs · services.rs + 실습 3 |
| 4주차 | tokio·reqwest 비동기 HTTP·페이지네이션 | executor.rs · tokio/reqwest 문서 |
| 5주차 | OAuth2 + 자격증명 암호화(AES·키링) | credential_store.rs · yup-oauth2 + 실습 4 |
| 6주차 | AI 에이전트 통합(SKILL.md·Model Armor·헬퍼) | CONTEXT.md · helpers/ · skills/ + 실습 5·6 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| gws | 이 프로젝트가 설치하는 실행 파일 이름(=Google WorkSpace). 패키지는 @googleworkspace/cli. |
| Discovery 문서 | Google API가 자신을 기계가 읽게 설명한 JSON. 리소스·메서드·파라미터가 들어 있어 gws가 이걸로 명령을 만든다. |
| 2단계 파싱 | 첫 단어로 서비스만 먼저 정하고(1차), Discovery로 트리를 만든 뒤 나머지를 다시 파싱(2차)하는 기법. |
| 스키마 주도 생성 | 코드가 아니라 외부 스키마가 명령의 모양을 결정하는 설계. API 변경을 자동 반영. |
| crate / 워크스페이스 | Rust의 패키지 단위 / 여러 크레이트를 함께 빌드하는 묶음(여기선 라이브러리+바이너리). |
| clap | Rust 명령줄 파서. gws는 이를 런타임에 동적으로 구성해 쓴다. |
| tokio / reqwest | 비동기 런타임 / HTTP 클라이언트. API 호출·스트리밍·업로드를 담당. |
| rustls | 순수 Rust TLS 구현. OpenSSL 의존 없이 정적 바이너리로 배포 가능케 함. |
| OAuth2 / yup-oauth2 | Google 로그인 인증 방식 / 그걸 구현한 Rust 크레이트. 토큰·서비스 계정 흐름 제공. |
| AES-256-GCM | 대칭키 암호. gws가 저장하는 OAuth 토큰을 암호화. 키는 OS 키링에 보관. |
| 키링(keyring) | 맥 Keychain·윈도 Credential Manager 등 OS의 비밀 저장소. 암호화 키를 여기 둔다. |
| 서비스 계정 | 사람 로그인 없이 서버↔서버로 인증하는 자격증명. CI·서버에서 사용. |
| 필드 마스크(--fields) | 응답에서 원하는 필드만 받게 제한. 거대한 JSON으로 에이전트 컨텍스트가 터지는 걸 방지. |
| NDJSON / --page-all | 줄마다 JSON 1개(Newline-Delimited). 자동 페이지네이션 결과를 한 줄씩 흘려보냄. |
| 헬퍼 명령(+) | Discovery 자동 명령과 별개로 손으로 만든 편의 명령. +send·+agenda처럼 + 접두사. |
| --dry-run | 실제 요청 대신 보낼 내용을 미리 보여주는 안전 플래그. 변경 작업 전 검증용. |
| Model Armor | Google Cloud의 응답 살균 서비스. API 응답 속 프롬프트 인젝션을 걸러 에이전트를 보호. |
| 종료 코드(exit code) | 0=성공,1=API,2=인증,3=인자,4=Discovery,5=내부. 글자 파싱 없이 실패 유형으로 분기. |
| SKILL.md | LLM에게 도구 사용법을 알려주는 마크다운 설명서. gws는 95개를 함께 배포. |