# 작업 보고: task-3029 — 인슈위키 유튜브 카드 품질 개선

- **팀**: dev4-team (비슈누)
- **저장소**: `Jeon-Jonghyuk/InsuWiki` · base `5da890c` (origin/master)
- **PR**: **#12** — https://github.com/Jeon-Jonghyuk/InsuWiki/pull/12 (open, 머지는 ANU)
- **브랜치**: `task/task-3029-dev4` (원격 sha `78a4dcf`) · 워크트리 `/home/jay/.worktrees/task-3029-dev4`
- **레벨**: Lv.2 · 검증 레벨 normal

---

## S — 상황
유튜브 영상을 자동 요약해 인슈위키 wiki 카드로 게시하는 파이프라인이 카드 8건을 만들었다.

## C — 문제
회장 지적 3건: ① 채널·게시일 확인 불가 ② 전사 원문 첨부 확인 불가 ③ 요약 서두가 카드마다 제각각인 LLM 말투.

## Q — 핵심 질문
각각의 원인은 무엇이며, 어디를 고쳐야 회장 화면에서 해소되는가?

## A — 해결
- ①③ 은 **코드 결함**이었고 고쳤다(파이프라인 + 프론트).
- ② 는 **코드 결함이 아니었다. 미배포였다.** 고칠 코드는 이미 master 에 있고 프로덕션에만 없다.
- ★ 그래서 **이 PR 을 머지해도 ①②③ 모두 회장 화면에는 나타나지 않는다.** InsuWiki `nextapp` 의 **프로덕션 배포가 선행되어야 한다.** 이것이 이번 작업의 가장 중요한 발견이다.

### 왜 이렇게 됐나 (5 Whys)
- **1st Why** — 회장이 첨부를 못 여는 이유는? → 클릭 시 `/api/attachments/{fileId}` 가 **404** 를 준다.
- **2nd Why** — 왜 404 인가? → 그 라우트가 **프로덕션에 존재하지 않는다**(`fileId=short` 도 400 이 아닌 404).
- **3rd Why** — 왜 존재하지 않는가? → task-3001 의 `nextapp` 산출물이 **배포되지 않았다**(task-2870 의 7/28 라우트는 405 로 살아 있음).
- **4th Why** — 왜 반쪽만 반영됐나? → 파이프라인(`scripts/**`)은 로컬 systemd 가 직접 실행해 머지 즉시 살아나지만, `nextapp` 은 **별도 배포 단계**를 거친다. 같은 PR 의 두 산출물이 서로 다른 반영 경로를 갖는다.
- **5th Why** — 왜 아무도 몰랐나? → 배포 여부를 검증하는 게이트가 없다. 파이프라인이 정상 동작하니 "머지=반영"으로 보였다.

---

## ★ ANU 조치 요청 (최우선)

### 1. InsuWiki nextapp 프로덕션 배포가 막혀 있다 — 사람 개입 필요
프로덕션(`https://insuwiki.vercel.app`)이 **task-3001(`4911a99`, 2026-08-25 12:27 KST 머지)의 nextapp 산출물을 반영하지 않았다.**

| 프로브 | 프로덕션 응답 | 판정 |
|---|---|---|
| `/api/attachments/short` | **404** (HTML) | 라우트 있으면 `FILE_ID_PATTERN` 에 걸려 **400** 이어야 함 → **라우트 부재** |
| `/api/attachments/{실제fileId}` (인증 없음) | 404 | 〃 |
| `/api/attachments/{실제fileId}` (**실제 로그인 member ID 토큰**) | **404** | 인증 실패(401)가 아니라 **라우트 자체가 없음** |
| `/api/upload/session` (task-2870, 2026-07-28) | 405 | 라우트 **존재** |
| `/api/upload/chunk` (task-2870) | 405 | 라우트 **존재** |
| `/api/drive-image/abc` | 500 | 라우트 존재 (`/api` 라우팅 자체는 정상) |

→ **배포본은 `d5859a9`(2026-07-28) 이후, `4911a99`(2026-08-25 12:27) 이전 상태.**

