// 이 파일이 하는 일: "운영과 장애 대응 기초" 코스 — 배포 이후의 세계, 즉
// 서비스가 느려지고 500이 뜨고 접속이 안 될 때 무엇을 어떤 순서로 확인하고,
// 어떻게 복구하고, 어떻게 기록으로 남기는지를 8개 섹션으로 안내하는 정적 학습 페이지.
// (배포는 시작이다 → 장애의 신호 → 원인 좁히기 → 로그 읽기 → 재시작의 정석
//  → 백업 3-2-1 → 장애 보고서 → 우리 플랫폼 시나리오 연습)
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip, warn 등)를 재사용한다 — 색을 여기서 하드코딩하지 않는다.
// 터미널 예시·표는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

// ── 텍스트 다이어그램 · 터미널 예시 상수들 ──

const CODE_LIFECYCLE = `개발자가 보는 시간  vs  서비스가 사는 시간

기획 → 개발 → 테스트 → 배포     ← 여기까지가 "만드는 시간" (몇 주)
                        └─→ 운영 · 운영 · 운영 · 운영 ...   ← "사는 시간" (몇 년)

배포는 결승선이 아니라 개업식이에요.
가게는 개업식 다음 날부터가 진짜 — 손님이 오고, 정전이 나고,
냉장고가 고장 나는 건 전부 '개업 이후'에 벌어집니다.`;

const CODE_SIGNALS = `장애의 3대 신호 — 증상이 다르면 범인도 다르다

신호            사용자가 보는 것              흔한 범인 후보
──────────────────────────────────────────────────────────
① 느림          빙글빙글... 10초 뒤 뜸        DB 느린 쿼리, 메모리 부족,
                                             트래픽 폭주
② 500 에러      "서버 오류" 페이지            백엔드 예외(코드 버그),
                                             DB 연결 끊김
③ 접속 불가     아예 응답 없음 / 타임아웃      서버 다운, 컨테이너 죽음,
                                             도메인·인증서 문제

같은 "안 돼요"라도 셋 중 뭔지부터 구분하세요.
의사가 "어디가 어떻게 아픈지"부터 묻는 것과 같아요.`;

const CODE_NARROWING = `원인 좁히기 — 바깥에서 안쪽으로, 큰 것에서 작은 것으로

[1] 서버 자체가 살았나?          ping, SSH 접속 시도
      │ 살았다면 ↓
[2] 컨테이너가 살았나?           docker ps  (Up? Restarting? Exited?)
      │ 살았다면 ↓
[3] 앱이 뭐라고 말하나?          docker logs 로 에러 메시지 확인
      │ 앱은 멀쩡해 보이면 ↓
[4] DB가 살았나?                 DB 컨테이너 상태, 연결 에러 로그

정전됐을 때를 떠올려 보세요:
동네 전체 정전인가?(서버) → 우리 집 두꺼비집인가?(컨테이너)
→ 이 방 스위치인가?(앱) → 전구가 나갔나?(DB)
큰 범위부터 확인해야 헛수고가 없어요.`;

const CODE_DOCKER_PS = `# EC2에 SSH로 들어간 뒤, 컨테이너 생존 확인:
docker ps

CONTAINER ID  IMAGE          STATUS                     NAMES
a1b2c3d4      mirim-backend  Up 3 days                  backend
e5f6a7b8      postgres:16    Up 3 days (healthy)        db
c9d0e1f2      caddy          Restarting (1) 5s ago      caddy   ← 범인!

# STATUS 읽는 법:
#  Up 3 days        → 3일째 정상 가동 중
#  Restarting (1)   → 죽었다 살아나기를 반복 중 (뭔가 잘못됨!)
#  Exited (137)     → 죽어 있음. 137은 메모리 부족(OOM)으로
#                     강제 종료됐을 때 자주 보이는 코드

# 멈춘 컨테이너까지 다 보려면:
docker ps -a`;

