동호회 회비 운영을 스킬 트리로 쪼갠 기록

## 1. 한 줄 요약

농구동호회 모어의 회비 운영에서 제가 매달 반복하던 일을 **하나의 큰 자동화**가 아니라 **역할이 분리된 4개의 스킬**로 쪼갰고, 각 스킬의 입력·출력·승인 경계를 명시적으로 정했습니다. 이 4개를 묶을 정식 우산(umbrella) 스킬은 **아직 만들지 않았고, 다음 산출물로 남겨둔 상태**입니다.

---

## 2. 문제: 자동화하기 전에 제가 실제로 하던 일

동호회 총무 일은 겉보기엔 단순한데, 실제로는 성격이 전혀 다른 작업 네 덩어리가 매달 섞여 돌아갑니다.

**(1) 대회 영상을 찾는 일**
연도별 참가 대회와 경기 영상이 여러 YouTube 채널에 흩어져 있습니다. 팀명 표기도 `모어`, `서울 모어`, `More`, `MORE`, `Moore`로 제각각이라 한 번의 검색으로는 잡히지 않습니다. KBA D3는 여러 팀이 들어간 날짜별 통합 라이브라서, 이걸 모어 단독 경기 영상으로 착각하면 이후 계산이 전부 틀어집니다.

**(2) 찾은 영상을 회비웹에 넣는 일**
기존 영상 분석 화면은 경기마다 `URL / 우리팀 유니폼색 / 시작 / 끝`을 한 행씩 입력해야 했습니다. 대회 하나에 경기가 여러 개면 그만큼 반복입니다.

**(3) 통장 입금과 회비 기록을 맞춰보는 일**
입금은 계좌에 찍히고, 회비 반영은 웹앱에 있습니다. 이 둘이 어긋난 건은 눈으로 훑어서 찾고 있었습니다.

**(4) 미납 공지를 만드는 일**
누가 얼마 밀렸는지 정리하고, 그걸 사람이 읽을 문장과 이미지로 다시 만드는 일입니다.

처음엔 이걸 "회비 자동화 스킬" 하나로 만들려고 했습니다. 결과적으로 그게 이번 주의 실패이자 설계의 출발점이었습니다.

---

## 3. 왜 스킬 하나로는 안 되었나

한 스킬에 다 넣으려고 하니 세 가지가 계속 걸렸습니다.

**첫째, 데이터 성격이 다릅니다.**
대회 참가 이력·영상 아카이브는 "비용이 아직 없어도 쌓아둘 가치가 있는 기록"입니다. 반면 대회비 정산은 "확정되면 돈이 움직이는 기록"입니다. 같은 테이블, 같은 스킬에 억지로 넣으면 아직 정산할 일이 없는 경기 영상을 보관할 곳이 없어집니다.

**둘째, 위험도가 다릅니다.**
영상 후보를 잘못 찾으면 제가 검수하다 걸러집니다. 하지만 입금 대조를 잘못하면 회원에게 잘못된 금액이 나갑니다. 같은 승인 규칙을 쓸 수 없습니다.

**셋째, 트리거가 다릅니다.**
영상 아카이브는 대회가 끝난 뒤 비정기로, 입금 대조와 미납 공지는 월 단위로 돕니다. 하나로 묶으면 매달 쓰지도 않을 영상 검색 규칙까지 컨텍스트에 딸려 들어옵니다.

그래서 **"한 스킬 = 한 책임 = 한 승인 경계"** 를 기준으로 쪼갰습니다.

---

## 4. 스킬 트리

```text
[제안] more-club-finance-operations          ← 우산 스킬. 아직 만들지 않음
│
├── more-tournament-video-archive            ← 대회 영상 조사·검수 아카이브
│
├── more-fee-automation                      ← 영상 기반 대회비 안분 "준비"
│
├── more-payment-reconciliation              ← 입금 ↔ 회비 기록 대조 (읽기 전용)
│
└── more-unpaid-notice                       ← 미납 요약 + 공지 초안(텍스트/이미지)

(트리 밖 · 주변 도구)
    youtube-summary-docx                     ← 자막 기반 영상 요약 문서화.
                                                회비 계통 스킬이 아님
```

