// 이 파일이 하는 일: "디버깅과 에러 읽는 법" 코스 — 빨간 에러 메시지를 무서워하지 않고
// '단서'로 읽는 법을 8개 섹션으로 안내하는 정적 학습 페이지.
// (에러 = 단서 → 자바 스택트레이스 → JS 콘솔 에러 → console.log 정석
//  → IntelliJ 브레이크포인트 → 이분 탐색 → 재현 조건 → 30분 룰과 좋은 질문)
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색을 여기서 하드코딩하지 않는다.
// 에러 메시지·터미널 예시는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

import { Link } from 'react-router-dom';

// ── 에러 예시 · 텍스트 다이어그램 상수들 ──

const CODE_ERROR_ANATOMY = `에러 메시지는 사실 '사건 보고서'예요. 세 가지 질문에 답해 줍니다.

① 무슨 일이야?   → 에러의 종류 (NullPointerException, TypeError...)
② 왜 그랬는데?   → 에러 옆의 설명 문장 ("... is null", "... is not a function")
③ 어디서?        → 파일 이름 + 줄 번호 (UserService.java:42, App.jsx:17)

이 셋만 읽어도 문제의 절반은 푼 겁니다.
"에러 났다" 하고 창을 닫는 순간, 범인이 남긴 자백서를 버리는 거예요.`;

const CODE_JAVA_STACKTRACE = `Spring Boot 콘솔에 뜨는 전형적인 스택트레이스:

java.lang.NullPointerException: Cannot invoke "User.getName()"
    because "user" is null                          ← ①② 종류 + 이유
    at com.awesomedev.mirim.service.UserService
        .greet(UserService.java:42)                 ← ③ 여기가 사건 현장!
    at com.awesomedev.mirim.controller.UserController
        .hello(UserController.java:18)              ← 현장으로 부른 사람
    at java.base/jdk.internal.reflect...            ← 여기부터는 자바/스프링 내부
    at org.springframework.web.servlet...           ← (우리가 쓴 코드 아님)
    ... 47 more

읽는 법:
1) 첫 줄 = 무슨 에러 + 왜 (user가 null인데 getName()을 불렀다)
2) 위에서 아래로 내려가며 "내 패키지(com.awesomedev)"가 나오는
   첫 줄을 찾는다 → 거기가 내 코드에서 터진 지점
3) org.springframework, java.base 줄은 스프링/자바 내부 —
   대부분 건너뛰어도 됩니다. 범인은 거의 항상 내 코드에 있어요.`;

const CODE_CAUSED_BY = `스택트레이스가 아주 길 때의 치트키 — "Caused by"를 찾아라!

org.springframework.web.util.NestedServletException:
    Request processing failed ...                   ← 겉포장 (증상)
    at ... (수십 줄)
Caused by: org.hibernate.exception.ConstraintViolationException:
    could not execute statement                     ← 한 겹 벗김
    at ... (수십 줄)
Caused by: org.postgresql.util.PSQLException:       ← 진짜 원인!
    ERROR: duplicate key value violates unique
    constraint "users_email_key"
    at ...

에러는 양파처럼 겹겹이 포장돼서 올라와요.
가장 아래쪽 "Caused by"가 가장 깊은 원인 —
여기서는 "이미 있는 이메일로 또 가입하려다 DB가 거절"이 진범입니다.
긴 스택트레이스를 만나면 Ctrl+F로 "Caused by"부터 검색하세요.`;

