# 7교시 바이브 코딩 · `tretry` 정답본 > **강사용 정답본** · 학생 저장소에는 수업 뒤에 올립니다. 학생용: [VIBE-REQ-TRETRY.md](VIBE-REQ-TRETRY.md) → [VIBE-Q-TRETRY.md](VIBE-Q-TRETRY.md). > 강사가 직접 만든 본: [tretry/](tretry/README.md) (테스트 17개, `uv tool install --editable .`). 아래 숫자는 2026-10-29 강사 Mac에서 76일차 데이터 API에 실제로 돌린 값입니다. 68일차부터 바이브 코딩은 CLI 에이전트로 다시 만들어 재지 않습니다 (요청문은 예시). ## 1. TODO 예 (요청의 말 → 할 일) | # | 요청의 말 | 할 일 (내 말) | 꼭 · 나중 | | --- | --- | --- | --- | | ① | 종료 코드 ≠ 0이면 다시 | `--tries N`까지 다시, 결과에 시도 수 | 꼭 | | ② | 점점 늘려 · 상한 · 한꺼번에 X | 지수 backoff · `--max-wait` · `--jitter`(줄이기만) | 꼭 | | ③ | 멈추면 끊고 다시 | 시도마다 `--timeout`, 끊긴 것도 다시 | 꼭 | | ④ | 대신 할 명령 · 알려 주기 | `--fallback`, "by the fallback" · 기록에 `fallback` | 꼭 | | ⑤ | 고칠 수 없는 실패는 바로 멈춤 · 잠깐 뒤에 | 4xx(408 · 425 · 429 빼고) 1번 · 종료 코드 3 · 대체 안 함, `Retry-After` | 꼭 | | ⑥ | 두 번 만들어지면 안 됨 | 실행에 한 번 만든 키를 모든 시도에 (헤더 · 본문) | 꼭 | | ⑦ | 몇 초 안에 | `--deadline`, 끝낼 수 없는 시도는 시작 안 함 | 나중 | | ⑧ | 시도마다 파일에 · 키 · 토큰 X | JSON Lines, `***` | 꼭 | | ⑨ | 어느 대상이 자주 실패 | `stats`: 성공률 · 실행당 시도 · p95 | 나중 | | ⑩ | 계속 실패한 대상은 바로 대체 | 회로 차단 (최근 60초 5번) | 나중 | | ⑪ | 화면 | `tretry log` 두 표 · 실패만 | 꼭 | | ⑫ | 종료 코드 · 다른 프로그램 | 0 · 1 · 2 · 3, `--json` | 꼭 | | 끝 | 시험에서 기다리지 않기 | sleep · clock · 무작위를 인자로, 가짜 서버 | 꼭 | ## 2. 요청문 (처음 한 번 + 다음 단계 한 줄) 단계마다 긴 요청문을 쓰지 않습니다. 할 일은 이미 `Doc/SPEC.md` · `Doc/IMP-PLAN.md`에 있으니, 에이전트가 그 파일을 읽게 하면 됩니다. ### 처음 한 번 ```text Doc/SPEC.md 와 Doc/IMP-PLAN.md 를 읽고 체크 안 된 첫 단계만 만들어 줘. uv 프로젝트, 명령 이름 tretry, 코드와 화면 글자는 영어. 끝나면 IMP-PLAN 체크를 표시하고 확인 명령을 알려 줘. 다음 단계는 내가 말할 때까지 하지 마. ``` ### 그다음 단계마다 (같은 한 줄) ```text 확인했고 commit 했어. 다음 단계. ``` - 그냥 "계속"만 치면 단계 두세 개를 한꺼번에 하거나 체크 · 확인 명령을 빼먹기 쉽습니다. 이 한 줄은 처음 요청의 약속(한 단계만 · 체크 · 확인 명령 · 멈춤)을 이어 가게 합니다. - 대화가 길어지면 새 세션에서 **처음 요청**을 다시 보냅니다. "체크 안 된 첫 단계"부터 이어집니다. - 에이전트가 단계 내용을 잘못 알았다면, 요청문을 길게 쓰기보다 **SPEC · IMP-PLAN을 고치고** 다시 시킵니다. ### 틀렸을 때만 · 고쳐 달라는 요청문 예 무엇이 틀렸나(보인 것) · SPEC의 어느 요구인가 · 어디만 고치나 · 무엇으로 확인하나, 넷을 적습니다. ```text tretry http POST 가 400 을 받아도 3번 다시 하고 대체 명령까지 돌려. SPEC R5 대로 408 · 425 · 429 를 뺀 4xx 는 1번에 멈추고 종료 코드 3, 대체도 안 해야 해. runners.http 의 retryable 판단만 고치고, 가짜 서버 400 → 시도 1 · 종료 코드 3 · 대체 출력 없음 테스트를 넣어 줘. 다른 파일은 건드리지 마. ``` ## 3. 확인 기준과 강사 실측 (10/29, 76일차 데이터 API) | 요구 | 기준 | 실측 | | --- | --- | --- | | R1 · R2 | `--tries 3 --backoff 0.2 -- false` | 시도 3 · 기다림 0.20 → 0.40 · 0.65초 · 종료 코드 1 | | R3 | `--tries 1 --timeout 0.3 -- sleep 5` | `timeout 0.3s` · 0.31초 · 1 | | R4 | 대체 | (회로 차단 예에서) `queued-locally` · `ok by the fallback` · 0 | | R5 | `flaky` · 400 | `flaky`: #1 HTTP 500 → #2 0.33초 성공 (`T-2001`). 본문 `{}`: HTTP 400 `key and title are required` · 1번 · `(not-retryable)` · **종료 코드 3**, `--fallback`을 줬어도 대체 안 함 | | R6 | 같은 키로 다시 | `{"ticket_id": "T-2001", "duplicate": true}` (1번, 0.01초) | | R7 | `slow` · `--timeout 3 --deadline 5` | #1 `timeout 3.0s` → 0.5초 기다림 → #2 `timeout 1.5s` (남은 시간으로 줄임) · **5.00초**에 `(deadline)` · 1. Lab 01 예제 3의 12.4초와 비교 | | R8 | 기록 | 14줄, 키 없음 | | R9 | `tretry stats` | `POST http://localhost:8800/tickets` 6 runs · 50% · 1.67 tries/run · failed 7 · fallback 1 · p95 5.00 | | R10 | `error`에서 실패가 이어진 뒤 | 앞선 실패(400 · 마감 2 · error 3)로 최근 5개가 모두 실패 → 다음 실행부터 `0 tries · breaker open`, 대체가 있으면 바로 대체 | | R12 | `tretry` (인자 없음) | `put the command after --` · 종료 코드 2 | | R13 | `uv run pytest -q` | 17 passed in 3.3s | | R14 | `/tmp`에서 `tretry --help` | `usage: tretry [options] -- ` | - R10에서 볼 것: 차단은 **대상**(`METHOD URL`) 단위라 본문이 다른 요청(400을 받은 것 포함)도 같은 대상으로 셉니다. 실무에서는 대상을 더 잘게(경로 · 키) 나누거나 4xx를 세지 않기도 합니다. 8교시에 "무엇을 같은 대상으로 볼까"를 질문으로 씁니다. ## 4. 7교시에 볼 것 | 볼 것 | 막혔을 때 한마디 | | --- | --- | | 400도 다시 함 | "고칠 수 없는 실패는 1번, 종료 코드 3, 대체도 하지 않기 (오늘 Lab 01)" | | 테스트가 몇 초씩 | "엔진이 sleep · clock을 인자로" | | 간격이 늘 같음 | "지수 backoff와 상한" | | 다시 보낸 POST로 접수 건이 둘 | "키는 실행에 한 번, 본문 key에도" | | `--timeout`이 HTTP에 안 먹음 | "urlopen(timeout=...)" | | `--` 뒤 옵션을 tretry가 먹음 | "`--` 앞만 tretry 옵션" | | 기록에 토큰 | "헤더 값은 남기지 않기, URL · 오류도 지우기" | | 대체가 늘 성공해 차단이 안 풀림 | "대체의 성공은 대상의 성공이 아님" |