`/challenger`의 하루: 수정 전에 4개의 버그, 4개의 가설이 반증됨

발행: (2026년 5월 29일 AM 10:15 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

Source:

하루가 하나라고 생각했던 아침

2026년 5월 16일 토요일 – 오전 08:10
프랑수아즈의 머그잔에 담긴 미지근한 커피 — 사무실 생일 선물로 물려받은 것. 밤새 Sentry 보드에 3시 am 크론에 빨간 점 하나가 고립돼 보이고, 캐서린에게서 오전 7시 50분에 도착한 음성메시지:

“음, 버그가 있네. 하지만 금방 고쳤어.”

그녀 특유의 어조로 된 일곱 마디, 급하지도 않으며 걱정도 아니다. 다른 일보다 먼저 닫아야 할 티켓으로 시작되는 하루의 당연함.

캐서린은 문장의 앞부분에서는 거의 틀리지 않으며, 뒤부분에서는 가끔 틀린다. 오늘의 “버그”는 버그가 아니라 네 개다. 오전 8시와 오후 6시 사이에 일어나는 사건들은 멀리서 보면 하나의 일처럼 보이지만, 가까이서 보면 네 가지 전혀 다른 일처럼 보인다.

우리는 보통 사건을 티켓, 메시지, 세션 등 증상으로 셈한다. 이를 반증 가능한 가설로 셈한다는 것은 “하나만 있었다고 생각했는데 네 개가 있다”는 것을 찾는 것이다.

1️⃣ 08:00 am – 출석 PDF에서 중복 서명

캐서린은 지점의 한 강좌가 PDF에서 각 학생이 두 번 서명된 것으로 보인다고 보고한다.

생산적인 반사: “폼이 더블‑클릭 시 두 번 전송돼. 고유 제약을 추가하고 넘어가자.”
결과: 마이그레이션 15줄 – 오류.

/challenger skill

가설 (한 문장):

emargements ↔ seances 조인이 강좌에 같은 날짜에 여러 세션이 생성될 때 N × M 행을 반환한다.”

세 가지 탐색 (확증이 아니라 반증을 목표로):

  1. 이 날짜에 해당 강좌가 몇 개의 세션을 가지고 있나요?

    SELECT cours_id,
           date_seance,
           COUNT(*) AS n_seances
    FROM   seances
    WHERE  date_seance = '2026-05-16'
    GROUP BY cours_id, date_seance
    HAVING COUNT(*) > 1;

    결과: 영향을 받은 강좌 4개, 중복된 세션 18개 (학년도 필터가 잘못 연결됨).

출력은 더블‑클릭 가설을 반증하고 더 깊은 원인을 확인한다: getAnneeScolaire(date) 필터가 2025‑2026 학년도와 2026‑2027 학년도 사이에서 새 학년 초에 잘못 연결돼 세션 생성기가 두 번 실행된 것이다.

소요 시간: 탐색 ≈ 90 초.
피해 회피: ≈ 30 분 (커밋 → 배포 → Sentry → 캐서린 → 콜백).
비율: 6 : 1.

2️⃣ 11:00 am – 강사 교체 후 교육 노트 사라짐

한 강사가 출석 양식 끝에 입력한 교육 노트가 다음 세션에서 다른 강사가 교체될 때 다시 나타나지 않는다고 보고한다.

표면 가설 (유혹적인): “폼이 이전 출석의 노트를 미리 로드하지 않는다.” → useEffect 수정.

/challenger hypothesis (재구성)

notes_formateur 필드는 강좌나 세션이 아니라 서명한 사용자의 식별자에 바인딩돼 있어, 다른 서명자가 교체될 경우 정규 강사가 입력한 노트가 정당하게 가려진다.”

세 가지 탐색:

a. 스키마를 읽는다.
b. 컴포넌트를 읽는다.
c. 직접 강사에게 물어본다 (기술적인 탐색만으로는 버그인지 비즈니스 선택인지 판단할 수 없다).

/ask‑3‑options‑before‑code (doctrine v0.7)

코드를 한 줄 쓰기 전에 비기술적인 옵션 세 가지를 제시해 중재한다:

  1. 노트는 세션당이며 모든 후속 서명자에게 보인다.
  2. 노트는 서명자에게만 비공개이며 절대 공유되지 않는다.
  3. 노트는 강좌의 교육 팀 내에서 공유되지만, 가끔 교체되는 사람에게는 숨겨진다.

결정: 강사는 옵션 2를 선택한다.

결과: 버그가 아니라 문서화된 설계 선택(소규모 ADR)이다.
성과: 코드 0줄, 토론 2분.

Note – 서두른 단독 개발자는 옵션 1을 구현했을 경우, 다음 주에 세 명의 사용자 불만(강사의 노트가 전체 지점에 노출)으로 이어졌을 것이다.

3️⃣ 02:00 pm – 플래닝 에디터에 나타난 예상치 못한 수직선

플래닝 에디터가 한 열에 얇은 검은 수직선을 표시한다. 이 선은 그리드 위에서 아래까지 가로지른다.

눈으로 보는 가설: “CSS 누수.”

/challenger hypothesis

“고아 이벤트가 빈 행을 만들어 내고, 이 행이 두꺼운 테두리처럼 렌더링돼서 수직선이 나타난다.”

(이하 내용은 이어집니다…)

se a course deletion didn’t cascade‑delete its sessions.”

Three probes:

  1. Inspect the DOM.
  2. Run a consistency query on orphan seances.
  3. git blame on the component.

Result: The third probe wins. A recent commit added a conditional to visually separate branches, branching on an isLastOfAtelier variable mis‑calculated at the table edge. It’s an interface border, not a database ghost.

Fix: One TypeScript line, no migration, ≈ 2 min.

Had I started by searching the database, I would have wasted an hour inspecting courses and sessions that had nothing to reproach.

4️⃣ 오후 6시 – 비상용 종이 출석표에 잘못된 요일이 인쇄됨

An emergency paper attendance sheet prints “Thursday 2 pm” whereas the course is “Wednesday 2 pm.”

Self‑diagnosis: “The PDF prints the session date, not the course date.” → commit without a probe.

Rollback (25 min later): All sheets now say “Wednesday,” including Thursday courses. Sentry raises three alerts in five minutes. Catherine, on her end, didn’t call back; she wrote “hum, it bugs” and left me alone with the fatigue.

Real cause (detectable with a 90‑second probe)

creneau_label is stored in LSC Cache without a refresher, diverging for three weeks for courses whose jour_semaine was corrected without propagating the label.

Lesson: Even under fatigue, a quick probe can prevent costly rollbacks.

요약

시간이슈/challenger 가설사용한 탐색수정 유형절약된 시간
08:00 am중복 서명Join이 N×M 행을 반환SQL 카운트 쿼리DB 마이그레이션(잘못된) → 근본적인 수정~30 분
11:00 am누락된 메모필드가 사용자 ID에 바인딩됨스키마, 컴포넌트, 사용자 인터뷰ADR(코드 없음)2 분
02:00 pm수직선고아 이벤트 → 빈 행DOM, DB 쿼리, git blame1 TS 라인~1 시간
06:00 pm시트에 잘못된 날짜PDF가 세션 날짜를 사용– (없음) → 롤백캐시 새로 고침 필요25 분 롤백 + 알림

핵심 원칙

  1. 반증 가능한 가설을 코드에 뛰어들기 전에 수립한다.
  2. 빠르고 목표 지향적인 탐색을 실행한다 (SQL, DOM, git blame, 이해관계자 인터뷰).
  3. 문제가 설계 결정일 수 있을 때 비기술적 중재를 선호한다.
  4. 결정을 문서화한다 (미니‑ADR) 미래의 “귀에 패치” 수정을 방지하기 위해.

각 사건을 가설 검증 연습으로 다룸으로써, 하나의 버그처럼 보였던 하루가 체계적인 네 단계 조사로 변했고—시간, 코드 변경량, 사용자 불만을 절감했다.

개요

문제는 PDF 버그가 아니라 Live/Snapshot/Cache 분류 버그이며, 이미 교리에서 문서화되어 있었고 한 달 전 ADR‑0024 감사를 통해 구조적 사고 사건 클래스로 표시되었습니다.

“우리를 모순되지 않는 에이전트는 상대가 아니라 더 빠른 타자이다; 저녁에 건너뛴 기술은 더 이상 기술이 아니라 포기된 엄격함이다.
비용은 가끔씩 건너뛰는 데 있는 것이 아니라, 그 건너뛰기가 드러내는 데 있다 — 프로토콜이 인간의 규율에 의존하고, 충분히 차단되는 물리적 메커니즘에 의존하지 않기 때문이다, 이는 일과 피로를 막는 데 충분히 차단되지 않는다.”

기술 정의 (발췌)

File: ~/.claude/skills/challenger/SKILL.md

name: challenger
description: |
  가설을 1문장으로 형성하도록 강제하고
  그 가설을 반박하기 위해 3개의 물리적 탐사를 실행한다
  어떤 수정 코드도 적용하기 전에.

일일 버그 처리 요약

Bug #관찰조치절감된 시간
1프로브 보류정확히 수정½ 시간
2프로토콜이 비즈니스 질문으로 전환됨2 분 (사용자 회귀 대신)
3프로브 보류한 줄 수정1 시간
4프로토콜 건너뛰기25 분 롤백 + Sentry 알림 + 저널 메모

핵심 인사이트: 세 번의 수정이 한 번의 롤백을 방지했습니다. 비율이 평균보다 더 강력하게 말합니다. 이 기술은 오류를 없애지는 않지만, 동일한 오류의 세 번째 시간을 방지합니다.

은유

“조각을 굽기 전에 슬립 레이어를 확인하지 않습니다.
굽는 행위는 커밋입니다.
슬립은 프로브입니다.
급해서 슬립을 건너뛰면 그 조각이 가마에서 깨지는 원인이 됩니다.”

시간 영향 계산

  • 5–10 분의 프로빙 → 20–30 분이 수정‑후‑롤백 사이클에서 절감됩니다.
  • 하루만 보면 계산이 간단하지만 60일 이상이면 압도적입니다.
  • 네 개의 프로브 중 하나만이라도 어제 저녁에 겪은 롤백을 방지한다면, 프로토콜은 이미 비용을 회수한 셈입니다.

참고 문헌

  • 스킬: /challenger, Counterpart Toolkit v0.7 R4 – Falsify before fix
  • 공개 저장소:
  • Article #54 of the series My ERP with Claude Code – 네 개의 연쇄 사고가 발생한 하루에 대한 부검.
0 조회
Back to Blog

관련 글

더 보기 »