최상단 `more-club-finance-operations`는 **제안 상태**입니다. 현재 저는 4개 스킬을 상황에 따라 직접 골라 부르고 있고, 이걸 자동으로 라우팅해주는 정식 스킬 파일은 존재하지 않습니다. 트리 그림에 넣은 건 목표 구조를 보여주기 위해서지, 이미 있다는 뜻이 아닙니다.

`youtube-summary-docx`는 자막이 있는 YouTube 영상을 5줄 요약·기억할 문장·활용 포인트 형태의 Word 문서로 만드는 도구입니다. 이름에 YouTube가 들어가서 영상 계통으로 오해하기 쉬운데, **경기 영상의 출전시간이나 대회비 안분에는 쓸 수 없습니다.** 자막이 없으면 내용을 지어내지 않고 그대로 멈춥니다. 그래서 회비 트리의 자식이 아니라 옆에 놓인 별개 도구로 정리했습니다.

---

## 5. 각 스킬의 역할 / 입력 / 출력 / 트리거

### 5-1. `more-tournament-video-archive`

| 항목 | 내용 |
|---|---|
| 역할 | 특정 연도에 모어가 참가한 대회와 경기 영상 후보를 찾아 **검수용 아카이브**로 정리 |
| 입력 | 연도, 팀 별칭 목록(`모어`, `서울 모어`, `More`, `MORE`, `Moore`) |
| 출력 | `대회명 \| 날짜 \| 상대팀 \| 우리팀 유니폼색 \| URL 주소` 표와 CSV 초안, 각 행의 근거 |
| 트리거 | 대회가 끝난 뒤, 또는 특정 연도 영상 이력을 채워야 할 때 (비정기) |

정한 규칙:

- 기본 출처는 농구연구소 B-LAB, 동아리농구방 BDR, KBA Live이며 부족하면 YouTube 검색으로 보완합니다.
- 유니폼색은 영상이나 신뢰할 수 있는 설명에서 확인되지 않으면 추측하지 않고 `미확인`으로 씁니다.
- KBA D3는 날짜별 통합 라이브 URL과 **모어 경기 시작·끝 시각을 따로** 관리합니다.
- 농구연구소의 `디비전3`은 KBA D3와 다른 대회로 취급합니다. 이름이 비슷해서 실제로 헷갈렸던 부분입니다.
- 결과물은 기본적으로 검수용 표·CSV 초안이고, 명시적 승인 없이 회비웹 DB에 저장하지 않습니다.

### 5-2. `more-fee-automation`

| 항목 | 내용 |
|---|---|
| 역할 | 영상 분석 결과를 바탕으로 대회비 안분안과 월 회비 공지 초안을 **준비**. 확정은 하지 않음 |
| 입력 | 총비용, 지원금, 경기별 회원 출전시간, 제외·면제 대상 |
| 출력 | 회원별 청구액 초안, 검수 대상 목록, `clubTopUp` 금액 |
| 트리거 | 대회 영상 분석이 끝나고 대회비를 나눠야 할 때 |

계산 규칙:

- 실부담 = 총비용 − 지원금
- 여러 경기의 출전시간은 회원별로 합산합니다.
- 개인 청구액은 천 원 단위 버림입니다.
- 버림 차액과 면제·제외로 생긴 차액은 `clubTopUp`(회비 부담)으로 흡수합니다. 즉 **개인 청구액 합계 + clubTopUp = 실부담**이 항상 성립해야 합니다.
- 등번호 매핑 실패, 낮은 분석 신뢰도, 출전 0초, 제외·면제는 자동 처리하지 않고 검수 대상으로 분리합니다.

이 스킬의 이름에 automation이 들어가지만, 실제로 하는 일은 **계산과 분류까지**입니다. DB 반영, 공지 전송, 로그인은 제가 명시적으로 승인하기 전에는 실행되지 않습니다.

### 5-3. `more-payment-reconciliation`

| 항목 | 내용 |
|---|---|
| 역할 | 통장 입금 내역과 회비 기록을 대조해 **어긋난 건을 찾아냄**. 읽기 전용 |
| 입력 | 기간(연도), 입금 내역, 회비 기록 |
| 출력 | 미반영 후보·부족 후보 목록과 각 건의 판단 근거 |
| 트리거 | 월 마감, 또는 연 단위 점검 |