프로덕션 번들 원문으로도 교차 확인했다. `DocumentClient` 청크(`108x-~d-o_ctl.js`)에 `첨부 파일` 섹션과 `클립보드에`·`문서 로딩 에러` 문자열은 있으나, task-3001 이 추가한 `handleAttachmentOpen`·`첨부파일을 열 수 없습니다`·`api/attachments` 는 **0건**이다. 배포본의 첨부 렌더는 구버전 그대로다:

```js
(0,R.jsx)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer", ... ,children:e.name})
```

즉 회장이 첨부 파일명을 클릭하면 `/api/attachments/{fileId}` 로 **그냥 이동**하고, 그 라우트가 없으니 **404 페이지**를 본다.

- 배포 권한은 팀 범위 밖이다(승인 없는 프로덕션 배포 금지). **ANU/회장 판단 요청.**
- 원인이 Vercel 빌드 실패인지, 프로덕션 alias 고정인지, 배포 브랜치 설정인지는 **확인하지 못했다.** 봇 토큰이 `repos/.../deployments` 에 403 이라 배포 이력을 못 읽었다. 추측하지 않는다.

### 2. `main.py` 호출부 3줄 패치 (task-3028 과 조율 필요)
`save_wiki_document` 에 `channel_name`/`published_at`/`video_url` 키워드를 추가했으나, 호출부 `main.py:375` 는 **forbidden_paths** 라 손대지 않았다. 그래서 **폴백 조회 경로**(youtube_knowledge 에서 videoId 로 조회)를 함께 구현해 **오늘 그대로 동작**한다. task-3028 종료 후 아래를 적용하면 Firestore 왕복 1회가 사라진다:
```python
save_wiki_document(
    video_id=video_id, title=title, content=summary_text,
    attachments=attachments, db=db,
    channel_name=channel_name, published_at=published_at,   # ← 추가
)
```

### 3. 기존 카드 8건 백필 — **설계만 제안, 실행 안 함** (명세 지시대로)
8건 전부 `properties == {}` 다(실측). 삭제·재생성 없이 `set(merge=True)` 로 properties 만 채우는 백필이 가능하다 — `_lookup_video_metadata` 가 `youtube_knowledge` 에 videoId 로 이미 조회하므로 재사용하면 된다. **서두 말투는 재요약이 필요해 별개 판단 사안이다.** ANU 승인 대기.

### 4. `findAttachmentOwner` 전수 스캔 (수정 안 함)
`api/attachments/[fileId]/route.ts` 가 `documents` 컬렉션을 **매 요청 전수 순회**한다. 문서가 늘면 지연·비용이 선형 증가한다. 명세가 "특정된 원인만 고쳐라"라고 했고 이번 원인이 아니므로 **손대지 않았다.** 별도 task 권장.

---

## ① 채널명 · 게시일 · 원본 링크 — 구현 완료

**저장** (`firestore_writer.py`) — `properties` 가 `{}` 였던 자리에 채워 넣는다. 값 없는 키는 **넣지 않는다**(None 저장 시 프론트가 깨짐).
```python
properties = {
  "channelName": ...,          # youtube_knowledge.channelName
  "publishedAt": ...,          # 원자료 UTC ISO 그대로 보존
  "publishedDateKst": ...,     # KST 기준 YYYY-MM-DD
  "videoUrl": "https://www.youtube.com/watch?v={video_id}",
}
```
★ **KST 변환은 문자열 절단이 아니다.** `drive_uploader.py:140` 은 `published_at[:10]` 을 쓰는데, UTC 21:30 은 KST 로 **다음 날**이라 한국 시청자 기준 게시일을 하루 앞당긴다. 이 작업은 `datetime.fromisoformat` + `astimezone(KST)` 로 변환하고, 파싱 실패 시 **추측하지 않고 키를 생략**한다.

**화면** — 신규 컴포넌트 `nextapp/src/app/docs/[id]/YoutubeSourceMeta.tsx`, `DocumentClient.tsx` 제목 `<h1>` 바로 아래 결선(+5줄). 게시일에 **`(KST)` 표기**. 값이 하나도 없으면 **`null` 반환** → 기존 문서·기존 카드 8건에 빈 박스가 생기지 않는다.

## ② 전사 첨부 — 원인 특정 = **(b)**, 단 실패 모드가 명세 예상과 다르다

명세의 3택 중 **(b) "렌더되지만 클릭 시 `/api/attachments/{fileId}` 호출이 실패한다"** 가 맞다.
다만 **인증 실패(401)가 아니라 라우트 부재(404)** 다.

