사용 가이드

KidsNote → Google Calendar 자동 동기화

키즈노트 알림장의 행사·준비물을 LLM(Grok)으로 분석해 매일 자동으로 Google 캘린더에 등록합니다.

운영 중 Cron: 매일 09:00 KST

1. 개요

키즈노트는 어린이집/유치원에서 학부모에게 공지를 전달하는 앱인데, 행사 일정과 준비물이 자유 서술된 알림장 형태로 올라옵니다. 학부모가 매번 읽고 캘린더에 옮겨 적는 작업을 자동화하는 것이 이 프로젝트의 목표입니다.

입력

키즈노트 알림장 (선생님/원장님 공지)

처리

Grok LLM 으로 행사·준비물 추출

출력

Google Calendar 이벤트 (자동 등록)

2. 시스템 구조 (어떻게 구현했는가)

       ┌──────────────┐    ① 매일 09:00 KST            ┌──────────────────┐
       │  node-cron   │ ─────────────────────────────▶ │   runOnce()      │
       └──────────────┘                                 └────────┬─────────┘
                                                                │
       ┌─────────────────────────────────────────────────────────┘
       │
       ▼  ② 키즈노트 로그인 → /api/v1_2/children/.../reports/
  ┌──────────────────────┐     cookies.json 으로 세션 캐시
  │  KidsNoteClient      │     (만료되면 자동 재로그인)
  │  src/kidsnote.js     │
  └──────────┬───────────┘
             │ reports[]
             ▼  ③ state.db 로 신규 리포트 필터링
  ┌──────────────────────┐     processed_reports 테이블에 이미 본 ID 기록
  │  State (SQLite WAL)  │     → 같은 리포트는 다시 LLM 호출 안 함
  │  src/state.js        │
  └──────────┬───────────┘
             │ new reports[]
             ▼  ④ teacher / admin 작성 글만, 본문 ≥ 20자 인 것만 LLM 호출
  ┌──────────────────────┐     xAI Grok API (OpenAI SDK 호환)
  │  EventExtractor      │     response_format: json_object
  │  src/extractor.js    │     SYSTEM_PROMPT → 이벤트/준비물 JSON
  └──────────┬───────────┘
             │ events[] (정규화: key, dedupKey 부여)
             ▼  ⑤ 과거 일정 / 글로벌 중복 / 이미 등록된 일정 필터
  ┌──────────────────────┐     dedup_key = sha1(normalize(title)|start_date)
  │  Dedup 로직 (main.js)│     → 여러 알림장이 같은 행사 언급해도 1번만 등록
  └──────────┬───────────┘
             │ unique events[]
             ▼  ⑥ Google Calendar API events.insert
  ┌──────────────────────┐     OAuth2 token.json (refresh_token 자동 갱신)
  │  CalendarClient      │     description 에 원본 공지 본문 첨부
  │  src/calendar.js     │
  └──────────────────────┘

핵심 설계 포인트

파일 구조

kidsnote-calendar-sync/
├── src/
│   ├── main.js          # 파이프라인 오케스트레이션 + cron 스케줄링
│   ├── kidsnote.js      # 키즈노트 로그인/리포트 fetch
│   ├── extractor.js     # Grok LLM 으로 이벤트 추출 + 정규화
│   ├── calendar.js      # Google Calendar 클라이언트
│   ├── state.js         # SQLite (better-sqlite3) 상태 관리
│   └── logger.js
├── scripts/
│   ├── google-auth.js   # 최초 1회 OAuth 인증 (readline 방식)
│   └── test-extract.js  # LLM 추출만 테스트
├── data/                # volume mount (gitignore)
│   ├── credentials.json # Google OAuth 클라이언트
│   ├── token.json       # access/refresh 토큰
│   ├── cookies.json     # 키즈노트 세션
│   └── state.db         # SQLite (WAL)
├── docker-compose.yml
├── Dockerfile
└── .env

3. 설치 & 초기 설정

3.1 필요한 정보 수집

