---
title: "작업보고 — 코치챗 파일첨부 수동전송·질문유형 자동분류·도구턴 원가분리·결제 준비·운동종목 자동분류·대화/메모리 저장·기억 활성화"
category: "workreport"
document_type: "작업보고"
source_status: "generated"
knowledge_group: "03_history"
priority: "High"
purpose: "코치챗 6대 작업 보고(PR#73~76): 파일첨부 자동기록→첨부 후 수동전송, 질문유형(question_type) 자동분류, 도구턴/코칭턴 원가분리(turn_type 컬럼), 결제 준비(PG 직전), 운동종목 자동분류, 코치 대화·메모리 저장/기억 활성화(2겹 opt-in 안전설계·저장배선·기억주입). 전 작업 Codex 사전검토+사후검수(민감 작업 2사이클). storageGate flip, 개인정보·건강정보 별도 동의 기반."
read_when: ["코치노트AI","최신상태복구","DB·RLS·보안"]
updated: "2026-06-09"
work_timestamp: "20260609_231135"
context: "달록본레포CC (D:\\dallog\\dallog_git) — 코치챗 6대 작업(PR#73~76)"
source_of_truth: "https://dallog-tools.hansbridge.co.kr/knowledge/"
---
# 작업보고 — 코치챗 6대 작업 (PR #73~76)

## 한 줄 요약

이번 스레드는 **(1) 코치챗 파일 첨부를 "자동 기록"에서 "첨부 후 직접 전송"으로 전환**하고, 이어 사장님이 고른 미결 4건 중 **코딩으로 닫을 수 있는 전부** — **질문유형 자동분류 · 도구턴 원가 분리 · 결제(PG 직전)까지 준비 · 운동종목 자동분류 · 코치 대화/메모리 저장·기억 활성화** — 를 구현·검수·머지했다. 모든 코딩 작업은 **Codex 사전검토 + 사후검수(민감 작업은 2사이클)**를 거쳤다.

## 배경 (비개발 사장님께 드리는 쉬운 설명)

이번 스레드의 핵심 철학은 두 가지다.
1. **"말 한마디로 다 되는 코치"를 더 매끄럽게** — 파일을 올렸을 때 무조건 기록으로 처리하지 않고, 사용자가 "이거 분석만 해줘" 같은 비기록 대화도 할 수 있게 했다.
2. **"기억하는 코치"로의 전환** — 그동안 코치는 새로고침하면 대화가 사라지는 일회용 상담창이었다. 이제 (동의한 사용자에 한해) 코치가 이전 대화와 메모리를 기억하는 개인 코치가 된다.

그리고 GPT가 대신 써준 지시서에는 "이런 기능을 만들어라"가 많았는데, **실제 코드를 열어보니 이미 만들어져 있던 것이 다수**였다. 그래서 헛수고 없이 **진짜 빠진 부분만** 구현했다.

---

## PR #73 — 코치챗 파일 업로드: 자동 기록 → 첨부 후 수동 전송

### 무엇을 바꿨나 (전/후)

| | 이전 | 이후 |
|---|---|---|
| 파일 올리면 | **즉시** "이 파일을 기록해줘"로 감싸 **자동 전송** | 텍스트로 변환해 입력창 위 **📎칩으로 대기** |
| 전송 | 업로드 = 전송 | **보내기 버튼(또는 Enter)**을 눌러야 전송 |
| 빈 프롬프트 | 불가(항상 기록 요청) | **파일만 보내기** 가능 |
| 기록 강제 | 항상 기록으로 처리 | **강제 안 함** — 기록할지 대화할지 코치가 판단 |

### 흐름

```
파일 선택·드래그·Ctrl+V
   → 텍스트 변환(OCR/CSV/XLSX) → 📎칩으로 누적(×로 개별 제거)
   → [보내기] → (프롬프트 + 첨부 본문) 합쳐 전송
```

### Codex 검수 반영
- **[버그]** 소진/캡 상태에서 보내면 `chat.send` 전에 첨부를 비워 **올린 파일이 사라지던** 손실 → `send`가 boolean 반환하게 해 막히면 입력·첨부 유지.
- 중복 전송 가드(`submitInFlight`), 제거 버튼 `aria-label` 접근성.

---

## PR #74 — 질문유형(question_type) 자동분류 + "AI에게 보내기" 문구

### question_type 자동분류 (어드민 전수분석 Phase1의 빈 조각)
- 그동안 모든 코칭 요청이 `other_unknown`(미분류)으로만 기록돼, 운영 분석에서 **"사용자가 무엇을 묻는지"가 안 보였다.**
- 신규 `classifyQuestion.ts`: 사용자 질문을 **키워드 우선순위 규칙**으로 10종 분류. AI 추가 호출(비용↑) 없이 규칙 기반.