- **(a) 반증** — 프로덕션 번들에 첨부 섹션과 `<a href>` 렌더 코드가 **실재**한다(위 원문 인용).
- **(c) 반증** — Drive 파일은 **정상**이다. 서비스 계정으로 직접 조회: `trashed:false`, `size:2780`, 본문 다운로드 성공(`# ... — 전사 원문 / **채널**: 보험명의정닥터 / **게시일**: 2026-08-20`). 404 는 Next 라우팅 단계라 Drive 까지 **도달조차 못 한다**.

**판단 — 형태를 사람 업로드 쪽(`webViewLink`)에 맞추지 않고, 화면이 두 형태를 모두 처리하게 둔다** (master 가 이미 그렇게 돼 있고, 그게 옳다).
근거: `/api/attachments` 인증 프록시는 **로그인 회원만** 열람하게 막는다. `webViewLink` 로 되돌리면 링크가 새는 순간 무인증 유출이고, 서비스 계정 Drive 파일이라 일반 회원 계정에서 열린다는 보장도 없다. 아래 L1 에서 프록시 경로가 **실제로 200 으로 동작함을 실증**했으므로 되돌릴 이유가 없다.

**추가 결함 2건 발견·수정** (허용 경로 `nextapp/src/app/docs/**` 내):
- **`uploadedAt` 이 ISO 문자열이면 날짜가 안 나온다.** 렌더는 `file.uploadedAt.seconds` 를 보는데 파이프라인은 `isoformat()` **문자열**을 넣는다 → 유튜브 카드의 **모든 전사 첨부가 영구히 `Just now`** 로 표시됐다. 회장 표현 *"사람이 올린 것처럼"* 에 정면으로 어긋나 수정했다(`formatAttachmentDate`, 4형태 수렴, 파싱 불가는 기존대로 `Just now`).
- **`file.type` 무가드.** `type` 없는 첨부 1건이면 `TypeError` 가 첨부 행이 아니라 **DocumentClient 전체 렌더를 죽인다**(에러 바운더리 없음). `?.` 로 방어(동작 동일).

## ③ 요약 서두 말투 — 구현 + **실제 LLM 호출로 실증**

**기존 8건 전수 실측 — `## 1. 주제 한줄` 로 시작하는 카드는 0건이었다:**
```
# 보험 유튜브 영상 핵심 논지 재구성            (5v6VeX4fk90)
자막 분석 결과입니다.                          (R7s8xFuaxdw)
라이나 생명 종신생활연금 자막을 분석했습니다. 아래 결과물을 그대로 사용하시면 됩니다.  (SHHE6cQgIyw)
## 삼성화재 간병인 지원보험 — 설계사용 핵심 논지 재구성  (b5tdBCw4HQs)
8월 18일 정닥터 라이브 방송 자막을 분석해 4단 구성으로 재구성한 결과입니다.  (m4g1nJQRXR0)
**1. 주제 한줄**                               (mLIt1vM6340)
# 간병인 지원 특약 비교 및 변호사 현물급부 약관 해설 — 정다토 유튜브 분석  (pdL2yD6CVPQ)
# 유튜브 자막 핵심 논지 재구성                 (sQImjclGXko)
```
→ 명세는 3건을 예시로 들었으나 **8건 중 7건이 제각각**이고, 나머지 1건도 제목 형식이 다르다.

`YOUTUBE_SUMMARY_PROMPT` 에 `[출력 형식]` 조항 추가: 첫 글자 `## 1. 주제 한줄` 강제 · 서두 인사말/메타 발화 금지(실제 발생 문구를 그대로 인용) · 전체 문서 제목(`# `) 금지 · 4단 제목 4줄 글자 그대로 고정 · 마무리 인사 금지.

★ **회장 확정 규격 5개는 무변경**이며 테스트로 봉인했다: 1,500~2,000자 / 4단 구성 내용 / "자막에 없는 내용 절대 추가 금지" / "숫자·보장한도·기간·면책 축약·생략 금지" / 마지막 줄 고지문.

---

## 검증 (명세 7항목)

