# 지능형 과학실 ON 연동 (과학쌤과학랩 → 한국과학창의재단 오픈API)

우리 앱에 붙은 모든 센서(폰·가상·MBL·수동 입력)의 측정값을, 선생님이 **지능형 과학실 ON**에 만든 탐구수업으로도 실시간으로 보내는 기능이다.
정의서 요약·센서타입 표·채널 매핑은 [api-summary.md](api-summary.md), 신청서 문구 초안은 [application-draft.md](application-draft.md).

## 1. 구조

```
학생 브라우저 (/r/방/수업)                 우리 워커 (lab.sciencessaem.com)                지능형 과학실 ON
 core/scion-bridge.js                       worker/api.js  /api/scion/*                  api-scion.kofac.re.kr/sensorapi
 ─ 측정 시작 ─ start {serial} ───────────▶ + serviceKey(비밀 값) ─ explortProcessStart ─▶  일련번호 → 탐구수업 찾기
                                  ◀──────── { sensorId, periodMs } ◀── 식별코드·수집주기 ──
 ─ 표본을 수집주기 칸마다 평균 ─
 ─ 5초마다 data {items ≤200} ────────────▶ 모양 검사 ─ explortDataCollection ─────────▶  그래프에 그림
 ─ 측정 정지 ─ 남은 것 → stop ───────────▶ ─ explortProcessStop ───────────────────────▶  수집 끝
 ◀ 상태 칩 「과학실 ON: 보내는 중 · 12건」    로그엔 액션·코드만(키·값 없음)
 ─ 진행 신호 labProgress + scion{sent,lastCode} ─▶ 수집기 캐시 ─▶ 교사 타일 「과학실 ON 전송 n건」
```

- **인증키는 워커 비밀 값 `SCION_SERVICE_KEY` 에만** 있다. 브라우저·저장소·수집기 시트 어디에도 없다.
- 일련번호는 **역할(센서)마다 하나**: `LAB-{방코드}-{수업코드}-{역할코드}` (예 `LAB-97Q24C-C9VQ88-TEMP`).
  한 역할에 같은 센서가 여러 개면(열평형의 뜨거운 물·찬물) 한 일련번호 안에서 채널코드 01·02로 나눈다(정의서 별첨 3).
- 수업 설정(`_sessions` 탭의 configJSON)에 `scion: { enabled:true, serials:{ 역할: 일련번호 } }` 로 저장된다.
  위저드가 수업 코드를 미리 정해(`proposeId`) 일련번호를 만들 때부터 보여 준다.
- 실패해도 **실험·제출은 그대로** 된다. 학생 화면에는 방 막대의 작은 칩으로만 알린다.
  - 인터넷이 끊기면 5분치까지 모아 두었다가 5초마다 다시 보낸다.
  - 과학실 ON이 거절하면(007 미등록 일련번호 등) 칩에 코드를 보여 주고 그 측정은 보내지 않는다.
  - 창을 닫아 탐구종료를 못 보냈으면 다음 측정 전에 그 식별코드를 먼저 닫는다(009 중복 방지).
- 반복 측정(회차)은 회차마다 시작·정지가 따로 일어나므로 과학실 ON에도 탐구시작·종료가 회차마다 한 쌍씩 간다.

## 2. 선생님 절차

1. 과학실 ON에서 **탐구수업 만들기** → 센서 추가에서 제품 「과학쌤과학랩」의 해당 센서를 고른다.
2. 과학쌤과학랩 교사 대시보드 → 새 수업 만들기 → ④ 보고서 단계에서 **「📡 지능형 과학실 ON으로도 보내기」** 를 켠다.
   역할(센서)마다 일련번호가 나오면 [📋 복사]로 과학실 ON 센서 추가 칸에 붙여 넣는다.
   (수업을 만든 뒤에도 링크 화면·수업 상세의 「과학실 ON」 칸에서 다시 복사할 수 있다.)
3. 학생은 평소처럼 수업 방에 들어와 측정한다. 측정을 시작하면 과학실 ON 탐구수업 화면에 그래프가 함께 그려진다.
4. 수업 상세 타일에서 모둠(학생)마다 「📡 과학실 ON 전송 n건」과 오류 코드를 본다.

## 3. 한계

- **수집 주기는 과학실 ON이 정한다**(탐구시작 응답의 수집주기). 우리 기록기는 보통 10 Hz라 그 주기 칸 안의 표본을 평균해 한 건으로 보낸다.
  우리 앱의 원래 기록(보고서·CSV)은 줄이지 않고 그대로 남는다.