```
우선순위: 통증·부상 > 오늘운동판단 > 식단 > 체성분 > 러닝 > 근력 > 장기흐름 > 앱사용 > 범위밖 > 기타
예) "오늘 무릎 아픈데 뛰어도 될까" → pain_recovery (안전 최우선)
    "지난주보다 페이스 어땠어"     → running_analysis
```
- dispatch의 성공·실패·도구 3경로 모두에 분류값 전달. **단위 케이스 11/11 통과.**

### "AI에게 보내기" 문구 정합화
- 실제로는 전체 기간 데이터를 보내는데 안내만 "30일"로 남아 어긋나 있었다 → "체성분·러닝·근력(전체 기간)"으로 수정.

---

## PR #75 — 미결 3건: 도구턴 원가분리 · 결제 준비 · 운동종목 자동분류

> ⚠ **GPT 지시 vs 실제 코드 차이를 먼저 바로잡음** (사장님 경고대로):
> GPT는 "도구턴 무차감·원가로깅·결제구조·운동분류를 만들어라"고 했으나, 실제로는 **대부분 이미 구현돼 있었다.** 아래는 *진짜 빠진 부분*만.

### #2 도구턴 / 코칭턴 원가 구분
- **이미 됨**: 도구(기록추가·설정변경) 실행 턴은 코칭 회를 **차감하지 않고**(무차감) 원가만 로깅하는 구조.
- **빠진 것**: 로그에 "코칭턴 vs 도구턴" 구분 컬럼이 없어, 어드민에서 **둘의 원가를 따로 못 봤다.**
- **구현**: `coach_request_log.turn_type` 컬럼 추가(마이그레이션) → 로깅·어드민 KPI 분리("코칭턴 원가 / 도구턴 원가") → `coach_usage_summary()`에서 도구턴을 **'사용 코칭 회'에서 제외**(무차감인데 회로 세던 것 교정) → 코치챗에 "실행은 차감 안 됨" 안내.

### #3 결제 준비 (PG는 의도적으로 미연동 유지)
- **이미 됨(거의 전부)**: 플랜 7종·자격·사용량·차감 RPC·관리자 지급(beta/vip 포함)·플랜 쇼핑/소진/캡 UI·결제 Phase 분기. = **사실상 PG 연결 직전까지 준비 완료.**
- **빠진 것(미세)**: 관리자가 beta/vip를 부여하면 제공량이 NULL이라 **기존 사용권이 0으로 회수**되던 버그.
- **구현**: 부여 시 미확정 제공량이면 기존 사용권 보존(`merge-duplicates,missing=default`) + catalog 조회 실패 early return + 관리자에게 "베타/VIP는 수동 지급" 안내.

### #4 운동종목 category 자동분류
- **이미 됨**: 확인 카드에 분류 표시, DB `category`는 TEXT(새 분류 가능), 도구가 category 인자 수용.
- **빠진 것**: 코치가 "벤치프레스→웨이트"처럼 **알아서 분류하도록 지시가 없었고**, 애매한 운동을 되묻지 않고 무조건 '기타'로 흡수.
- **구현**: systemPrompt + 도구 스키마에 **자동분류 가이드**(프리웨이트→웨이트, 자기체중→맨몸, 기구→머신, 버티기→아이소메트릭) + **애매·생소하면 되묻기** + 사용자가 원하면 **새 분류 허용**. (앱 전역 `ExerciseCategory` 타입은 56곳·근력 볼륨 계산에 얽혀 광역 변경은 회귀위험 → 채팅 경로를 정본으로, 타입 광역화는 보류·사유기록.)

### Codex 협업 (PR #75)
- 사전검토 1회 + 사후검수 **2사이클**. 반영: usage_summary 도구턴 제외 / changePlan lost-update·조회실패 분기 / `missing=default` 신규행 처리.

---

## PR #76 — 코치 대화·메모리 저장/기억 활성화 (대형, 최고 민감도)

### 핵심 — "2겹 opt-in" 안전 설계

![개인정보처리방침 — 저장·국외이전·파기 고지](pacelog-archive/pages/02-workreport/assets-20260609_작업보고/privacy_policy.png)

달록 코치를 **"매번 휘발되는 상담창"에서 "이전 대화·메모리를 기억하는 개인 코치"**로 전환했다. 변호사 자문 없이 **표준약관 기반**으로 안전하게 가동하는 방식은 다음과 같다.

```
실제 저장 = (전역 게이트 storageGate = ON)  AND  (사용자가 설정에서 직접 동의)
            └ 코드로 켬                         └ 기본값 = 전원 미동의(opt-in)

→ 게이트를 켜도, 동의한 사람만 저장된다. 동의 안 하면 저장 0.
```

### 발견 (GPT가 몰랐던 핵심)
GPT는 "동의 UI만 만들면 된다"고 봤지만, 실제로는 **대화를 DB에 저장하는 호출이 어디에도 안 걸려 있었다**(코치챗이 100% 인메모리, 새로고침 시 소실). 동의 UI만 만들고 게이트를 켜면 "저장 동의"는 보이는데 실제 저장이 안 되는 **거짓 약속**이 됐을 것이다. 그래서 **저장 배선까지** 진짜로 연결했다.