### 1. 실제 카드 생성 — **하지 않았다. 이유를 명시한다**
파이프라인 1회 실행은 **중단했다.** 채널 `ch_보험명의정닥터` 의 `lastCrawledAt` 이 **2026-04-11** 로 4개월 이상 밀려 있다(실측). 처리량을 1건으로 낮춰 돌리면 커서만 현재로 전진해 **그 사이 4개월치 영상이 영구히 건너뛰어진다**(기지 함정: 실패해도 커서 전진 → 영구 유실). 회복 불가능한 부작용이라 실행하지 않았다. **ANU 판단 요청.**

대신 아래 2·3·4 로 실측 대체했다.

### 2. 첨부 원인 특정 근거 → 위 ② 표 (프로덕션 6종 프로브 + 번들 원문 + Drive 직접 조회)

### 3. 첨부 열람 실증 — **성공** (기지 미실증 항목 해소)
로컬에 master 코드 서버를 띄우고 **실제 Firebase member ID 토큰**(서비스 계정으로 발급, 읽기 전용)으로 호출:
```
HTTP=200 type=text/plain bytes=2780
# 업계 유일 월 15,000원, 자녀 100세까지 수술비 2천만 원 만드는 법 ... — 전사 원문
- **채널**: 보험명의정닥터
- **영상 ID**: 5v6VeX4fk90
- **게시일**: 2026-08-20
```

### 4. 요약 서두 검사 — **실제 Claude Sonnet 1회 호출 결과 원문**
실제 Drive 전사(2,424자)를 새 프롬프트로 요약했다. **첫 200자 그대로:**
```
## 1. 주제 한줄
실손보험이 5세대로 개정되며 비급여 보장이 대폭 축소된 것은 성인만이 아니라 자녀에게도 그대로
적용되는 문제이므로, 그 공백을 부모가 자녀 명의로 15,000원짜리 단독 종수술비 상품으로 메워줘야
한다는 내용입니다.

## 2. 핵심 주장
영상은 먼저 "자녀에게 왜 굳이 15,000원짜리 종수술비를 만들어줘야 하는가"라는 질문을 던지고
```
- 인사말·메타 발화·문서 제목 **없음**. 첫 글자부터 본문.
- 헤딩 전수: `## 1. 주제 한줄` / `## 2. 핵심 주장` / `## 3. 수치 / 조건 / 예외` / `## 4. 실무 시사점` — `**1.**` 혼용 **0건**.
- 마지막 줄: `※ 이 요약은 자동 생성이며 약관 원문을 우선합니다` 유지.
- ⚠️ **관찰**: 길이 2,156자로 상한 2,000자 **초과**(`length_exceeded_max=True`). 계약상 실패가 아닌 경고이고 상한 초과는 기지의 선재 현상이나, **표본 1건**이므로 단정하지 않는다. 배포 후 추이 관찰 권장.

### 5. 기존 카드 8건 영향 없음
8건 전부 `properties == {}` · `attachments` 1건씩 · `visibility=public` 정상(실측). 신규 컴포넌트는 값이 없으면 `null` 을 반환한다 — 스크린샷 [B] 구획(붉은 점선)이 **비어 있음**으로 확인.

### 6. 봉인(변이) 테스트 — **5종 전부 no-op 아님을 증명 후 FAIL 확인 → 원복**
| 변이 | 결과 |
|---|---|
| A. `channelName` 저장 제거 | `KeyError: 'channelName'` · **5 failed** / 228 passed → 원복 후 233 passed |
| B. 프롬프트 서두 금지 조항 제거 | **7 failed** / 226 passed → 원복 후 233 passed |
| C. KST 변환을 `published_at[:10]` 절단으로 | **8 failed** / 225 passed (연말·월말 경계 포함) → 원복 후 233 passed |
| D. 채널명 렌더 비활성화 (`false &&`) | **2 failed** / 14 passed → 원복 후 16 passed |
| E. 첨부 ISO 분기 제거 (`return null`) | **6 failed** / 30 passed → 원복 후 36 passed |

### 7. 회귀 — **팀장이 직접 재측정**
| 항목 | base `5da890c` (clean worktree) | 작업 후 | 델타 |
|---|---|---|---|
| pytest | **157 passed / 0 failed** | **233 passed / 0 failed** | 실패 **+0**, 신규 76 |
| npm test | 26 failed / 1341 passed (1367) | **26 failed / 1393 passed (1419)** | 실패 **+0**, 신규 52 |
| `tsc --noEmit` | 91 errors | 91 errors | **0** |