const CODE_JS_CONSOLE = `브라우저 F12 → Console 탭에서 자주 만나는 빨간 줄들:

TypeError: Cannot read properties of undefined (reading 'name')
    at UserCard (UserCard.jsx:12)        ← 파일:줄번호, 클릭하면 이동!

→ 해석: "undefined인 무언가의 .name을 읽으려 했다"
  user.name을 썼는데 user가 아직 없다(undefined)는 뜻.
  API 응답이 도착하기 전에 화면을 그리려 할 때 단골로 나옵니다.

ReferenceError: userNmae is not defined
→ 해석: userNmae라는 변수는 세상에 없다 — 오타!

Failed to fetch  /  404 Not Found  /  500 Internal Server Error
→ 해석: 프론트가 아니라 '서버와의 통화'가 문제.
  404 = 그런 주소 없음(URL 오타?), 500 = 서버 쪽에서 터짐
  → 이럴 땐 백엔드(Spring Boot) 콘솔의 스택트레이스를 봐야 해요.

프론트 에러인지 서버 에러인지 구분하는 것만으로도
"어느 콘솔을 봐야 하는지"가 정해집니다.`;

const CODE_CONSOLE_LOG = `console.log 디버깅의 정석 — 아무 데나 찍지 말고 '질문'을 찍자.

// ❌ 이렇게 찍으면 나중에 뭐가 뭔지 몰라요
console.log(data);
console.log(data2);

// ✅ 라벨을 붙이고, '의심 지점의 앞뒤'에 찍는다
console.log('[UserCard] 받은 props.user =', user);
console.log('[fetchUsers] 응답 status =', res.status);
console.log('[fetchUsers] 파싱된 데이터 =', json);

어디에 찍을까? — 데이터가 지나가는 '관문'마다:
1) 함수 입구  → 들어온 값이 애초에 정상인가?
2) 분기 직전  → if문이 어느 길로 갔는가?
3) 반환 직전  → 나가는 값이 기대한 모양인가?

객체는 통째로 찍기: console.log('user =', user)
  → 콘솔에서 펼쳐 보며 실제 모양(필드 이름!)을 확인할 수 있어요.
  "user.userName인 줄 알았는데 실제론 user.username이더라" 같은
  사건의 8할이 여기서 해결됩니다.`;

const CODE_BREAKPOINT = `IntelliJ 브레이크포인트 실습 — 시간을 멈추고 변수 구경하기

1) UserService.java의 의심 가는 줄, 줄 번호 옆 여백을 클릭
   → 빨간 점(브레이크포인트)이 생김
2) 실행을 Run(▶)이 아니라 Debug(🐞)로 시작
3) 브라우저에서 해당 API를 호출 (페이지 새로고침 등)
4) 빨간 점 줄에서 프로그램이 '일시정지'됨!
   ├─ Variables 창: 이 순간 모든 변수의 실제 값이 보임
   ├─ F8 (Step Over): 한 줄씩 실행하며 값 변화 관찰
   ├─ F7 (Step Into): 지금 줄에서 부르는 함수 '안으로' 들어가기
   └─ F9 (Resume): 다음 브레이크포인트까지 다시 진행

console.log가 "지나간 자리에 남긴 CCTV 사진"이라면,
브레이크포인트는 "현장에서 시간을 정지시키고 직접 둘러보기"예요.
값을 확인하려고 로그 찍고 → 재시작하는 반복이 사라집니다.`;

const CODE_BISECT = `이분 탐색식 원인 좁히기 — 업다운 게임처럼 반씩 자르기

증상: "버튼을 누르면 화면이 하얗게 죽어요" (원인 후보: 코드 전체 😱)

[입력] → A(이벤트 핸들러) → B(fetch 호출) → C(응답 가공) → D(화면 그리기) → [출력]

1) 가운데(B와 C 사이)에 로그를 찍는다
   → 로그가 찍혔다? 앞쪽(A·B)은 무죄. 범인은 C·D 중에.
   → 안 찍혔다? 범인은 A·B 중에.
2) 남은 절반의 가운데에 또 찍는다 → 또 반으로 줄어듦
3) 반복하면 후보 16곳도 단 4번 만에 한 곳으로 좁혀져요 (2⁴=16)

코드뿐 아니라 '변경 이력'에도 통합니다:
"어제는 됐는데 오늘 안 돼요" → 어제와 오늘 사이의 커밋들을
반씩 갈라 가운데 커밋으로 되돌려 실행 — 되면 뒤쪽 절반이,
안 되면 앞쪽 절반이 범인. (git엔 이걸 자동화한 bisect도 있어요.)`;