항목어디서
KIDSNOTE_USERNAME / PASSWORD키즈노트 로그인 계정 (카카오 OAuth 계정 ✗)
KIDSNOTE_CHILD_IDF12 → Network → reports 요청 URL의 children/{여기}
KIDSNOTE_CLASS_ID같은 URL의 ?cls={여기}
KIDSNOTE_CENTER_ID같은 URL의 ¢er_id={여기}
KIDSNOTE_ENROLLMENT_ID같은 요청의 x-enrollment 헤더
GROK_API_KEYconsole.x.ai 에서 API Key 발급

3.2 .env 작성

cp .env.example .env
# 위에서 수집한 값으로 채우기

3.3 Docker 이미지 빌드

docker compose build

4. Google OAuth 1회 인증

4.1 Cloud Console 설정

  1. Google Cloud Console → 새 프로젝트 (예: kidsnote-sync)
  2. API 및 서비스 → 라이브러리 → "Google Calendar API" 검색 → 사용 설정
  3. OAuth 동의 화면 → User Type: 외부 → 앱 이름/이메일 입력 → 테스트 사용자에 본인 Gmail 추가 (중요)
  4. 사용자 인증 정보 → OAuth 클라이언트 ID 생성 → 애플리케이션 유형: 데스크톱 앱 → JSON 다운로드
  5. 다운로드한 JSON 을 data/credentials.json 으로 저장
주의: "외부" + "테스트 모드" 상태에서는 테스트 사용자 목록에 있는 Gmail 만 인증할 수 있습니다. 캘린더 등록받을 본인 계정을 꼭 추가하세요.

4.2 토큰 발급

docker compose run --rm kidsnote-calendar-sync node scripts/google-auth.js
  1. 출력된 URL 을 브라우저에서 열어 인증
  2. 인증 후 브라우저가 http://localhost/?code=... 로 리다이렉트 (연결 실패 페이지가 떠도 정상)
  3. 주소창의 URL 에서 code= 뒤 부분을 복사해 터미널에 붙여넣기
  4. data/token.json 생성 완료 → 이후 자동 갱신

5. 실행 & 운영

5.1 1회 테스트 실행

docker compose run --rm -e CRON_SCHEDULE= kidsnote-calendar-sync node src/main.js --once

5.2 상시 스케줄러 모드 권장

docker compose up -d        # 백그라운드 시작
docker compose logs -f      # 실시간 로그
docker compose ps           # 상태 확인

.envCRON_SCHEDULE=0 9 * * * 기준 매일 오전 9시(KST)에 자동 실행됩니다. 컨테이너 시작 시에도 1회 즉시 실행합니다. restart: unless-stopped 이므로 호스트 재부팅 후에도 자동 시작.

5.3 자주 쓰는 명령

동작명령
수동 1회 실행docker compose exec kidsnote-calendar-sync node src/main.js --once
재시작docker compose restart
.env 수정 후 적용docker compose up -d --force-recreate
정지docker compose down

6. LLM 프롬프트

알림장에서 행사·준비물을 어떻게 추출하는지가 이 시스템의 핵심입니다. 정확한 시스템 프롬프트와 호출 설정, 정규화 로직을 별도 문서로 정리했습니다.

프롬프트 MD 다운로드 파일명: kidsnote-extractor-prompt.md

프롬프트 수정 시: src/extractor.jsSYSTEM_PROMPT 상수를 직접 편집한 뒤 docker compose up -d --force-recreate. 이미 처리된 리포트는 다시 분석되지 않으니, 특정 리포트를 재추출하려면 state.db 의 해당 행을 삭제하세요.

7. 트러블슈팅

증상해결
OAuth "access blocked: 인증되지 않은 앱" Cloud Console → OAuth 동의 화면 → 테스트 사용자에 본인 Gmail 추가
키즈노트 로그인 실패 비밀번호 확인. 카카오 OAuth 계정은 지원되지 않습니다. data/cookies.json 삭제 후 재시도
이벤트가 중복 등록되지 않음 정상. state.dbcalendar_events.dedup_key 가 중복을 막습니다. 재등록 원하면 해당 행 삭제
일정이 잘못 추출됨 src/extractor.jsSYSTEM_PROMPT 수정 → 재시작
"Insufficient Permission" 에러 현재 scope 은 calendar.events 만. calendarList.list 등 다른 API 는 불가 (의도된 최소 권한)
Google 토큰 만료 refresh_token 으로 자동 갱신. 그래도 안 되면 data/token.json 삭제 후 scripts/google-auth.js 재실행