★ npm 실패 26건은 **base 에서 직접 재현 확인**했다 — `/tmp/t3029_base` 클린 워크트리에서 해당 2파일 실행 결과 `26 failed | 18 passed (44)`, 동일 파일·동일 건수. 우리 변경분과 교집합 **0**.

---

---

## 수정 파일별 검증 상태

| 파일 | 변경 내용 | grep 검증 | 상태 |
|------|-----------|-----------|------|
| firestore_writer.py | properties 에 채널명/게시일/KST/원본링크 저장 | grep "publishedDateKst" OK | verified |
| firestore_writer.py | youtube_knowledge 폴백 조회(fail-open) | grep "_lookup_video_metadata" OK | verified |
| summarizer.py | 출력 형식 고정 조항 추가 | grep "출력 형식" OK | verified |
| types/firestore.ts | DocumentProperties 4필드 optional 확장 | grep "channelName" OK | verified |
| YoutubeSourceMeta.tsx | 채널/게시일 KST 표기/원본링크 노출 | grep "(KST)" OK | verified |
| DocumentClient.tsx | 제목 아래 출처 메타 결선 | grep "YoutubeSourceMeta" OK | verified |
| attachmentDisplay.ts | 첨부 uploadedAt ISO 문자열 표시 정정 | grep "formatAttachmentDate" OK | verified |
| attachmentDisplay.ts | file.type 무가드 방어 | grep "resolveAttachmentIcon" OK | verified |
| test_wiki_properties.py | properties/KST 경계 테스트 | grep "publishedDateKst" OK | verified |
| test_summary_prompt_format.py | 서두 금지 문구 봉인 | grep "아래는" OK | verified |
| youtubeSourceMeta.test.tsx | 노출/부재/부분값/KST 테스트 16건 | grep "youtube-source-meta" OK | verified |
| attachmentDisplay.test.ts | 첨부 날짜 4형태 테스트 36건 | grep "Just now" OK | verified |

planned 항목 **0건**.

## PR CI (`validate`) 판정 — 선재 실패