이 스킬은 **쓰기 동작을 아예 갖고 있지 않습니다.** 승인하면 쓰겠다는 뜻이 아니라, 이 스킬로는 DB를 못 고칩니다. 대조 결과는 목록으로만 나오고, 실제 수정은 제가 회비웹 화면에서 직접 합니다. 회계 대조에서 "찾는 것"과 "고치는 것"을 같은 도구에 넣으면 잘못 찾은 걸 그대로 반영해버릴 위험이 있어서 그렇게 했습니다.

### 5-4. `more-unpaid-notice`

| 항목 | 내용 |
|---|---|
| 역할 | 미납 현황을 요약하고, 공지용 텍스트와 이미지 **초안**을 만듦 |
| 입력 | 기준 시점, 미납 집계 결과 |
| 출력 | 미납 요약, 공지 문안 초안, 공지 이미지 초안 |
| 트리거 | 월 공지 시점 |

공지는 **초안까지만** 만듭니다. 어디에도 자동으로 보내지 않습니다. 제가 내용을 읽고, 필요하면 고치고, 직접 복사해서 올립니다.

---

## 6. 실행 순서

두 갈래가 따로 돕니다. 하나로 합치지 않은 게 이번 설계의 핵심입니다.

**갈래 A — 대회비 (비정기)**

```text
1. more-tournament-video-archive
   YouTube 채널·검색 → 대회별 영상 후보 + 근거 수집
        ↓
2. 사람 검수
   대회 구분, 유니폼색, KBA D3 통합 라이브 구간 확인
        ↓
3. 회비웹: 영상 목록 일괄 붙여넣기 (TSV)
   대회 하나를 열고 여러 경기 행을 한 번에 생성
        ↓
4. 회비웹: 영상 분석
        ↓
5. 사람 검수
   등번호 매핑, 출전시간, 신뢰도 확인
        ↓
6. more-fee-automation
   실부담 계산 → 회원별 청구액 초안 + clubTopUp + 검수 대상 분리
        ↓
7. 사람 승인 → 회비웹에서 대회비 반영
```

**갈래 B — 월 회비 (월 단위)**

```text
1. more-payment-reconciliation
   입금 내역 ↔ 회비 기록 대조 (읽기 전용) → 미반영·부족 후보 목록
        ↓
2. 사람 검수 및 수정
   회비웹 화면에서 직접 반영
        ↓
3. more-unpaid-notice
   미납 요약 + 공지 텍스트·이미지 초안
        ↓
4. 사람이 읽고 수정 후 직접 게시
```

두 갈래 모두 **마지막 단계는 사람**입니다. 예외 없습니다.

---

## 7. 구현한 것: TSV 일괄 붙여넣기

갈래 A의 3단계는 스킬이 아니라 웹앱 기능으로 만들었습니다. 이유가 있습니다.

처음엔 아카이브 스킬이 링크 목록을 뱉어주면 될 거라고 생각했습니다. 그런데 막상 써보니 제가 URL을 하나씩 드래그해서 복사하고, 유니폼색을 다시 입력하고 있었습니다. 링크 목록은 **읽기에는 좋지만 입력에는 최악의 형식**이었습니다.

그래서 출력 형식을 웹 입력폼이 바로 먹을 수 있는 TSV로 바꾸고, 회비웹 쪽에 그걸 받는 입구를 만들었습니다. 관리자 대회비의 영상 분석 패널에 `목록 붙여넣기` UI를 추가했고, 대회 하나를 열어 아래 블록을 통째로 붙여넣습니다.

```tsv
URL	우리팀 유니폼색	시작	끝
https://www.youtube.com/watch?v=sUGRhJRhrcA	남색		
https://www.youtube.com/watch?v=nyLI70LE8Xg	흰색		
https://www.youtube.com/watch?v=6rirVPGI41Q	흰색		
```

동작:

- 한 줄당 한 경기 행을 만듭니다.
- 헤더와 빈 줄은 무시합니다.
- YouTube URL 형식과 `H:MM:SS` 또는 `M:SS` 시간 형식을 검사합니다.
- 이미 입력된 URL, 그리고 붙여넣기 안에서 중복된 URL은 추가하지 않습니다.
- 시작·끝이 비어 있으면 기존 흐름대로 영상 전체 분석을 뜻합니다.
- **붙여넣기만으로는 저장도, 분석 실행도, 대회비 반영도 하지 않습니다.**