const CODE_REPRODUCE = `재현 조건 찾기 — "가끔 안 돼요"를 "이럴 때 항상 안 돼요"로

버그 신고의 등급:
  ❌ "가끔 로그인이 안 돼요"            → 아무도 못 고침
  ⭕ "비밀번호에 공백이 들어가면
      항상 로그인이 안 돼요"            → 5분이면 고침

재현 조건을 좁히는 질문 목록:
- 항상? 가끔? (가끔이면 → 뭐가 다를 때 나는지 찾는 게 숙제)
- 특정 계정만? 특정 데이터만? (테스트 계정 vs 내 계정)
- 새로고침하면? 다른 브라우저에선? 시크릿 창에선?
  (시크릿 창에서 되면 → 캐시나 저장된 로그인 정보가 용의자)
- 방금 뭘 바꿨지? (내 마지막 커밋/저장이 1순위 용의자)

"항상 터지게 만드는 최소 재현 절차"를 찾으면
그 버그는 이미 반쯤 고쳐진 겁니다 — 고친 뒤에
같은 절차로 '이제 안 터짐'을 증명할 수도 있고요.`;

const CODE_GOOD_QUESTION = `30분 룰 — 그리고 '답을 부르는 질문'의 3요소

30분 룰: 혼자 30분 파고들었는데 진전이 없으면 질문한다.
  - 5분 만에 질문 → 스스로 클 기회를 버림
  - 3시간 붙잡기 → 팀의 시간을 버림 (여러분의 막힘은 팀의 일이에요)

질문 템플릿 (Gitea 이슈·팀 채팅 공통):
─────────────────────────────────────────
[하려던 것] 회원가입 API에 이메일 중복 검사를 붙이는 중
[에러 전문] (스크린샷 말고 텍스트로, 요약하지 말고 통째로!)
  Caused by: org.postgresql.util.PSQLException: ERROR:
  duplicate key value violates unique constraint ...
[재현 방법] 같은 이메일로 두 번 가입 요청하면 항상 발생
[시도한 것] ① 스택트레이스에서 Caused by 확인 → DB 제약 위반
            ② 검사 로직에 로그 찍음 → 검사 함수가 아예 호출 안 됨
[내 추측]   컨트롤러에서 검사 서비스를 안 거치는 것 같은데,
            어디서 연결해야 할지 모르겠습니다
─────────────────────────────────────────

에러를 요약하지 말고 '전문'을 붙이는 이유:
여러분이 사소하다고 지운 그 줄에 답이 있는 경우가 정말 많아요.
그리고 '시도한 것'을 쓰다가 스스로 답을 찾는 일 — 매우 흔합니다.
(고무 오리에게 설명하다 깨닫는 그 현상, 질문 쓰기에서도 일어나요.)`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. props만 바꿔 재사용.
function Section({ n, title, sub, children }) {
  return (
    <section className="step-card" id={`sec-${n}`}>
      <div className="step-head">
        <span className="step-num">{n}</span>
        <div>
          <h3>{title}</h3>
          {sub && <span className="step-sub">{sub}</span>}
        </div>
      </div>
      <div className="step-body">{children}</div>
    </section>
  );
}

function Code({ children }) {
  return <pre className="code-block"><code>{children}</code></pre>;
}

const SECTIONS = [
  { n: 1, label: '에러 = 단서' },
  { n: 2, label: '자바 스택트레이스' },
  { n: 3, label: 'JS 콘솔 에러' },
  { n: 4, label: 'console.log 정석' },
  { n: 5, label: '브레이크포인트' },
  { n: 6, label: '이분 탐색' },
  { n: 7, label: '재현 조건' },
  { n: 8, label: '30분 룰 · 좋은 질문' },
];