`validate` 는 이 저장소에서 상시 적색이다. **추측하지 않고 직전 PR(#10, task-3021) 로그와 직접 대조했다.**

| 항목 | PR #10 (base) | PR #12 (우리) |
|---|---|---|
| 총 TS 오류 | **547** | **549** |
| `TS2307` (모듈 미해결) | 384 | 386 |

**순증 6건처럼 보이나 4건은 같은 오류의 행번호 이동**이다(우리가 줄을 삽입해 `DocumentClient.tsx` 40→42, 425→427, 782→788, 783→789 로 밀림).

**진짜 순증은 2건뿐이고 둘 다 동일한 선재 유형이다:**
```
nextapp/src/app/docs/[id]/YoutubeSourceMeta.tsx(13,41): error TS2307: Cannot find module '@/types/firestore'
nextapp/src/app/docs/[id]/__tests__/youtubeSourceMeta.test.tsx(18,41): error TS2307: Cannot find module '@/types/firestore'
```
CI 의 tsc 가 `@/` 경로 별칭을 해석하지 못해 **이미 384건**이 같은 이유로 깨져 있다. 신규 파일이 그 집합에 합류했을 뿐, **새로운 오류 종류는 0** 이다. 로컬 `nextapp` tsc 는 별칭이 정상 해석되어 **91 → 91 (델타 0)** 이다.


## L1 스모크테스트
- **서버 재시작**: **성공** — 로컬 `next dev`(master 코드) 기동 후 API 실호출. 별도로 워크트리 코드 서버도 기동해 실제 CSS 확보.
- **API 응답 확인**:
  | 호출 | 로컬(master 코드) | 프로덕션 |
  |---|---|---|
  | `/api/attachments/short` | **400** `{"error":"Invalid fileId"}` | **404** (HTML) |
  | `/api/attachments/{실제}` 무인증 | **401** `{"error":"Unauthorized"}` | **404** |
  | `/api/attachments/{실제}` + 실제 member 토큰 | **200** `text/plain` **2,780 bytes** 전사 원문 | **404** |
- **스크린샷**: `memory/reports/assets/task-3029-card-meta.png`
  - ★ **성격 명시**: 프로덕션 로그인 화면이 아니다. **실제 `YoutubeSourceMeta` 컴포넌트를 React SSR 로 렌더**하고 **워크트리 dev 서버의 실제 Tailwind CSS** 를 입힌 것이다. 프로덕션 카드는 `properties` 가 비어 있고(①이 아직 미배포·미반영) 배포 자체가 막혀 있어 라이브 촬영이 불가능하다.
  - [A] `보험명의정닥터` · `게시일 2026-08-20 (KST)` · `원본 영상 보기` 링크 노출 확인
  - [B] `properties` 빈 상태 → 렌더 결과 **빈 문자열**(SSR 길이 0) → 빈 박스 없음

---

## 명세 전제 반증 3건
1. **"데이터도 있고 렌더 코드도 있는데 확인 불가"** → 렌더 코드는 **master 에만** 있다. 프로덕션엔 없다. (a)(b)(c) 삼분법은 *프로덕션에 코드가 있다*는 전제 위에 있었고, 그 전제가 틀렸다.
2. **"properties 를 쓰라"는 옳았으나 그것만으로는 값이 절대 안 채워진다.** 호출부 `main.py` 가 forbidden 이라 명시 파라미터가 영원히 `None` 이다 → 폴백 조회 경로를 함께 넣어야 오늘 동작한다.
3. **서두 실태는 명세 예시(3건)보다 넓다.** 8건 중 `## 1.` 로 시작하는 카드 **0건**.

## 자체 판단으로 범위를 넓힌 것 (근거)
- 첨부 `uploadedAt` / `file.type` 수정 2건. 둘 다 허용 경로 `nextapp/src/app/docs/**` 안이고, 전자는 회장 표현 *"사람이 올린 것처럼"* 에 직접 어긋나는 **실동작 결함**이다. 후자는 1글자 방어로 전체 페이지 크래시를 막는다.

---

## trip-wire 5종 (실측)
- Critical7 = **0**
- PII net-new = **0**
- 회귀 실패 = **0** (pytest 0→0, npm 26→26 전부 선재, base 재현 확인)
- forbidden_paths 침범 = **0** (`config.py`·`main.py`·`transcriber.py`·`drive_uploader.py`·`.github/workflows/**`·`functions/**` 전부 변경 0건)
- nonce = `task-3029` 일치

## 변경 파일 12개 (전부 allowed_resources 내)
```
scripts/youtube-pipeline/youtube_pipeline/firestore_writer.py   +148
scripts/youtube-pipeline/youtube_pipeline/summarizer.py         +33/-?
scripts/youtube-pipeline/tests/test_wiki_properties.py          +423 (신규)
scripts/youtube-pipeline/tests/test_summary_prompt_format.py    +166 (신규)
scripts/youtube-pipeline/tests/test_wiki_document.py            ±11
nextapp/src/types/firestore.ts                                  +12
nextapp/src/app/docs/[id]/YoutubeSourceMeta.tsx                 +105 (신규)
nextapp/src/app/docs/[id]/attachmentDisplay.ts                  +102 (신규)
nextapp/src/app/docs/[id]/DocumentClient.tsx                    +12/-11
nextapp/src/app/docs/[id]/__tests__/youtubeSourceMeta.test.tsx  +138 (신규)
nextapp/src/app/docs/[id]/__tests__/attachmentDisplay.test.ts   +167 (신규)
memory/reports/task-3029.md                                     보고서
```

## 비고
- **머지는 ANU 가 한다. 자동 머지 없음.**
- 브랜치 remote push 는 v3.6 하네스가 CLI 를 차단하므로 정본 도구(`worktree_manager`/`taskctl`)로 수행했다. 착수 즉시 드래프트 PR 을 여는 명세 지시는 이 차단 때문에 이행하지 못했고, 대신 **단계마다 로컬 커밋**(총 6커밋)으로 진척을 보존했다.
- PR Check `validate` 는 이 저장소에서 상시 적색이다. 변경 파일과 교집합 여부는 PR 생성 후 확인 대상.
- 검증에 사용한 임시 서버·산출물은 전부 정리했다. 워크트리 clean.

## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회


## 세션 통계
- 총 도구 호출: 0회

