---
title: "📅 2026-06-09 (화) 작업일지 — 코치 에이전트 잔여기능·설정 통합/CRUD·app_settings 치명버그·저장기억 활성화"
category: "devlog"
document_type: "개발일지"
source_status: "generated"
knowledge_group: "03_history"
priority: "High"
purpose: "2026-06-09 개발일지(갭 복원). 코치 에이전트 잔여기능(복수기록 공개·브리프탭 실갱신·턴차감 RPC 침묵제거), 설정 6목록 접힘·코칭AI 설정 통합 페이지, 코치챗 설정 CRUD(P3a/P3b)와 런타입 per-user 하이브리드, app_settings onConflict 치명버그 전면수정, 도구턴 원가분리·beta/vip 보정·운동 자동분류, 대화·메모리 저장/기억 활성화까지 시간 흐름으로 기록."
read_when: ["개발일지","갭복원","최신상태복구","2026-06","코치노트AI","app_settings","저장동의"]
updated: "2026-06-09"
work_timestamp: "20260609_230700"
context: "달록본레포CC (D:\\dallog\\dallog_git) — 작업일지 갭 복원. 근거: git log·KB 작업보고·핸드오프."
source_of_truth: "https://dallog-tools.hansbridge.co.kr/knowledge/"
---

> **이 문서가 무엇인가 (비개발자용 한 줄 설명)**
> 2026년 6월 9일(화), 코치 AI 에이전트의 남은 기능들을 마무리하고, 설정 화면을 깔끔하게 정리(긴 목록 접기·코칭AI 설정 한 카드로 통합)하고, 말로 설정을 바꾸는 기능(CRUD=생성·조회·수정·삭제)을 붙였으며, 그 과정에서 **그동안 조용히 저장이 안 되고 있던 치명 버그(app_settings)** 를 잡아내 전면 수정하고, 마지막으로 코치와의 **대화·메모리를 저장하고 다음에 기억하게** 하는 기능까지 켠 하루의 기록이다. 커밋 16건(PR #61~#76)이 쏟아진 대형 작업일이다.

---

## 0. 이 날 한눈에

| 항목 | 내용 |
|---|---|
| 날짜 | 2026-06-09 (화) |
| 커밋/PR | 16건 (PR #61 ~ #76) |
| 첫 커밋 | 09:32 `7d899fe` (#61) 복수 기록 피드 공개 |
| 마지막 커밋 | 23:07 `4d19fb6` (#76) 코치 대화·메모리 저장/기억 활성화 |
| 핵심 줄기 | (1) 코치 에이전트 잔여 4종 마무리 → (2) 설정 UI 정리 + 코칭AI 설정 통합 → (3) 말로 설정 CRUD(P3a/P3b) + per-user 하이브리드 → (4) **app_settings 치명버그 전면수정** → (5) 파일첨부 수동전송·질문 자동분류·원가분리 → (6) 저장/기억 활성화 |
| 가장 중요한 사건 | **app_settings onConflict 치명버그**(아래 "발생 이벤트(특이)" 참조) — 그동안 목표·루틴·메모·브리프캐시 저장이 조용히 실패해 왔을 수 있는 구조적 결함 |
| 관련 작업보고 | #95(코치AI에이전트-잔여4종), #96(코칭AI커스텀-설정CRUD-app_settings저장버그수정), #97(파일첨부수동전송·질문분류·원가분리·저장기억활성화), #99(장기스레드총괄) |
| 관련 핸드오프 | #32(테스트반영-차감버그-잔여기능), #33(코칭AI커스텀-설정CRUD-app_settings) |

**용어 빠른 풀이** — CRUD=생성·조회·수정·삭제, upsert=있으면 수정·없으면 삽입, onConflict=충돌 기준 컬럼(어떤 컬럼이 겹치면 같은 행으로 볼지), RPC=DB 함수 호출, 하이브리드=글로벌 기본값 + 사용자별 덮어쓰기.

---

## 1. 오전 — 코치 에이전트 잔여기능 마무리 (09:32 ~ 10:55)

전날(06-08)까지 코치 AI 에이전트의 골격을 세웠고, 이 날 오전은 그 **잔여 기능 4종**을 닫는 작업이었다. 핵심 목표는 "코치챗 안에서 말 한마디로 공개·갱신이 실제로 일어나게" 하는 것이었다.

### 1-1. 09:32 `7d899fe` (#61) — 복수 기록 피드 공개

| 구분 | 내용 |
|---|---|
| 무엇 | "최근 근력 3일치 자랑해줘" 같은 요청 한 번에 **글 1건**으로 묶어 피드에 발행 |
| 왜 | 기존엔 기록 1건씩만 공개 가능 → 여러 날치를 자랑하려면 여러 번 시켜야 했고 피드가 도배됨 |
| 결과 | 복수 기록을 하나의 게시물로 합쳐 발행 → 자연스러운 "묶음 자랑" 가능 |

### 1-2. 09:37 `b84fec2` (#62) — 턴 차감 RPC 에러 침묵 제거

| 구분 | 내용 |
|---|---|
| 문제 | 코칭 회차 차감을 처리하는 RPC(DB 함수 호출)가 에러가 나도 **조용히 삼켜서**, "왜 차감이 안 됐는지" 진단 자체가 불가능 |
| 원인 | 차감 RPC 호출부가 에러를 무시(silent) 처리 |
| 해결 | 에러를 표면화(비침묵화) → 차감 누락 시 원인 로그·진단 가능 |
| 맥락 | 단, **도구 호출 턴은 설계상 무차감**이 권고 모델이라 "차감 안 됨" 다수는 정상이다 ([[project_coach_tool_turn_billing]]). 이 PR은 "정상 무차감"과 "버그 누락"을 구분 가능하게 만든 것 |

### 1-3. 09:50 `5121771` (#63) — 프로필 대시보드/기록 공개 (코치챗에서)

| 구분 | 내용 |
|---|---|
| 무엇 | "체성분 대시보드 공개해줘"처럼 코치챗 안에서 프로필 대시보드·기록 스냅샷을 공개 |
| 왜 | 프로필 공개 기능(profile_publications)을 코치 에이전트의 도구로 연결 |
| 결과 | 대화만으로 대시보드 공개 토글 가능 |

### 1-4. 10:13 `2802472` (#64) — 코치챗 브리프 생성이 브리프 탭을 실제 갱신

| 구분 | 내용 |
|---|---|
| 문제 | 코치챗에서 브리프를 만들어도 정작 **브리프 탭에는 반영이 안 됨**(따로 노는 두 경로) |
| 원인 | 브리프 생성 파이프라인이 코치챗 쪽과 브리프 탭 쪽으로 이원화 |
| 해결 | 브리프 생성 파이프라인을 **공용 lib로 추출** → 코치챗 생성이 브리프 탭을 실제로 갱신 |
| 결과 | 한 곳에서 만든 브리프가 양쪽에서 동일하게 보임 (단일 진실원) |

### 1-5. 10:55 `0721583` (#65) — GPT 자동 폴백 차단 + 결제에러 사용자 노출 차단

| 구분 | 내용 |
|---|---|
| 무엇 | chat-proxy(코치챗 백엔드 워커)에서 GPT로의 **자동 폴백(대체 호출)을 차단** |
| 왜 | 자동 폴백이 작동하면 의도치 않은 비싼 모델 호출 → **플랜 경제성**이 무너짐 |
| 추가 | 결제 관련 에러 메시지가 사용자에게 그대로 노출되던 것 차단(내부 처리) |
| 결과 | 비용 폭주 방지 + 사용자 경험상 결제 내부 에러 비노출 |

---

## 2. 점심~오후 초반 — 설정 UI 정리 + 코칭AI 설정 통합 (12:34 ~ 13:04)

코치 기능이 늘면서 설정 화면의 목록들이 길어져 스크롤 지옥이 됐다. 이를 **접고 통합**하는 정리 작업.

### 2-1. 12:34 `e92cbe3` (#66) — 긴 목록 6종 기본 접힘

| 구분 | 내용 |
|---|---|
| 무엇 | 설정 화면의 긴 목록 **6종을 기본 접힘** 상태로 |
| 대상 6종 | 신발 · 런타입 · 운동종목 · 저장루틴 · 저장운동 · 개인메모 |
| 왜 | 목록이 모두 펼쳐져 있어 설정 화면이 지나치게 길었음 |
| 결과 | 필요할 때만 펼쳐 보는 구조 → 가독성·탐색성 개선 |

### 2-2. 12:42 `e6fbcc9` (#67) — 펼치기/접기 토글 가시성 강화

| 구분 | 내용 |
|---|---|
| 문제 | 접힘 도입 후 토글 표시가 **작은 화살표**라 접힌 줄 모르고 지나침 |
| 해결 | 작은 화살표 → **테두리 바 + accent 칩(강조 라벨)** 으로 가시성 강화 |
| 결과 | "여기 펼칠 게 더 있다"가 한눈에 보임 |

### 2-3. 13:04 `86c7409` (#68) — AI 브리프지침 + 코치 메모리 일원화 (통합 카드)

| 구분 | 내용 |
|---|---|
| 무엇 | 흩어져 있던 **AI 브리프 지침**과 **코치 메모리** 설정을 `설정 > 코칭AI설정` **통합 카드 하나**로 일원화 |
| 왜 | 두 설정이 다른 위치에 있어 "AI한테 뭘 어떻게 시키는지"를 한곳에서 못 봄 |
| 결과 | 코칭 AI 커스터마이즈를 단일 페이지에서 관리 + 카드 스타일 정리 |

---

## 3. 오후 — 말로 설정 CRUD + per-user 하이브리드 (13:26 ~ 14:42)

이제 설정을 **대화(코치챗)로 직접 바꾸는** 단계(P3a/P3b). 가장 까다로운 데이터 분류 결정이 들어간 구간이다.

### 3-1. 13:26 `94cc2d0` (#69) — 코치챗으로 설정 CRUD (P3a)

| 구분 | 내용 |
|---|---|
| 무엇 | 신발·운동종목을 **말로 추가/수정/삭제**(CRUD=생성·조회·수정·삭제) |
| 예 | "나이키 신발 추가해줘", "이 종목 이름 바꿔줘", "이거 지워줘" |
| 결과 | 설정 화면을 직접 안 열어도 대화로 개인 설정 편집 |

### 3-2. 14:27 `a0f076d` (#70) — 코치챗 개인메모 추가 (P3b) + 런타입 per-user 하이브리드 마이그레이션

| 구분 | 내용 |
|---|---|
| 무엇 (P3b) | 코치챗으로 **개인메모** 추가 가능 |
| 무엇 (마이그레이션) | 런타입(run type, 달리기 유형)을 **per-user 하이브리드** 구조로 전환하는 DB 마이그레이션 적용 |
| 하이브리드란 | 글로벌 기본값(모두 공통) + 사용자별 덮어쓰기(개인 커스텀)를 함께 두는 방식 |
| 왜 | 런타입은 "기본 제공값"과 "사용자가 추가한 값"이 섞여야 함 → boolean 같은 기본여부 플래그가 있으면 하이브리드가 정석 ([[project_data_classification_lesson]]) |
| 순서 주의 | 마이그레이션을 **먼저 적용**하고, 코드는 다음 PR에서 켠다(아래 #71) |

### 3-3. 14:42 `ac0adc7` (#71) — 런타입 개인화 차단 해제 (코드 flip)

| 구분 | 내용 |
|---|---|
| 무엇 | #70에서 마이그레이션을 적용했으므로, 그동안 막아두었던 **런타입 개인화 차단을 해제**(코드 flip) |
| 왜 이렇게 | 마이그레이션 미적용 상태에서 코드를 먼저 켜면 런타임 에러 → "DB 먼저, 코드 나중" 안전 순서 |
| 결과 | per-user 런타입 개인화 실제 동작 |

---

## 4. 15:03 `095181f` (#72) — app_settings 저장 치명버그 전면 수정

오전부터 P3a/P3b로 "말로 설정 저장"을 붙이는 과정에서, 신규 코치 코드가 저장 시 에러를 던지며 **코치챗이 정체**되는 현상이 드러났다. 추적해 보니 단순 신규 버그가 아니라 **앱 전반에 걸친 구조적 결함**이었다.

### 발생 이벤트(특이)

| 항목 | 내용 |
|---|---|
| 증상 | 코치챗에서 설정 저장 시 `no unique constraint matching ON CONFLICT specification` 에러 → 저장 실패·코치챗 정체 |
| 직접 원인 | `app_settings` 테이블의 실제 unique 제약은 **(user_id, key) 복합** 인데, 코드 전반이 존재하지 않는 **단독 제약 `onConflict:'key'`**(충돌 기준 컬럼을 key 단독으로 지정)를 가리킴 → upsert(있으면 수정·없으면 삽입)가 매칭할 제약을 못 찾음 |
| 숨어 있던 진짜 문제 | **기존 코드는 이 에러를 무시(silent)** 해 왔다. 즉 목표·루틴·메모·브리프캐시 등의 저장이 **그동안 조용히 실패해 왔을 수 있다**. 화면상으론 멀쩡해 보였지만 실제 DB에 안 들어간 케이스가 잠재 |
| 왜 이제 드러났나 | 신규 코치 코드는 에러를 **throw(표면화)** 했기 때문에 비로소 가시화됨 → "조용한 실패"가 "시끄러운 실패"로 바뀌며 발각 |
| 해결 | `lib/appSettings.ts`의 **`upsertAppSetting`** 헬퍼로 전면 치환. 이 헬퍼는 onConflict에 의존하지 않고 **본인 행 select → 있으면 update / 없으면 insert** 방식으로 안전하게 처리 |
| 결과 | 저장 실패·코치챗 정체 해소. 앱 전반의 잠재 저장 버그도 함께 봉합 |

> **교훈 (강조)** — **Codex는 코드와 라이브 DB 스키마의 불일치를 못 잡는다.** 코드만 보면 `onConflict:'key'`는 문법상 멀쩡하다. 실제 DB의 제약이 복합 unique라는 사실은 라이브 스키마를 봐야만 알 수 있다. 이후 app_settings 저장은 **반드시 `lib/appSettings`의 `upsertAppSetting`을 사용**한다. ([[project_app_settings_upsert_gotcha]])

---

## 5. 저녁 — 파일첨부 수동전송 · 질문 자동분류 · 원가분리 (19:56 ~ 22:48)

### 5-1. 19:56 `4476405` (#73) — 코치챗 파일 업로드 자동기록 제거

| 구분 | 내용 |
|---|---|
| 문제 | 파일을 첨부하는 순간 **자동으로 전송·기록**되어 사용자가 메시지를 다듬을 틈이 없었음 |
| 해결 | 자동기록 제거 → **첨부 후 "보내기"로 수동 전송** |
| 결과 | 첨부 + 설명 텍스트를 함께 다듬어 한 번에 보내는 자연스러운 UX |

### 5-2. 21:28 `9170006` (#74) — question_type 규칙기반 자동분류 (어드민 Phase1) + AI보내기 문구 정합화

| 구분 | 내용 |
|---|---|
| 무엇 | 코치챗 질문을 **규칙 기반으로 question_type 자동분류** |
| 왜 | 어드민-애널리틱스(운영자 전수분석) **Phase1**의 잔여 조각. RC2가 로깅 토대는 깔았고, question_type 분류가 남아 있었음 ([[project_admin_analytics_phase1]]) |
| 추가 | 'AI에게 보내기' 안내 문구 정합화 (문구가 실제 동작과 어긋났던 부분 정리, "30일" 류 문구 잔재 정합) ([[project_ai_handoff_30day_label_todo]]) |
| 결과 | 운영자 분석용 질문 유형 자동 태깅 |

### 5-3. 22:48 `e611801` (#75) — 도구턴 원가분리 + beta/vip 지급보정 + 운동종목 자동분류 (미결 3건)

| 구분 | 내용 |
|---|---|
| 도구턴 원가분리 | 도구 호출 턴은 **차감하지 않되 원가만 분리 로깅** → 무료여도 비용은 추적. "도구턴 무차감=과금모델" 권고에 부합 ([[project_coach_tool_turn_billing]]) |
| beta/vip 지급보정 | beta·vip 등급의 지급(할당) 보정 처리 |
| 운동종목 자동분류 | 운동종목을 자동 분류 |
| 성격 | 묶음 PR(미결 3건을 한 번에 닫음) |

---

## 6. 23:07 `4d19fb6` (#76) — 코치 대화·메모리 저장/기억 활성화

이 날의 피날레. 코치가 **대화와 메모리를 저장하고 다음 대화에서 기억**하게 켰다. 단, 프라이버시 민감 영역이라 **2겹 opt-in(동의해야 켜짐)** 구조다.

| 구성요소 | 내용 |
|---|---|
| 별도 동의 UX | 저장·기억은 일반 동의와 분리된 **별도 동의 화면**으로 명시적 opt-in |
| 저장 배선 | 대화·메모리를 실제로 저장하는 경로 연결 |
| 기억 주입 | 다음 대화 시 저장된 메모리를 컨텍스트로 **주입** |
| storageGate ON | 저장 기능 게이트(storageGate)를 ON |
| 2겹 opt-in | **게이트 AND 동의** 둘 다 충족해야 저장·기억이 동작 → 한쪽만으론 안 켜짐(보수적 안전장치) |

> 저장·기억은 데이터 민감도가 높아, 게이트(시스템 차원 스위치)와 사용자 동의(본인 의사)를 **둘 다** 요구하는 이중 잠금으로 설계했다.

---

## 7. 이 날의 결과 정리

| 줄기 | 닫은 것 |
|---|---|
| 코치 에이전트 잔여 4종 | 복수기록 공개(#61) · 차감 RPC 비침묵화(#62) · 대시보드/기록 공개(#63) · 브리프탭 실갱신(#64) |
| 경제성 보호 | GPT 자동폴백 차단 + 결제에러 비노출(#65) |
| 설정 UI 정리 | 6목록 접힘(#66) · 토글 가시성(#67) · 코칭AI 설정 통합 카드(#68) |
| 말로 설정 CRUD | P3a 신발·운동종목(#69) · P3b 개인메모 + per-user 하이브리드 마이그레이션(#70) · 코드 flip(#71) |
| **치명버그 봉합** | **app_settings onConflict 전면수정(#72)** — 앱 전반 잠재 저장버그 해소 |
| 코치챗 마감 처리 | 파일첨부 수동전송(#73) · 질문 자동분류(#74) · 원가분리·지급보정·운동분류(#75) |
| 저장/기억 | 대화·메모리 저장/기억 활성화 + 2겹 opt-in(#76) |

---

## 관련 페이지

- [[project_app_settings_upsert_gotcha]] — app_settings 복합 unique → onConflict:'key' upsert 실패, upsertAppSetting 필수
- [[project_coach_tool_turn_billing]] — 코치챗 도구턴 무차감=과금모델, 원가만 로깅
- [[project_data_classification_lesson]] — 신규 RLS·데이터 분류 시 글로벌/사용자별 하이브리드 검토
- [[project_admin_analytics_phase1]] — 어드민-애널리틱스 Phase1, question_type 분류 잔여
- [[project_ai_handoff_30day_label_todo]] — 'AI에게 보내기' 문구 정합
- [[project_profile_publications]] — 프로필 공개 대시보드/기록 스냅샷
- 작업보고 #95 · #96 · #97 · #99 / 핸드오프 #32 · #33

---

## 작업 리드타임

| 항목 | 값 |
|---|---|
| 작업 시작 | 2026-06-09 09:32 (첫 커밋 #61) |
| 작업 완료 | 2026-06-09 23:07 (마지막 커밋 #76) |
| 경과 | 약 13시간 35분 (커밋 16건 / PR #61~#76) |
| 문서 복원 작성 | 2026-06-12 (갭 복원) |

---