- **정의서에 없는 센서는 보내지 않는다** — 힘·가속도·자기장·CO₂·O₂·소리 진동수·색. 위저드가 그 역할에는 일련번호를 만들지 않고 이유를 보여 준다.
- 수집일자는 **한국 시각**, 칸 시작 시각이다(수집주기 1초 → `2026-09-27 12:00:05`, 0.1초 → `… 12:00:05.3`).
- 가상 센서 값도 보낸다(연습용). 과학실 ON 데이터에서 실제 측정과 구별되지 않으니, 필요하면 수업 설정에서 가상 센서를 빼고 만든다.
- Cloudflare Workers 무료 등급은 하루 10만 요청이다. 학생 30명이 40분 동안 5초마다 보내면 약 1만 5천 요청 — 하루 여러 반이 동시에 쓰면 유료 전환을 검토한다.
- 우리 워커의 속도 제한은 IP당 10분 4,000번(같은 학교 와이파이를 함께 쓰는 한 반 기준). 넘으면 429 → 브라우저가 잠시 뒤 다시 보낸다.

## 4. 운영자(과학쌤) 절차 — 한 번만

1. **회원가입** — https://science-on.kosac.re.kr/ (지능형 과학실 ON)
2. **오픈API 신청** — 오픈API → 오픈API 신청 및 현황.
   업체 「과학쌤과학습」, 제품 「과학쌤과학랩」, 센서 목록은 [application-draft.md](application-draft.md)의 표(센서타입별 한 줄)를 넣는다.
3. **인증키 넣기** — 발급된 인증키를 Cloudflare 대시보드 → **Workers & Pages → sciencelab → Settings → Variables and Secrets → Add**
   → Type **Secret**, 이름 **`SCION_SERVICE_KEY`**, 값 = 인증키 → 저장(Deploy). (명령줄: `npx wrangler secret put SCION_SERVICE_KEY`)
   저장소·`wrangler.jsonc`·코드에는 절대 넣지 않는다.
4. **확인** — https://lab.sciencessaem.com/api/health 가 `"scion":true` 를 돌려준다. `/cloud-setup/` 의 「과학실 ON 연동」 칸도 「인증키 설정됨」으로 바뀐다.
5. **실제 탐구수업으로 시험** — 과학실 ON에 탐구수업 하나를 만들고, 우리 쪽에서 과학실 ON 보내기를 켠 수업(예: 열평형, 가상 센서)을 만들어
   일련번호를 등록한 뒤 학생 방에서 15초쯤 측정 → 과학실 ON 화면에 그래프가 그려지는지, 정지 뒤 탐구종료 버튼이 켜지는지 본다.
   [api-summary.md 7절](api-summary.md#7-우리-쪽-해석실제-인증키로-확인할-것)의 애매한 점(메서드·수집주기 단위·날짜 형식)을 이때 확인한다.

## 5. 개발·검사

```bash
node tools/test/scion-bridge.test.mjs            # 매핑·날짜·묶기·배치·워커 라우트 (모의 과학실 ON 을 빈 포트로 띄움)
node tools/test/collector-contract.test.mjs      # 세션 scion 저장·proposeId·진행 신호 scion (54~57)

# 브라우저로 전체 흐름
node tools/mock-scion.mjs --seed --port 8793     # 모의 과학실 ON (인증키 TESTKEY)
node tools/mock-collector.mjs --seed --port 8790 # 모의 수집기
# .dev.vars: SCION_BASE_URL=http://localhost:8793 · SCION_SERVICE_KEY=TESTKEY · AI_MOCK=1
npx -y wrangler@latest dev --port 8787
curl -X POST localhost:8793/__register -d '{"serial":"LAB-방-수업-TEMP"}'   # 선생님이 과학실 ON에 센서를 추가한 셈
curl localhost:8793/__dump                                                  # 받은 start·data·stop
```

- 환경 변수: `SCION_SERVICE_KEY`(비밀) · `SCION_BASE_URL`(기본 `https://api-scion.kofac.re.kr/sensorapi`) · `SCION_MOCK=1`(키 없이 내장 모의 응답, 로컬 전용).
- 워커 응답: 과학실 ON 코드가 001이 아니면 HTTP 200 `{ ok:false, code, msg: 정의서 문구 }`, 키 없음 503 `{ needKey:true }`, 과학실 ON 5xx·연결 실패·10초 초과 502, 입력 모양이 틀리면 400(과학실 ON으로 보내지 않음).