const CODE_LOGS = `# 최근 로그 100줄만 보기 (전체를 보면 화면이 폭발해요):
docker logs --tail 100 backend

# 실시간으로 흘러가는 로그 지켜보기 (-f = follow, 따라가기):
docker logs -f backend
# 끄기: Ctrl + C

# 컨테이너 밖 일반 로그 파일도 같은 요령:
tail -f /var/log/caddy/access.log

# 로그에서 눈이 먼저 가야 할 곳:
2026-07-16 14:03:21 ERROR ... Connection refused: db:5432
                    ─────      ──────────────────────────
                    레벨!       메시지 — "DB 5432 포트 연결 거부"
# ERROR·FATAL 레벨 + 시각 + 마지막 메시지, 이 셋이 단서의 90%예요.
# 특히 죽기 '직전' 마지막 몇 줄이 유언장입니다.`;

const CODE_RESTART = `# 재시작의 정석 — 순서가 있어요

# 1) 재시작 '전에' 증거(로그)부터 확보! 재시작하면 로그 맥락이 흐려져요
docker logs --tail 300 backend > ~/incident_$(date +%m%d_%H%M).log

# 2) 컨테이너 하나만 재시작
docker restart backend

# 3) 살아났는지 확인 — STATUS와 로그 둘 다
docker ps
docker logs -f backend    # 에러 없이 기동 로그가 쭉 올라오면 OK

# 4) 사용자 입장에서 최종 확인 (서버 안에서 직접 요청 쏘기)
curl -I https://edu.awesomedevapp.com
# HTTP/2 200  ← 이게 떠야 진짜 복구!`;

const CODE_321 = `백업 3-2-1 규칙

3 — 복사본을 최소 3개 유지            (원본 1 + 백업 2)
2 — 서로 다른 2가지 저장 장소/매체에    (예: 서버 디스크 + S3)
1 — 그중 1개는 반드시 다른 장소(원격)에 (서버가 통째로 죽어도 살아남게)

우리 플랫폼 예시:
원본:   EC2 위 PostgreSQL (지금 돌아가는 DB)
백업 1: 같은 EC2의 매일 새벽 pg_dump 파일
백업 2: AWS S3에 업로드된 사본  ← "다른 장소 1개" 담당

# DB 백업(덤프) 만들기 — 개념만 눈으로 익혀 두세요:
docker exec db pg_dump -U mirim mirimdb > backup_20260716.sql

# 복구는 반대 방향:
docker exec -i db psql -U mirim mirimdb < backup_20260716.sql`;

const CODE_REPORT = `장애 보고서 5줄 골격 — 이 순서 그대로 채우면 됩니다

① 무슨 일이:  7/16 14:00~14:25 학습 플랫폼 접속 시 500 에러 (25분)
② 영향 범위:  로그인한 전체 사용자, 페이지 로딩 실패
③ 원인:      DB 컨테이너가 메모리 부족(OOM)으로 종료됨
④ 조치:      로그 확보 → db 컨테이너 재시작 → 정상 확인(14:25)
⑤ 재발 방지:  메모리 사용량 모니터링 추가, 컨테이너 메모리 상한 조정 검토

포인트:
- 시각은 반드시 숫자로 (❌ "아까" / ⭕ "14:03")
- 추측과 사실을 구분 ("~로 추정됨"이라고 솔직하게 쓰기)
- ⑤가 없는 보고서는 반쪽 — 같은 장애를 두 번 겪지 않는 게 목적!`;

const CODE_SCENARIO = `시나리오 훈련 — 소방 대피 훈련처럼, 말로 리허설해 보기

[시나리오 A] 아침에 출근했더니 edu.awesomedevapp.com이 안 열린다
  → 내 순서: ping/SSH → docker ps → 죽은 컨테이너 logs
    → 로그 저장 → restart → curl로 200 확인 → 보고

[시나리오 B] 사이트는 열리는데 로그인만 500 에러
  → 힌트: 사이트가 '열린다'는 건 서버·Caddy는 살았다는 뜻!
    범위가 이미 좁혀졌죠 → 백엔드(Spring Boot) 로그부터

[시나리오 C] 어제까지 되던 게 배포 직후부터 이상하다
  → 제1용의자는 '방금 바꾼 것'. 배포 내역부터 확인,
    필요하면 이전 버전으로 롤백 → 원인은 그다음에 천천히

[시나리오 D] DB 데이터가 실수로 지워졌다
  → 재시작은 아무 소용 없음! (섹션 5에서 배운 '덮으면 안 되는 것')
    최근 백업 확인 → 복구 → 유실 구간 파악 → 정직하게 보고`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: '원인 좁히기' },
  { n: 4, label: '로그 읽기' },
  { n: 5, label: '재시작의 정석' },
  { n: 6, label: '백업 3-2-1' },
  { n: 7, label: '장애 보고서' },
  { n: 8, label: '시나리오 연습' },
];