export default function DebuggingPage() {
  return (
    <div>
      {/* 히어로: 이 코스의 목표 — 빨간 글씨 공포증 탈출 */}
      <div className="hero">
        <div className="eyebrow">Course · 소프트웨어</div>
        <h1>디버깅과 에러 읽는 법<br />— 빨간 글씨는 자백서다</h1>
        <p>
          에러 메시지는 프로그램이 남긴 <strong>사건 보고서</strong>예요 — 무섭다고 창을
          닫으면 범인이 쓴 자백서를 버리는 셈이죠. 이 코스에서는 자바 스택트레이스와
          브라우저 콘솔을 <strong>읽는 순서</strong>, 그리고 원인을 반씩 잘라 좁혀 가는
          탐정의 기술을 배웁니다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 70분</span>
          <span className="chip">실습 3개</span>
          <span className="chip">준비물: 내 PC + IntelliJ</span>
        </div>
      </div>

      {/* 섹션 바로가기 */}
      <nav className="pill-nav">
        {SECTIONS.map((s) => (
          <a key={s.n} href={`#sec-${s.n}`}>
            <b>{s.n}</b>
            {s.label}
          </a>
        ))}
      </nav>

      <Section n={1} title="에러는 적이 아니라 단서" sub="사건 보고서를 읽는 탐정이 되자">
        <p>
          코드가 터지면 화면 가득 빨간 글씨가 쏟아지죠. 이때 개발자는 두 부류로 갈립니다 —
          <strong> 창을 닫고 코드를 노려보는 사람</strong>과,
          <strong> 빨간 글씨를 천천히 읽는 사람</strong>. 실력 차이는 대부분 여기서 나요.
        </p>
        <p>
          에러 메시지는 프로그램이 죽기 직전에 남긴 <strong>사건 보고서</strong>입니다.
          누가(어느 파일·몇 번째 줄), 무엇을(어떤 종류의 에러), 왜(설명 문장) —
          범인이 자백서까지 써 놓고 갔는데 안 읽을 이유가 없죠.
        </p>
        <Code>{CODE_ERROR_ANATOMY}</Code>
        <div className="tip">
          <b>탐정의 첫 습관</b> 에러가 나면 고치려 들기 전에, 에러 메시지를
          <strong> 소리 내어(속으로라도) 한국어로 번역</strong>해 보세요.
          "user가 null인데 getName을 불렀대" — 번역이 되면 이미 절반은 푼 겁니다.
          번역이 안 되는 단어는 그대로 검색하면 돼요.
        </div>
      </Section>

      <Section n={2} title="자바 스택트레이스 읽는 법" sub="위에서 아래로, '내 코드'가 나올 때까지">
        <p>
          Spring Boot 서버가 터지면 콘솔에 수십 줄짜리 <strong>스택트레이스</strong>가
          쏟아집니다. 스택트레이스는 "사건 현장까지 누가 누구를 불렀는지"를 적은
          <strong> 호출의 족보</strong>예요 — 맨 위가 사건 현장, 아래로 갈수록
          그 현장으로 이어진 호출의 조상들입니다.
        </p>
        <Code>{CODE_JAVA_STACKTRACE}</Code>
        <p>
          핵심은 <strong>전부 읽지 않는 것</strong>이에요. 첫 줄(무슨 에러·왜)을 읽고,
          위에서 아래로 훑으며 <strong>내 패키지 이름이 나오는 첫 줄</strong>을 찾으면 끝.
          스프링 내부 줄 수십 개는 "택배가 거쳐 온 물류 센터 목록"일 뿐, 범인이 아닙니다.
        </p>
        <p>
          그런데 스택트레이스가 여러 덩어리로 이어질 때가 있어요. 그럴 땐 이 단어를 찾으세요.
        </p>
        <Code>{CODE_CAUSED_BY}</Code>
        <div className="warn">
          <b>흔한 함정</b> 맨 위 에러만 보고 "스프링이 이상해요!"라고 결론 내리기.
          겉포장(NestedServletException)은 증상이고, 진범은 거의 항상
          <strong> 가장 아래쪽 Caused by</strong>에 있습니다. 긴 로그에서는
          <span className="kbd">Ctrl</span>+<span className="kbd">F</span>로
          <span className="icode">Caused by</span>를 검색하는 게 제일 빠른 길이에요.
        </div>
      </Section>

      <Section n={3} title="JS 콘솔 에러 읽기" sub="F12 콘솔의 단골 손님들과 인사하기">
        <p>
          프론트(React)에서 터진 에러는 브라우저의 <span className="kbd">F12</span> →
          <span className="kbd">Console</span> 탭에 모입니다. 자바처럼 긴 족보 대신
          짧고 굵은 한 줄 + <strong>파일:줄번호 링크</strong>가 뜨는데, 그 링크를 클릭하면
          문제의 코드로 바로 이동해요.
        </p>
        <Code>{CODE_JS_CONSOLE}</Code>
        <p>
          특히 중요한 감각이 <strong>"이게 프론트 문제야, 서버 문제야?"</strong>를 가르는
          거예요. <span className="icode">TypeError</span>·<span className="icode">ReferenceError</span>는
          내 JS 코드 문제, <span className="icode">404</span>·<span className="icode">500</span> 같은
          숫자가 보이면 서버와의 통신 문제 — 후자라면 브라우저가 아니라
          <strong> Spring Boot 콘솔</strong>(섹션 2!)을 열어야 합니다. 병원으로 치면
          접수처에서 내과로 갈지 외과로 갈지 정하는 단계죠.
        </p>
        <div className="tip">
          <b>직접 확인해 보기 ①</b> 우리 학습 플랫폼을 열고 <span className="kbd">F12</span> →
          Console 탭에서 <span className="icode">window.없는변수.name</span>을 직접 입력해 보세요.
          방금 배운 <span className="icode">TypeError: Cannot read properties of undefined</span>가
          그대로 나타나요. 이번엔 <span className="icode">엉뚱한이름()</span>을 입력해서
          <span className="icode">ReferenceError</span>도 만나 보세요 — 일부러 내 본 에러는
          실전에서 만나도 반갑습니다.
        </div>
      </Section>

      <Section n={4} title="console.log 디버깅의 정석" sub="아무 데나 찍지 말고, 질문을 찍자">
        <p>
          가장 원시적이면서 가장 많이 쓰는 무기가 <span className="icode">console.log</span>예요
          (자바라면 로그 출력). 그런데 무작정 찍으면 콘솔이 쓰레기장이 됩니다.
          로그 하나하나가 <strong>"이 지점에서 이 값은 뭐지?"라는 질문</strong>이 되도록 찍는 게 정석이에요.
        </p>
        <Code>{CODE_CONSOLE_LOG}</Code>
        <ol className="olist">
            <li><strong>라벨을 붙인다</strong> — <span className="icode">'[함수명] 변수명 ='</span> 형식이면 어디서 찍힌 건지 바로 알아요.</li>
            <li><strong>관문마다 찍는다</strong> — 함수 입구·분기 직전·반환 직전. 데이터가 어디서부터 이상해졌는지 드러납니다.</li>
            <li><strong>객체는 통째로 찍는다</strong> — 필드 이름을 내 기억이 아니라 실제 데이터로 확인하세요.</li>
            <li><strong>다 잡으면 지운다</strong> — 디버그 로그를 커밋에 남기면 다음 사람의 콘솔이 쓰레기장이 돼요.</li>
          </ol>
        <div className="warn">
          <b>주의</b> 로그가 <strong>아예 안 찍히는 것</strong>도 중요한 단서입니다!
          "값이 이상하다"가 아니라 "이 함수 자체가 호출되지 않는다"는 뜻이니까요.
          안 찍힌 로그는 실패가 아니라 용의자 절반을 지워 준 수확이에요.
        </div>
      </Section>

      <Section n={5} title="IntelliJ 브레이크포인트" sub="시간을 멈추고 변수 값을 직접 구경하기">
        <p>
          <span className="icode">console.log</span>가 지나간 자리의 <strong>CCTV 사진</strong>이라면,
          브레이크포인트는 <strong>사건 현장에서 시간을 정지</strong>시키는 마법이에요.
          멈춘 그 순간의 모든 변수 값을 눈으로 보고, 한 줄씩 재생하며 어디서 값이
          틀어지는지 실시간으로 관찰할 수 있습니다.
        </p>
        <Code>{CODE_BREAKPOINT}</Code>
        <div className="tip">
          <b>직접 확인해 보기 ②</b> IntelliJ에서 우리 백엔드 프로젝트를 열고, 아무
          컨트롤러 메서드의 첫 줄에 브레이크포인트(줄 번호 옆 클릭)를 걸어 보세요.
          <strong> Debug(🐞)</strong>로 실행 → 브라우저에서 그 API를 호출(페이지 접속) →
          IntelliJ가 그 줄에서 멈추면, Variables 창에서 요청으로 들어온 값을 구경하고
          <span className="kbd">F8</span>로 세 줄만 전진해 보세요. 마지막엔
          <span className="kbd">F9</span>로 풀어 주기 — 안 풀면 브라우저는 계속
          로딩 중으로 기다립니다(이것도 직접 목격해 보세요!).
        </div>
        <p>
          언제 로그를 쓰고 언제 브레이크포인트를 쓸까요? <strong>빠른 확인 한 번</strong>이면
          로그, <strong>값이 변해 가는 과정을 추적</strong>해야 하면 브레이크포인트 —
          둘 다 쓸 줄 알아야 상황에 맞는 무기를 고를 수 있어요.
        </p>
      </Section>

      <Section n={6} title="이분 탐색식 원인 좁히기" sub="업다운 게임처럼, 용의자를 반씩 지운다">
        <p>
          에러 메시지가 친절하지 않을 때도 있어요. "화면이 하얗다", "아무 반응이 없다" —
          단서가 없으면 코드 전체가 용의자죠. 이럴 때 처음부터 한 줄씩 읽는 건 최악의 수사예요.
          <strong> 1~100 업다운 게임</strong>을 떠올리세요. 1부터 차례로 부르면 최대 100번,
          반씩 자르면 <strong>7번</strong>이면 끝납니다.
        </p>
        <Code>{CODE_BISECT}</Code>
        <p>
          요령은 <strong>"데이터가 지나가는 길의 한가운데"에 검문소(로그)를 세우는 것</strong>.
          검문소를 통과했으면 앞 구간은 무죄, 못 왔으면 앞 구간이 유죄 — 어느 쪽이든
          용의자가 절반으로 줄어요. 코드가 아니라 "어제는 됐는데 오늘 안 되는" 상황이라면
          커밋 이력을 반씩 잘라 가는 것도 똑같은 원리입니다.
        </p>
        <svg className="diagram" viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="이분 탐색식 원인 좁히기: 가운데에 로그 검문소를 세울 때마다 용의자가 4곳에서 2곳, 1곳으로 절반씩 줄어드는 과정">
          <text x="60" y="24" fontSize="13" fontWeight="600" fill="var(--ink)">① 가운데(B와 C 사이)에 로그를 찍는다 — 용의자는 아직 4곳</text>
          <rect x="60" y="34" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="115" y="51" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">A</text>
          <text x="115" y="67" fontSize="12" textAnchor="middle" fill="var(--ink)">이벤트 핸들러</text>
          <rect x="192" y="34" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="247" y="51" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">B</text>
          <text x="247" y="67" fontSize="12" textAnchor="middle" fill="var(--ink)">fetch 호출</text>
          <rect x="324" y="34" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="379" y="51" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">C</text>
          <text x="379" y="67" fontSize="12" textAnchor="middle" fill="var(--ink)">응답 가공</text>
          <rect x="456" y="34" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="511" y="51" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">D</text>
          <text x="511" y="67" fontSize="12" textAnchor="middle" fill="var(--ink)">화면 그리기</text>
          <text x="181" y="59" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <text x="445" y="59" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <line x1="313" y1="28" x2="313" y2="88" stroke="var(--amber)" strokeWidth="2" strokeDasharray="4 3" />
          <text x="313" y="101" fontSize="12" fontWeight="600" textAnchor="middle" fill="var(--amber)">로그 ①</text>
          <text x="60" y="130" fontSize="13" fontWeight="600" fill="var(--ink)">② 로그가 찍혔다! → 앞쪽 A·B는 무죄 — 용의자는 C·D 2곳</text>
          <rect x="60" y="140" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="4 3" />
          <text x="115" y="157" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--muted)">A</text>
          <text x="115" y="173" fontSize="12" textAnchor="middle" fill="var(--muted)">무죄</text>
          <rect x="192" y="140" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="4 3" />
          <text x="247" y="157" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--muted)">B</text>
          <text x="247" y="173" fontSize="12" textAnchor="middle" fill="var(--muted)">무죄</text>
          <rect x="324" y="140" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="379" y="157" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">C</text>
          <text x="379" y="173" fontSize="12" textAnchor="middle" fill="var(--ink)">응답 가공</text>
          <rect x="456" y="140" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="1.5" />
          <text x="511" y="157" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">D</text>
          <text x="511" y="173" fontSize="12" textAnchor="middle" fill="var(--ink)">화면 그리기</text>
          <text x="181" y="165" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <text x="313" y="165" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <line x1="445" y1="134" x2="445" y2="194" stroke="var(--amber)" strokeWidth="2" strokeDasharray="4 3" />
          <text x="445" y="207" fontSize="12" fontWeight="600" textAnchor="middle" fill="var(--amber)">로그 ②</text>
          <text x="60" y="236" fontSize="13" fontWeight="600" fill="var(--ink)">③ 남은 절반의 가운데에 또 찍는다 → 또 찍혔다! → 범인은 D 한 곳</text>
          <rect x="60" y="246" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="4 3" />
          <text x="115" y="263" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--muted)">A</text>
          <text x="115" y="279" fontSize="12" textAnchor="middle" fill="var(--muted)">무죄</text>
          <rect x="192" y="246" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="4 3" />
          <text x="247" y="263" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--muted)">B</text>
          <text x="247" y="279" fontSize="12" textAnchor="middle" fill="var(--muted)">무죄</text>
          <rect x="324" y="246" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="4 3" />
          <text x="379" y="263" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--muted)">C</text>
          <text x="379" y="279" fontSize="12" textAnchor="middle" fill="var(--muted)">무죄</text>
          <rect x="456" y="246" width="110" height="42" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="2.5" />
          <text x="511" y="263" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">D</text>
          <text x="511" y="279" fontSize="12" textAnchor="middle" fill="var(--ink)">화면 그리기</text>
          <text x="181" y="271" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <text x="313" y="271" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <text x="445" y="271" fontSize="13" textAnchor="middle" fill="var(--muted)">→</text>
          <text x="511" y="303" fontSize="13" fontWeight="700" textAnchor="middle" fill="var(--rose)">범인!</text>
          <text x="320" y="330" fontSize="13" fontWeight="600" textAnchor="middle" fill="var(--ink)">용의자 4곳 → 2곳 → 1곳, 로그는 단 2번</text>
          <text x="320" y="348" fontSize="12" textAnchor="middle" fill="var(--muted)">후보가 16곳이라도 4번이면 한 곳으로 좁혀져요 (2⁴=16)</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 가운데에 검문소(로그)를 세울 때마다 용의자가 절반씩 줄어든다</p>
        <div className="tip">
          <b>버릇 들이기</b> "어디가 문제지?" 싶을 때마다 속으로 외치세요 —
          <strong> "반으로 자르자."</strong> 감으로 여기저기 찔러 보는 것보다
          항상 빠르고, 항상 끝이 납니다.
        </div>
      </Section>

      <Section n={7} title="재현 조건 찾기" sub='"가끔 안 돼요"를 "이럴 때 항상 안 돼요"로'>
        <p>
          고칠 수 없는 버그는 없지만, <strong>다시 일으킬 수 없는 버그</strong>는 못 고칩니다.
          그래서 수사의 중요한 반환점이 "이렇게 하면 <strong>항상</strong> 터진다"는
          <strong> 재현 절차</strong>를 손에 넣는 순간이에요.
        </p>
        <Code>{CODE_REPRODUCE}</Code>
        <p>
          재현 절차는 두 번 일합니다. 고치기 <strong>전</strong>엔 원인을 가리키는 나침반이 되고,
          고친 <strong>후</strong>엔 같은 절차로 "이제 안 터진다"를 증명하는 검증 도구가 돼요.
          범인을 잡는 것도, 잡았다고 확인하는 것도 같은 절차 하나로 하는 거죠.
        </p>
        <div className="warn">
          <b>1순위 용의자</b> "갑자기 안 돼요"의 8할은 <strong>방금 내가 바꾼 것</strong>입니다.
          귀신이 아니라 내 마지막 저장·마지막 커밋부터 의심하세요. Gitea에서 내 최근 커밋의
          변경 파일 목록을 열어 보는 게 훌륭한 첫 수입니다.
        </div>
      </Section>

      <Section n={8} title="30분 룰과 좋은 질문" sub="혼자 파는 시간과 팀의 시간, 그 사이의 예의">
        <p>
          어썸데브의 약속, <strong>30분 룰</strong> — 혼자 30분 진지하게 파 보고, 그래도
          진전이 없으면 질문합니다. 5분 만에 묻는 건 스스로 클 기회를 버리는 것이고,
          3시간을 혼자 끙끙대는 건 팀의 시간을 버리는 거예요. 여러분이 막혀 있는 건
          부끄러운 일이 아니라 <strong>팀이 알아야 할 정보</strong>입니다.
        </p>
        <Code>{CODE_GOOD_QUESTION}</Code>
        <ol className="olist">
            <li><strong>하려던 것</strong> — 목표를 알아야 답도 목표에 맞출 수 있어요.</li>
            <li><strong>에러 전문</strong> — 요약·스크린샷 말고 텍스트 통째로. 여러분이 지운 그 줄에 답이 있는 경우가 정말 많아요.</li>
            <li><strong>재현 방법 + 시도한 것</strong> — 이걸 쓰다가 스스로 답을 찾는 일이 놀랄 만큼 자주 일어납니다.</li>
          </ol>
        <div className="tip">
          <b>직접 확인해 보기 ③</b> 이번 주에 만난 에러 하나를 골라, 위 템플릿대로
          <strong> Gitea(edu.awesomedevapp.com:3000) 이슈</strong>로 실제로 작성해 보세요.
          이미 해결한 에러여도 좋아요 — "시도한 것"까지 다 쓴 다음 멘토에게 링크를 보내고,
          "이 질문 받으면 바로 도와줄 수 있겠어요?"라고 피드백을 받아 보세요.
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>🔍 여기까지 왔다면</h3>
          <p className="muted">
            이제 여러분은 빨간 글씨를 <strong>읽고</strong>(스택트레이스·콘솔),
            시간을 <strong>멈추고</strong>(브레이크포인트), 용의자를 <strong>반씩 지우고</strong>(이분 탐색),
            사건을 <strong>재현하고</strong>, 막히면 <strong>답이 오는 질문</strong>을 쓸 수 있는
            탐정이에요. 디버깅으로 잡은 버그가 다시 돌아오지 못하게 막는 법 —{' '}
            <Link to="/learn/git-deep"><strong>Git과 협업</strong></Link> 코스에서 커밋으로 수사 기록을
            남기는 법을 이어서 배우고, 다음 과제에서 만나는 첫 에러에 오늘 배운 순서를 그대로
            적용해 보세요.
          </p>
        </div>
      </div>
    </div>
  );
}