관련 코드:

- `app/(dashboard)/admin/fee-events/video-analysis-panel.tsx`
- `lib/video-bulk-import.ts`
- `lib/video-bulk-import.test.ts`

---

## 8. Code as Gate / Eval — 모델이 판단해도, 통과는 코드가 정한다

스킬을 쪼개면서 같이 정한 규칙입니다. 모델 출력이 다음 단계로 넘어가려면 **코드가 검사해서 통과시켜야 합니다.** 프롬프트에 "정확하게 해줘"라고 쓰는 건 게이트가 아닙니다.

### Gate (통과 조건 — 코드가 판정)

| 지점 | 게이트 | 실패 시 |
|---|---|---|
| TSV 붙여넣기 | YouTube URL 형식 검사 | 해당 행 버림 |
| TSV 붙여넣기 | `H:MM:SS` / `M:SS` 시간 형식 검사 | 시간값만 거부, 유효한 URL은 살림 |
| TSV 붙여넣기 | 기존 URL·붙여넣기 내 중복 검사 | 행 추가 안 함 |
| 대회비 안분 | 개인 청구액 합계 + `clubTopUp` = 실부담 | 안분안 자체가 성립 안 함 |
| 대회비 안분 | 매핑 실패·낮은 신뢰도·출전 0초·면제 | 자동 처리 금지, 검수 큐로 분리 |
| 입금 대조 | 쓰기 동작 미보유 | 애초에 DB 반영 불가 |
| 유니폼색 | 근거 없으면 `미확인` | 추측값 기입 금지 |

시간 형식 게이트는 특히 신경 썼습니다. 잘못된 시간값 하나 때문에 멀쩡한 URL까지 통째로 버려지면, 사람이 뭐가 빠졌는지 모릅니다. 그래서 **시간값만 거부하고 URL은 살리도록** 설계했고, 그 동작을 테스트로 고정했습니다.

### Eval (반복 검증)

- `lib/video-bulk-import.test.ts` — `parseBulkVideos`에 대해 3개 케이스를 고정했습니다.
  1. 탭 구분 `URL / 유니폼색 / 시작 / 끝` 파싱
  2. 헤더·빈 줄·잘못된 URL·이미 존재하는 URL 건너뛰기
  3. 잘못된 시간값을 거부하되 유효한 URL은 버리지 않기
- 2026-07-25 기준 전체 Vitest: **5개 파일, 72개 테스트 통과**
- 2026-07-25 기준 `next build` **성공** (프로덕션 빌드)
- 전체 ESLint는 기존 파일들의 오류로 실패했습니다. 다만 새로 추가한 일괄 붙여넣기 파일들은 그 오류 목록에 없었습니다. 이건 통과가 아니라 **아직 정리 안 된 부채**로 적어둡니다.

### 운영 실측 (2026년 대조)

2026년 입금 대조를 `more-payment-reconciliation`으로 한 번 돌렸을 때:

- 읽은 입금 건수: **66건**
- 눈에 보이는 미반영·부족 후보로 식별된 건수: **14건**

이 66/14는 스킬이 실제 데이터에서 돌아갔다는 기록이자, 앞으로 회귀 여부를 볼 기준선입니다. 다만 14건은 **후보**입니다. 확정된 오류 건수가 아니고, 각 건은 제가 회비웹에서 직접 확인해야 합니다. 그리고 스킬은 이 중 어느 것도 자동으로 고치지 않았습니다.

> 이 문서에는 회원 이름, 금액, 은행 정보, 개별 회비 내역을 일절 싣지 않습니다. 건수만으로도 스킬이 돌았다는 증거는 충분합니다.

---

## 9. 승인 경계

명시적 승인 없이는 절대 실행하지 않는 것들입니다.

- 회비웹 DB 반영 (대회비 확정, 회비 기록 수정)
- 공지 전송·게시
- 로그인이 필요한 동작
- 아카이브 결과의 DB 저장