### 구현 4종

| 영역 | 내용 |
|---|---|
| **동의 UX** | 설정>계정에 **별도 저장 동의 토글**(전체 약관에 묻지 않음) + 고지(무엇/왜/어디 안 씀/통제권 + 처리방침·건강동의 링크) + 저장 대화 **열람·개별삭제** + **전체삭제**. |
| **저장 배선** | 정상 코칭 성공 턴의 user+assistant만 **best-effort 저장**(도구·확인대기·실패 턴 제외 → 가짜 대화 방지). 저장 실패는 채팅을 막지 않음. thread-id 캡처·키링으로 경합 제거. |
| **기억 주입** | 최근 저장 대화의 끝부분(≤1200자)만 "[이전 대화 기억]"으로 주입. 동의 철회·삭제 시 **즉시 미주입.** |
| **게이트 flip** | `storageGate.enabled` false→true. 기본 미동의라 자동저장 0. |

![건강정보 수집·이용 별도 동의 — AI 처리 위탁 고지](pacelog-archive/pages/02-workreport/assets-20260609_작업보고/health_consent.png)

### Codex 협업 (PR #76 — 최고 민감도, 2사이클)
- **사전검토**에서 함정 예고: 철회·삭제 후 미주입 / 미리보기 턴 저장 금지 / RLS 에러 처리.
- **사후검수 1사이클**: 비동기 저장이 thread를 늦게 읽어 새 대화/삭제와 경합 → **threadId 캡처** / 단건삭제 user_id 방어.
- **사후검수 2사이클**: `conversationEnsuredRef`(boolean 공유)가 thread 교체 시 경합 → **thread-id 키링**으로 자기교정.
- **미반영(제품 결정 이관)**: `addCoachAiNote`(코칭AI 카드) 비게이트 — 핸드오프에 명시된 **의도 설계**(사용자 관리 카드 + 매 쓰기 확인카드 승인). 저장 토글로 묶으면 비동의 사용자의 기존 코칭AI 설정 기능이 막힘 → **사장님 판단 필요.**

---

## app_settings 회귀확인 (직전 스레드 #72 파급)

직전 스레드에서 고친 app_settings 저장 버그의 회귀 여부를 **코드 전수 확인**: 잔존 `onConflict:'key'` **0건**, 목표·루틴·저장운동·개인메모·브리프 캐시 모두 안전 helper(`upsertAppSetting`) 사용. **코드 레벨 회귀 없음.** (라이브 클릭 검증만 사장님 몫.)

## 검증

- 모든 PR `npm run build` 통과, 워커 `node --check` 통과.
- 분류기 단위 케이스 11/11 통과.
- Codex: 사전검토 2회 + 사후검수 4사이클(영역별).
- MCP 라이브 브라우징: 약관/건강동의 페이지(저장 동의의 법적 근거) 캡처 확인(위 스크린샷). 코치챗·설정·어드민 등 인증·미배포 기능은 데모/배포 제약으로 라이브 캡처 불가 → 아래 사장님 수동 확인 항목.

## ⚠ 사장님 액션 필요 (배포·라이브 확인)

1. **마이그레이션 선적용** — `migrations/2026-06-09_coach_request_log_turn_type.sql`을 Supabase에 적용(코드보다 먼저). turn_type 컬럼 + usage_summary 정정.
2. **워커 재배포** — `workers/admin-analytics`(원가 분리·beta/vip 보정), `workers/chat-proxy`(운동분류 스키마). `cd workers/<해당> && npx wrangler deploy`.
3. **#1 저장/기억 라이브 확인** — 본인 계정 설정>계정에서 저장 동의 ON → 코치와 대화 → 새로고침 후 저장 대화가 목록에 뜨는지 / 동의 OFF면 저장 안 됨 / 전체삭제 후 코치가 과거를 언급 안 하는지.
4. **제품 결정** — `addCoachAiNote` 비게이트 유지 여부(위 PR#76 참조).

## 열린 결정 — 사장님 제안 정리(코딩 아님)

- **도구턴 무차감 = 과금모델**: 무차감 유지 권고(말로 하면 손해인 역인센티브 방지 + 원가 모니터링 + 남용 시 캡).
- **결제 PG**: 베타 중 보류, "데이터 먼저"(1인당 원가 실측 후 BETA/VIP 제공량·단가 확정) → 정식 오픈 직전 포트원/토스 연동.
- **운동 category**: 현행 '기타' 흡수 + 자동분류·되묻기·새 분류로 충분. 엄격화 불요.

## 작업 리드타임

- 이번 스레드 작업(PR #73~76 + 미결 보고·제안) 진행: 2026-06-09.
- 본 작업보고 작성·발행: 2026-06-09 23:11 (KST).