export default function OpsBasicsPage() {
  return (
    <div>
      {/* 히어로: 배포 이후의 세계로 초대 */}
      <div className="hero">
        <div className="eyebrow">Course · 서버와 데이터</div>
        <h1>운영과 장애 대응 기초<br />— 서비스가 아플 때 침착해지는 법</h1>
        <p>
          코드를 배포하는 순간부터 서비스는 24시간 살아 움직이고, 언젠가 반드시 아픕니다.
          이 코스에서는 장애의 신호를 알아채고, 원인을 <strong>순서대로</strong> 좁히고,
          복구하고, 기록으로 남기는 — 운영자의 기본기를 배워요.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 60분</span>
          <span className="chip">실습 2개 + 시나리오 4개</span>
          <span className="chip">준비물: 내 PC + Docker(선택)</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>
        <Code>{CODE_LIFECYCLE}</Code>
        <p>
          우리 학습 플랫폼도 마찬가지예요. AWS 서울 리전의 EC2 위에서 Docker 컨테이너들
          (React 프론트, Spring Boot 백엔드, PostgreSQL, Caddy)이 <strong>지금 이 순간에도</strong>
          돌아가고 있습니다. 이렇게 돌아가는 서비스를 건강하게 유지하는 일이 <strong>운영(Operations)</strong>,
          아플 때 고치는 일이 <strong>장애 대응</strong>이에요. 운영을 아는 개발자는 코드를 짤 때부터
          "이게 새벽 3시에 죽으면 어떤 로그가 남을까?"를 생각합니다 — 그게 이 코스의 목표예요.
        </p>
        <div className="tip">
          <b>마음가짐 하나</b> 장애는 실력이 부족해서 나는 게 아니라, 살아 있는 서비스라면
          <strong> 반드시</strong> 겪는 일이에요. 세계적인 서비스들도 매년 장애 공지를 올립니다.
          중요한 건 장애를 0으로 만드는 게 아니라, <strong>빨리 알아채고 · 침착하게 고치고 · 같은 일을
          반복하지 않는 것</strong>입니다.
        </div>
      </Section>

      <Section n={2} title="장애의 신호 — 느림 · 500 · 접속 불가" sub="증상이 다르면 범인도 다르다">
        <p>
          병원에 가면 의사 선생님이 "어디가, 어떻게, 언제부터 아프세요?"부터 묻죠.
          장애 대응도 똑같아요. 사용자의 "안 돼요!"라는 말만 듣고 뛰어들면 안 되고,
          <strong> 증상이 셋 중 무엇인지</strong>부터 구분해야 합니다.
        </p>
        <Code>{CODE_SIGNALS}</Code>
        <ol className="olist">
          <li>
            <strong>느림</strong> — 응답은 오는데 오래 걸려요. 서버는 살아 있지만 어딘가
            힘에 부치는 상태. 손님이 몰려 주방(DB)이 밀렸거나, 주방장이 지쳤거나(메모리 부족).
          </li>
          <li>
            <strong>500 에러</strong> — 서버가 "저 지금 처리하다 넘어졌어요"라고 스스로 알려주는
            상태예요. 요청은 도착했는데 처리 중 예외가 터진 것. 범인은 대부분 백엔드 코드나 DB 연결.
          </li>
          <li>
            <strong>접속 불가</strong> — 아예 대답이 없어요. 가게 문이 잠긴 상태.
            서버가 죽었거나, 컨테이너가 내려갔거나, 도메인·인증서가 꼬였을 수 있어요.
          </li>
        </ol>
        <div className="warn">
          <b>참고로, 400번대와 500번대는 달라요</b> — <span className="icode">404</span>(없는 주소),
          <span className="icode">403</span>(권한 없음) 같은 400번대는 "요청한 쪽 문제",
          <span className="icode">500</span>번대가 "서버 쪽 문제"입니다. 사용자가 주소를 잘못 친 것까지
          장애로 착각하면 밤새 엉뚱한 곳을 뒤지게 돼요.
        </div>
      </Section>

      <Section n={3} title="원인 좁히기 — 확인에는 순서가 있다" sub="서버 → 컨테이너 → 로그 → DB, 바깥에서 안쪽으로">
        <p>
          집에 불이 안 들어올 때를 떠올려 보세요. 전구부터 갈아 끼우는 사람은 없어요.
          창밖을 보고 <strong>동네 전체 정전인지</strong> 먼저 확인하고, 아니면 <strong>두꺼비집</strong>,
          그다음 <strong>방 스위치</strong>, 마지막에 <strong>전구</strong> 순서죠.
          장애 대응도 똑같이 <strong>큰 범위에서 작은 범위로</strong> 좁혀 갑니다.
        </p>
        <svg className="diagram" viewBox="0 0 640 368" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="장애 원인 좁히기 순서도: 서버, 컨테이너, 앱 로그, DB 순서로 바깥에서 안쪽으로 확인한다">
          <text x="84" y="30" textAnchor="middle" fontSize="12" fill="var(--teal)">바깥 · 큰 범위</text>
          <line x1="84" y1="42" x2="84" y2="324" stroke="var(--teal)" strokeWidth="1.5" strokeDasharray="4 4" />
          <polyline points="78,318 84,325 90,318" fill="none" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="84" y="348" textAnchor="middle" fontSize="12" fill="var(--teal)">안쪽 · 작은 범위</text>
          <rect x="170" y="12" width="300" height="58" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <circle cx="200" cy="41" r="13" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="200" y="46" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--primary)">1</text>
          <text x="224" y="37" fontSize="14" fontWeight="700" fill="var(--ink)">서버가 살았나?</text>
          <text x="224" y="57" fontSize="12" fill="var(--muted)">ping · SSH 접속 시도</text>
          <line x1="470" y1="41" x2="484" y2="41" stroke="var(--line)" strokeWidth="1.5" />
          <text x="490" y="45" fontSize="12" fill="var(--muted)">동네 전체가 정전?</text>
          <line x1="320" y1="74" x2="320" y2="97" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="314,91 320,98 326,91" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="330" y="90" fontSize="12" fill="var(--muted)">살았다면</text>
          <rect x="170" y="106" width="300" height="58" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <circle cx="200" cy="135" r="13" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="200" y="140" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--primary)">2</text>
          <text x="224" y="131" fontSize="14" fontWeight="700" fill="var(--ink)">컨테이너가 살았나?</text>
          <text x="224" y="151" fontSize="12" fill="var(--muted)">docker ps — Up? Exited?</text>
          <line x1="470" y1="135" x2="484" y2="135" stroke="var(--line)" strokeWidth="1.5" />
          <text x="490" y="139" fontSize="12" fill="var(--muted)">우리 집 두꺼비집?</text>
          <line x1="320" y1="168" x2="320" y2="191" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="314,185 320,192 326,185" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="330" y="184" fontSize="12" fill="var(--muted)">살았다면</text>
          <rect x="170" y="200" width="300" height="58" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <circle cx="200" cy="229" r="13" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="200" y="234" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--primary)">3</text>
          <text x="224" y="225" fontSize="14" fontWeight="700" fill="var(--ink)">앱이 뭐라고 말하나?</text>
          <text x="224" y="245" fontSize="12" fill="var(--muted)">docker logs — 에러 메시지 확인</text>
          <line x1="470" y1="229" x2="484" y2="229" stroke="var(--line)" strokeWidth="1.5" />
          <text x="490" y="233" fontSize="12" fill="var(--muted)">이 방 스위치?</text>
          <line x1="320" y1="262" x2="320" y2="285" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="314,279 320,286 326,279" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="330" y="278" fontSize="12" fill="var(--muted)">앱은 멀쩡해 보이면</text>
          <rect x="170" y="294" width="300" height="58" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <circle cx="200" cy="323" r="13" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="200" y="328" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--primary)">4</text>
          <text x="224" y="319" fontSize="14" fontWeight="700" fill="var(--ink)">DB가 살았나?</text>
          <text x="224" y="339" fontSize="12" fill="var(--muted)">DB 컨테이너 상태 · 연결 에러 로그</text>
          <line x1="470" y1="323" x2="484" y2="323" stroke="var(--line)" strokeWidth="1.5" />
          <text x="490" y="327" fontSize="12" fill="var(--muted)">전구가 나갔나?</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 원인 좁히기 순서 — 바깥(서버)에서 안쪽(DB)으로, 정전 비유와 함께</p>
        <Code>{CODE_NARROWING}</Code>
        <p>
          우리 플랫폼(EC2 + Docker)에서 2번 단계의 핵심 명령이
          <span className="icode">docker ps</span>예요. 컨테이너들의 출석부라고 생각하면 됩니다.
        </p>
        <Code>{CODE_DOCKER_PS}</Code>
        <p>
          이 순서를 지키면 좋은 점이 하나 더 있어요 — <strong>중간에 멀쩡한 단계는 용의선상에서
          빠집니다</strong>. "사이트는 열리는데 로그인만 안 된다"면 서버·Caddy는 이미 무죄고,
          백엔드나 DB로 범위가 확 좁혀지죠. 탐정이 알리바이로 용의자를 지워 가는 것과 같아요.
        </p>
        <div className="tip">
          <b>직접 확인해 보기 ①</b> 내 PC에 Docker Desktop이 있다면 터미널에서
          <span className="icode">docker ps</span>를 쳐 보세요. 로컬에서 실습 컨테이너를 띄운 적이
          있다면 STATUS 열에 <span className="icode">Up ...</span>이 보일 거예요. 이어서
          <span className="icode">docker ps -a</span>로 죽어 있는(Exited) 컨테이너까지 확인해 보고,
          둘의 차이를 눈으로 비교해 보세요. Docker가 없다면 멘토와 함께 개발 서버에서 구경해도 좋아요.
        </div>
      </Section>

      <Section n={4} title="로그 읽기 — 서버가 남긴 일기장" sub="docker logs와 tail -f, 그리고 어디부터 볼 것인가">
        <p>
          로그는 서버가 매 순간 쓰는 <strong>일기장</strong>이에요. "14시 3분, DB에 연결하려 했는데
          거절당했다"처럼 자기가 겪은 일을 시각과 함께 전부 적어 둡니다. 장애의 원인은
          십중팔구 이 일기장 안에 있어요 — 문제는 일기가 하루에 수십만 줄이라는 것.
          그래서 <strong>어디부터 읽을지</strong>가 기술입니다.
        </p>
        <Code>{CODE_LOGS}</Code>
        <ol className="olist">
          <li><strong>레벨부터</strong> — <span className="icode">INFO</span>는 평범한 일기,
            <span className="icode">WARN</span>은 "좀 이상한데?", <span className="icode">ERROR</span>·
            <span className="icode">FATAL</span>이 비명이에요. 비명만 먼저 골라 읽으세요.</li>
          <li><strong>시각을 맞춰서</strong> — 장애가 14:03에 시작됐다면 14:03 <strong>직전</strong> 로그가
            결정적 단서예요. 사건 발생 직전 CCTV부터 돌려 보는 것과 같죠.</li>
          <li><strong>죽었다면 마지막 줄</strong> — 컨테이너가 죽어 있다면 마지막 몇 줄이 유언장입니다.
            <span className="icode">OutOfMemoryError</span>, <span className="icode">Connection refused</span>
            같은 사인(死因)이 거기 적혀 있어요.</li>
        </ol>
        <div className="tip">
          <b>직접 확인해 보기 ②</b> 로그 감각은 브라우저에서도 기를 수 있어요.
          지금 이 페이지에서 <span className="kbd">F12</span> → <span className="kbd">Console</span> 탭을
          열어 두고 플랫폼 여기저기를 돌아다녀 보세요. 프론트엔드의 로그가 흐르는 걸 볼 수 있습니다.
          빨간 글씨(에러)가 있다면 눌러서 메시지를 읽어 보세요 — 서버 로그를 읽는 눈과 똑같은 눈이에요.
        </div>
      </Section>

      <Section n={5} title="재시작의 정석 — 그리고 재시작으로 덮으면 안 되는 것" sub="껐다 켜기는 치료제이자, 증거인멸이 될 수도 있다">
        <p>
          "껐다 켜 보세요"는 농담 같지만 실제로 강력한 응급처치예요. 꼬인 메모리, 끊긴 연결,
          멈춘 프로세스가 재시작 한 번에 초기화되니까요. 하지만 정석에는 <strong>순서</strong>가 있습니다 —
          특히 <strong>재시작 전에 로그부터 저장</strong>하는 것. 재시작은 병을 고치는 동시에
          <strong> 현장을 치워 버리는</strong> 행동이라, 증거를 먼저 챙기지 않으면 원인을 영영 모르게 돼요.
        </p>
        <Code>{CODE_RESTART}</Code>
        <p>
          그리고 더 중요한 이야기 — <strong>재시작으로 덮으면 안 되는 것</strong>들이 있어요.
        </p>
        <ol className="olist">
          <li>
            <strong>반복되는 장애</strong> — 일주일에 세 번 죽어서 세 번 재시작했다면, 그건 해결이 아니라
            <strong> 미룸</strong>이에요. 열이 날 때마다 해열제만 먹고 원인 검사를 안 받는 것과 같죠.
            메모리 누수나 느린 쿼리 같은 진짜 병은 그대로 자라고 있습니다.
          </li>
          <li>
            <strong>데이터 문제</strong> — 데이터가 지워졌거나 꼬였다면 재시작은 <strong>아무것도</strong>
            해결하지 못해요. 오히려 복구에 쓸 수 있던 흔적이 사라질 수 있습니다. 이건 백업의 영역이에요(다음 섹션!).
          </li>
          <li>
            <strong>원인 불명인 채로 종료 선언</strong> — "재시작했더니 되네요, 끝!"은 금지.
            보고서에 최소한 "원인은 OOM으로 <strong>추정</strong>, 로그 확보함"까지는 남겨야
            다음 사람(그리고 미래의 나)이 이어서 조사할 수 있어요.
          </li>
        </ol>
        <div className="warn">
          <b>절대 하지 말 것</b> — 확인 안 된 상태에서 <span className="icode">docker compose down</span>처럼
          <strong> 전체를 내리는</strong> 명령이나, DB 컨테이너를 <strong>삭제</strong>하는 명령을 습관처럼
          치지 마세요. 컨테이너 하나 재시작과 전체 초기화는 반창고와 대수술만큼 달라요.
          수습 기간에는 재시작도 <strong>멘토에게 먼저 알리고</strong> 함께 진행하는 게 원칙입니다.
        </div>
      </Section>

      <Section n={6} title="백업 3-2-1 — 그리고 복구 '연습'의 중요성" sub="백업은 보험, 복구 연습은 보험금 청구 리허설">
        <p>
          재시작으로도 못 고치는 최악의 상황 — 데이터가 사라졌을 때 — 을 위한 마지막 안전망이
          <strong> 백업</strong>이에요. 백업의 세계에는 유명한 <strong>3-2-1 규칙</strong>이 있습니다.
        </p>
        <Code>{CODE_321}</Code>
        <p>
          왜 "다른 장소 1개"가 꼭 필요할까요? 백업 파일을 같은 서버에만 두면, 그 서버가 통째로
          죽는 순간 원본과 백업이 <strong>같이</strong> 사라지거든요. 일기장 복사본을 만들어 놓고
          같은 가방에 넣어 다니는 것과 같아요 — 가방을 잃어버리면 둘 다 끝이죠. 그래서 우리는
          EC2 밖의 S3에 사본을 하나 더 둡니다.
        </p>
        <svg className="diagram" viewBox="0 0 640 240" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="백업 3-2-1 규칙 도해: 복사본 3개를 두 가지 저장 장소에 두고 그중 하나는 원격 S3에 둔다">
          <text x="320" y="22" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">3 — 복사본은 항상 3개 (원본 1 + 백업 2)</text>
          <polyline points="40,42 40,36 600,36 600,42" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <rect x="24" y="48" width="348" height="120" rx="10" fill="none" stroke="var(--line)" strokeWidth="1.5" strokeDasharray="6 4" />
          <rect x="436" y="48" width="180" height="120" rx="10" fill="none" stroke="var(--teal)" strokeWidth="1.5" strokeDasharray="6 4" />
          <rect x="40" y="72" width="124" height="60" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="102" y="96" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">원본</text>
          <text x="102" y="118" textAnchor="middle" fontSize="12" fill="var(--muted)">EC2 PostgreSQL</text>
          <line x1="168" y1="102" x2="214" y2="102" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="214,96 221,102 214,108" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="196" y="90" textAnchor="middle" fontSize="12" fill="var(--muted)">pg_dump</text>
          <rect x="228" y="72" width="132" height="60" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="294" y="96" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">백업 1</text>
          <text x="294" y="118" textAnchor="middle" fontSize="12" fill="var(--muted)">매일 새벽 pg_dump</text>
          <line x1="364" y1="102" x2="435" y2="102" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="435,96 442,102 435,108" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="404" y="90" textAnchor="middle" fontSize="12" fill="var(--muted)">S3 업로드</text>
          <rect x="452" y="72" width="148" height="60" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="526" y="96" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">백업 2</text>
          <text x="526" y="118" textAnchor="middle" fontSize="12" fill="var(--muted)">S3에 올린 사본</text>
          <text x="198" y="154" textAnchor="middle" fontSize="12" fill="var(--muted)">저장 장소 ① — EC2 서버 디스크</text>
          <text x="526" y="154" textAnchor="middle" fontSize="12" fill="var(--teal)">저장 장소 ② — S3 (원격)</text>
          <text x="320" y="196" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">2 — 서로 다른 저장 장소 2곳 (EC2 디스크 + S3)</text>
          <text x="320" y="220" textAnchor="middle" fontSize="12" fill="var(--teal)">1 — 그중 1개는 반드시 원격(S3)에: 서버가 통째로 죽어도 살아남아요</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 백업 3-2-1 — 복사본 3개 · 저장 장소 2곳 · 그중 1개는 원격(S3)</p>
        <p>
          그런데 백업보다 더 자주 잊히는 게 <strong>복구 연습</strong>이에요. 백업 파일이 있다는 것과
          그걸로 <strong>실제로 되살릴 수 있다</strong>는 건 전혀 다른 문제입니다. 막상 복구하려니
          파일이 깨져 있거나, 명령어를 아무도 모르거나, 복구에 하루가 걸리는 경우가 현실에서 정말 많아요.
          소화기를 사 두는 것(백업)과 소화기 사용법을 연습해 두는 것(복구 연습)의 차이죠 —
          불이 난 뒤에 사용 설명서를 처음 읽으면 늦습니다.
        </p>
        <div className="tip">
          <b>복구 연습의 3가지 질문</b> — 우리 팀은 이 질문에 답할 수 있어야 해요.
          ① 가장 최근 백업은 <strong>언제</strong> 것인가? ② 복구하는 데 <strong>얼마나</strong> 걸리는가?
          ③ 백업 시점 이후의 데이터는 <strong>얼마나</strong> 잃는가? 분기에 한 번은 테스트용 DB에
          진짜로 복구해 보면서 세 숫자를 갱신하는 게 이상적입니다.
        </div>
      </Section>

      <Section n={7} title="장애 보고서 쓰는 법" sub="다섯 줄이면 충분하다 — 수습 과제 A2와 연결">
        <p>
          불을 다 껐다고 끝이 아니에요. 마지막 임무는 <strong>기록</strong>입니다. 장애 보고서는
          잘잘못을 따지는 반성문이 아니라, <strong>같은 불이 다시 나지 않게 하는 소방 일지</strong>예요.
          어썸데브에서는 다섯 줄 골격으로 씁니다.
        </p>
        <Code>{CODE_REPORT}</Code>
        <ol className="olist">
          <li><strong>사실과 추측을 구분하세요</strong> — 로그로 확인한 건 단정하고, 아닌 건
            "~로 추정"이라고 솔직하게. 틀린 단정 하나가 다음 사람을 엉뚱한 길로 보냅니다.</li>
          <li><strong>시각은 숫자로</strong> — "아까", "점심쯤" 대신 "14:03". 나중에 로그와
            대조할 수 있는 건 숫자뿐이에요.</li>
          <li><strong>재발 방지가 본론</strong> — ①~④는 과거 이야기, ⑤가 미래 이야기예요.
            "모니터링 추가", "백업 주기 단축"처럼 <strong>행동</strong>으로 적어야 합니다.</li>
        </ol>
        <div className="tip">
          <b>수습 과제 A2와 연결</b> — 수습 온보딩의 <strong>A2 보고 훈련</strong>에서 배운
          "상황 → 원인 → 조치 → 다음 계획" 구조, 기억나나요? 장애 보고서는 그 구조의 서버 버전이에요.
          A2에서 연습한 대로 쓰되, 시각과 로그라는 <strong>증거</strong>가 붙는다는 점만 다릅니다.
          다음 A2 보고 때 이 5줄 골격으로 한번 써서 멘토에게 피드백을 받아 보세요.
        </div>
      </Section>

      <Section n={8} title="우리 플랫폼 장애 대응 시나리오 연습" sub="소방 훈련처럼 — 실제 상황 전에 말로 리허설">
        <p>
          마지막은 실전 리허설이에요. 진짜 장애가 났을 때 침착할 수 있는 비결은 딱 하나 —
          <strong> 미리 머릿속으로 겪어 보는 것</strong>입니다. 소방 대피 훈련을 해 본 사람이
          실제 화재에서 덜 당황하는 것과 같아요. 아래 네 가지 시나리오를 읽고, 각각
          <strong> 내가 무엇을 어떤 순서로 할지</strong> 소리 내어 설명해 보세요.
        </p>
        <Code>{CODE_SCENARIO}</Code>
        <p>
          시나리오 B가 특히 좋은 연습이에요 — "일부만 안 된다"는 정보 자체가 이미
          <strong> 원인 좁히기(섹션 3)의 절반</strong>을 끝내 준다는 걸 몸으로 느낄 수 있거든요.
          그리고 시나리오 D는 섹션 5·6의 교훈이 한꺼번에 등장합니다: 재시작이 만능이 아니라는 것,
          그리고 백업이 왜 마지막 안전망인지.
        </p>
        <div className="tip">
          <b>멘토와 함께</b> — 넷 중 하나를 골라 멘토 앞에서 <strong>모의 장애 대응 브리핑</strong>을
          해 보세요. "먼저 docker ps로 확인하고요..."처럼 명령어까지 말로 짚으면서요.
          막히는 지점이 나오면 그 섹션으로 돌아가면 됩니다 — 말로 설명하다 막히는 곳이
          진짜 복습 포인트예요.
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>🚒 여기까지 왔다면</h3>
          <p className="muted">
            이제 여러분은 장애의 세 가지 신호를 구분하고, <strong>서버 → 컨테이너 → 로그 → DB</strong> 순서로
            원인을 좁히고, 증거를 남기며 재시작하고, 3-2-1 백업과 5줄 보고서까지 아는 사람이 됐어요.
            서버와 Docker가 아직 낯설다면 <Link to="/learn/server-anatomy"><strong>서버 해부</strong></Link> 코스를
            먼저 복습하고, 로그 속 DB 에러를 더 깊이 이해하고 싶다면{' '}
            <Link to="/learn/sql"><strong>SQL 첫걸음</strong></Link>으로 이어 가세요.
            그리고 다음 <strong>A2 보고</strong>에서 오늘 배운 5줄 골격을 꼭 한번 써 보기!
          </p>
        </div>
      </div>
    </div>
  );
}