스킬이 만드는 건 전부 **초안**입니다. 표, CSV, 계산안, 대조 목록, 공지 문안. 이걸 실제 상태로 바꾸는 동작은 제가 화면에서 직접 합니다.

이렇게 나눈 이유는 단순합니다. 영상 분석이 등번호를 잘못 읽으면 누군가 안 낸 돈을 내게 되거나, 낸 돈을 또 내게 됩니다. 그건 되돌리는 비용이 자동화로 아낀 시간보다 훨씬 큽니다.

---

## 10. 아직 만들지 않은 것 (솔직하게)

사례글에서 이 부분을 빼면 나머지가 다 과장으로 읽힙니다.

**만들지 않은 것:**

- **정식 우산 스킬 `more-club-finance-operations`** — 없습니다. 지금은 제가 상황 보고 4개 중 하나를 직접 부릅니다.
- **대회·경기영상 아카이브 전용 화면** — 대회 참가 이력과 URL을 대회비 이벤트와 분리해 보관할 화면이 없습니다. 지금은 비용이 없는 경기 영상을 쌓아둘 곳이 마땅치 않습니다.
- **아카이브 → 분석 화면 자동 전달** — 아카이브에서 고른 경기를 TSV로 자동 생성하거나 분석 화면으로 바로 넘기는 연결이 없습니다. TSV는 아직 제가 옮깁니다.
- **영상 분석 검수 보조** — 첫 경기 등번호 매핑을 다음 경기로 복사하기, 유니폼색별 매핑 템플릿, 낮은 신뢰도·미매핑·출전시간 급변만 강조하기, KBA D3 통합 라이브의 지정 구간만 분석하기. 전부 아이디어 단계입니다.
- **주기적 영상 후보 수집** — 정기적으로 새 후보만 모아 보고하는 흐름이 없습니다.

**하지 않은 주장:**

- 프로덕션 배포 완료라고 쓰지 않습니다. 로컬 프로덕션 빌드가 성공했다는 것과 배포됐다는 건 다른 얘기고, 배포 상태는 이 문서 기준으로 확인하지 않았습니다.
- 자동 청구, 자동 DB 반영은 하지 않습니다. 만들다 만 게 아니라 **일부러 안 만든** 것입니다.
- 영상 분석 결과가 정확하다고 말하지 않습니다. 그래서 검수 단계가 있습니다.
- 메신저 연동은 검토도 구현도 계획도 없습니다. 공지는 제가 복사해서 올립니다.

---

## 11. 이번 주에 배운 것

**하나의 큰 스킬보다 경계가 분명한 작은 스킬 4개가 낫습니다.** 쪼개니까 각 스킬이 "무엇을 받아서 무엇을 내놓는지"를 한 문장으로 쓸 수 있게 됐고, 그게 안 써지는 스킬은 아직 설계가 덜 된 스킬이라는 신호였습니다.

**출력 형식이 사람의 다음 동작을 결정합니다.** 같은 정보라도 링크 목록으로 주면 사람이 드래그하고, TSV로 주면 붙여넣습니다. 스킬 품질을 올리는 것보다 출력 형식을 바꾸는 게 병목을 더 크게 줄였습니다.

**게이트는 프롬프트가 아니라 코드로 써야 합니다.** "중복 URL은 넣지 마"라고 지시하는 것과, 중복이면 행을 만들지 않는 코드에 테스트를 붙여두는 건 다릅니다. 후자만 다음 달에도 똑같이 동작합니다.

**위험도가 다른 작업은 승인 경계도 달라야 합니다.** 영상 후보 조사와 입금 대조를 같은 규칙으로 다루면, 둘 중 하나는 과하게 막히거나 위험하게 열립니다.

---

## 12. 다음 단계

다음 산출물은 **정식 우산 스킬 `more-club-finance-operations`** 입니다.

지금 트리 최상단에 그려둔 그 노드를 실제 스킬 파일로 만드는 작업입니다. 상황을 보고 4개 자식 스킬 중 어느 것을 부를지 라우팅하고, 공통 승인 경계(DB 반영·공지 전송·로그인 금지)와 개인정보 취급 규칙을 한 곳에서 관리하는 게 목적입니다.
1
3개의 답글
밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.