// 이 파일이 하는 일: "스토어 배포" 코스 — 다 만든 앱을 세상에 내놓는 마지막 관문
// (배포 전 준비 → 서명 → Google Play → App Store 심사 → 리젝 사유 → 버전 관리)을
// 웹 개발자의 눈으로 안내하는 정적 학습 페이지. 목표는 "앱 배포 전문가"가 아니라
// "우리 웹 배포(Caddy·Docker)와 뭐가 같고 뭐가 다른지 지형을 읽는" 수준이다.
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색은 여기서
// 하드코딩하지 않고 var(--primary/--teal/--amber/--ink/--line/--card/--muted/--rose)만 쓴다.
// 예제 코드는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

// ── 예제 코드 상수들 ──

const CODE_PREP = `배포 전 체크리스트 — "코드가 다 됐다"는 끝이 아니라 시작

□ 앱 아이콘     여러 해상도의 정사각 이미지
   - Android: 적응형 아이콘(앞면+배경 두 겹) + 512×512 Play 아이콘
   - iOS:     1024×1024 App Store 아이콘 (투명·둥근모서리 X — 애플이 알아서 깎음)
□ 스크린샷      실제 화면 캡처를 기기 크기별로
   - Play:      최소 2장 (폰/태블릿)
   - App Store: 아이폰·아이패드 크기별로 각각
□ 스토어 설명   앱 이름 · 짧은 설명 · 자세한 설명 · 카테고리 · 연령 등급
□ 개인정보 처리방침   URL 필수! (데이터를 조금이라도 모으면 반드시)
   - Play:  '데이터 보안(Data safety)' 설문 작성
   - Apple: 'App 개인정보 보호'(영양성분표처럼 생긴 라벨)

비유: 앱 = 상품, 스토어 = 백화점 매대.
      물건만 좋다고 진열되진 않아요 — 사진·설명·안전정보까지 갖춰야 매대에 오릅니다.`;

const CODE_SIGNING = `앱 서명 = 앱에 찍는 '인감도장' — 누가 만들었나 + 중간에 변조 안 됐나

왜 필요할까?
  1) 출처 증명    "이 앱은 진짜 그 개발자가 냈다"
  2) 위변조 탐지   누가 코드를 몰래 바꾸면 도장이 깨져서 설치가 막힘
  3) 업데이트 연결  새 버전도 '같은 도장'이어야 기존 앱을 덮어쓸 수 있음

# Android — keystore(도장) 만들기 (keytool은 JDK에 딸려 옴)
keytool -genkeypair -v -keystore mirim-release.keystore -alias mirim -keyalg RSA -keysize 2048 -validity 10000

# 이 keystore로 앱 번들(AAB)에 서명해서 업로드합니다.
# ⚠ keystore 파일과 비밀번호를 잃어버리면? 같은 앱을 업데이트할 수 없어요!
#    (Play App Signing을 켜면 구글이 '최종 서명키'를 대신 보관 → 한결 안전)

// iOS — 여긴 애플이 도장을 관리해 줍니다
//   인증서(Certificate) + 프로비저닝 프로파일(Provisioning Profile)을
//   애플 개발자 계정에서 발급받으면, Xcode가 빌드할 때 자동으로 서명해요.`;

const CODE_PLAY_FLOW = `Google Play 등록 흐름 — Play Console에서 차례로

0) 개발자 계정 등록   1회 $25 (평생, 갱신 없음)
1) 앱 만들기          이름 · 기본 언어 · 무료/유료
2) 스토어 등록정보     아이콘 · 스크린샷 · 설명 (섹션 1에서 준비한 것)
3) 콘텐츠 등급        설문지에 답하면 연령 등급이 자동 산정
4) 데이터 보안        어떤 개인정보를 왜 모으는지 신고
5) 앱 번들 업로드      .aab (Android App Bundle) 파일
                     └ 2021년 8월부터 '새 앱'은 AAB 필수!
                       구글이 기기마다 딱 맞는 APK를 만들어 배달해요
6) 테스트 트랙        내부 → 비공개 → 공개 테스트로 단계별 검증
7) 프로덕션 출시      검토를 통과하면 스토어에 공개

APK vs AAB: APK = 완성된 설치 파일 '한 덩어리'.
            AAB = '재료 묶음' → 구글이 기기별 최적 APK로 재조립(용량↓, 다운로드↑).`;

const CODE_APP_STORE_FLOW = `App Store 등록 흐름 — App Store Connect + Xcode

0) Apple Developer Program 가입    연 $99 (매년 갱신)
1) App Store Connect에서 앱 생성     번들 ID · 이름
2) Xcode로 빌드 → 업로드            (또는 Transporter 앱으로)
3) TestFlight 베타 테스트           내부/외부 테스터에게 먼저 배포
4) 메타데이터 입력                  스크린샷 · 설명 · 키워드 · 개인정보 라벨
5) 심사(App Review) 제출            ← 사람이 직접 앱을 실행해 검사!
6) 승인 → 출시                      수동 출시 or 승인 즉시 자동 출시

⏱ Play보다 대체로 더 오래, 더 깐깐합니다. 왜?
   애플은 '앱은 App Store로만 설치'하는 닫힌 생태계라,
   품질·보안·프라이버시를 사람이 문지기처럼 직접 지키기 때문이에요.`;

const CODE_REJECT = `심사에서 자주 미끄러지는 이유 (특히 App Store)

1) 크래시·버그      리뷰어 기기에서 앱이 죽음 → 바로 리젝
   → 내 폰이 아니라 여러 실기기에서 충분히 테스트하고 제출!
2) 정보 부실        로그인 필요한 앱인데 '데모 계정'을 안 줌 /
                   스크린샷이 실제 화면과 딴판
3) 개인정보 처리방침  URL이 없거나 깨졌거나 내용이 부실
4) 미완성·빈약       "웹사이트를 그냥 감싸기만" 한 앱
                   (Apple 가이드 4.2 '최소한의 기능' 위반)
5) 결제 우회         앱 안 디지털 상품을 애플 결제(IAP)를 안 거치고 팔기
                   (Apple 3.1.1)
6) 권한 남용         안 쓰는 위치·연락처 권한을 이유 없이 요구
7) 사칭·저작권       남의 브랜드/아이콘 도용, 똑같은 앱 도배(스팸)

교훈: 리젝은 '탈락'이 아니라 '보완 요청서'예요. 사유를 읽고 고쳐 재제출하면 됩니다.`;

const CODE_VERSION = `버전은 숫자 두 개로 관리해요 — 사람용 '이름' + 기계용 '번호'

              사람에게 보이는 이름            스토어가 세는 번호(내부)
Android       versionName "1.2.0"          versionCode 34  (정수, 올릴 때마다 +1)
iOS           CFBundleShortVersion "1.2.0"  빌드 번호 34

// build.gradle (Android) — 새로 올릴 때 반드시 손대는 두 줄
android {
  defaultConfig {
    versionCode 34          // 이전보다 '무조건' 커야 업로드가 통과돼요
    versionName "1.2.0"     // 사용자에게 보이는 버전
  }
}

시맨틱 버저닝(SemVer)   MAJOR.MINOR.PATCH
   1  .  2  .  0
   │     │     └ PATCH: 버그 수정 (호환 O)
   │     └────── MINOR: 기능 추가 (호환 O)
   └──────────── MAJOR: 큰 변경 (호환이 깨질 수 있음)

⚠ 스토어는 웹처럼 '즉시 롤백'이 어려워요. 잘못 낸 버전을 되돌리려면
   대개 고친 새 버전을 다시 올려 심사를 또 받아야 합니다. → 올리기 전에 테스트!`;

const CODE_COMPARE = `우리 웹 배포  vs  앱 스토어 배포 — 결정적 차이

                   우리 웹(React+Spring)         모바일 앱
────────────────  ──────────────────────────  ───────────────────────
배포 방법          git push → 빌드 → Docker     빌드+서명 → 스토어 업로드
                   → 서버 → Caddy(HTTPS)
문지기(심사)        없음 (내가 곧 결정권)          있음 (구글·애플이 검사)
반영 속도          배포 즉시 = 전원 최신           심사 대기 + 사용자가 설치해야
버전 파편화         URL 하나 → 모두 같은 버전       기기마다 설치된 버전이 제각각
되돌리기           이전 이미지로 즉시 롤백          새 버전 올려 다시 심사(느림)
비용              서버 비용                       Play $25(1회) · Apple $99(매년)

한 줄 요약: 웹은 '내 서버, 내 마음' — 앱은 '남의 백화점 매대'.
           그래서 앱엔 준비·서명·심사라는 관문이 하나 더 붙는 거예요.`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 (섹션 헤더 + 코드 블록) ──
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: 'Google Play' },
  { n: 4, label: 'App Store 심사' },
  { n: 5, label: '흔한 리젝' },
  { n: 6, label: '버전 관리' },
  { n: 7, label: '웹 배포와 비교' },
];

export default function AppStoreDeployPage() {
  return (
    <div>
      {/* 히어로: 코드가 끝난 지점에서 '세상에 내놓기'까지 */}
      <div className="hero">
        <div className="eyebrow">Course · 앱 개발</div>
        <h1>스토어 배포<br />— 앱을 세상에 내놓기</h1>
        <p>
          코드가 다 돌아간다고 끝이 아니에요. 앱은 <strong>구글·애플이라는 문지기</strong>가 지키는
          백화점 매대(스토어)에 올라가야 사람들 손에 닿습니다. 준비물부터 서명·등록·심사까지,
          <strong> 우리 웹 배포(Caddy·Docker)와 나란히 놓고</strong> 무엇이 같고 무엇이 다른지
          지형을 읽어 봅니다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 70분</span>
          <span className="chip">실습: 스토어 등록정보 초안</span>
          <span className="chip">선수 지식: 앱의 지형 · React 기초</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>를 요구합니다. 아이콘, 스크린샷, 설명 글, 그리고
          가장 자주 빠뜨리는 <strong>개인정보 처리방침</strong>까지요.
        </p>
        <Code>{CODE_PREP}</Code>
        <p>
          이 중 <strong>개인정보 처리방침(Privacy Policy)</strong>은 선택이 아니라 사실상
          의무예요. 로그인, 광고 ID, 위치, 심지어 에러 로그만 모아도 "개인정보 수집"에
          해당할 수 있고, 그때는 <span className="icode">https://...</span> 형태의
          <strong> 공개 URL</strong>로 방침을 올려 두고 스토어에 그 주소를 등록해야 합니다.
          우리 <Link to="/learn/wifi-security"><strong>보안 기초</strong></Link> 코스에서 배운
          "데이터는 함부로 모으지도, 흘리지도 않는다"는 원칙이 스토어 규칙으로 강제되는 셈이죠.
        </p>
        <div className="tip">
          <b>지금 해 볼 것</b> 우리 학습 플랫폼 화면 한 장을 골라 스토어 등록정보처럼 써 보세요.
          앱 이름 한 줄, 짧은 설명(30자 이내) 한 줄, 자세한 설명 3~4문장.
          막상 써 보면 "이 앱이 사용자에게 무엇을 주는가"를 한 문장으로 못 말한다는 걸
          깨닫게 되는데 — 그 깨달음이 이 실습의 진짜 목적이에요.
        </div>
      </Section>

      <Section n={2} title="앱 서명" sub="키스토어·인증서 — 앱에 찍는 '인감도장'">
        <p>
          웹에선 그냥 서버에 파일을 올리면 끝이지만, 앱은 설치 파일에 반드시
          <strong> 디지털 서명(signing)</strong>을 해야 해요. 서명은 앱에 찍는
          <strong> 인감도장</strong>이라고 생각하면 딱 맞습니다. 이 도장은 세 가지를 보장해요:
          누가 만들었는지(출처), 중간에 변조되지 않았는지(무결성), 그리고
          <strong> 나중에 올릴 업데이트가 같은 사람 것인지</strong>(연속성).
        </p>
        <Code>{CODE_SIGNING}</Code>
        <p>
          안드로이드에선 <span className="icode">keystore</span>라는 파일이 곧 도장이고,
          iOS에선 애플이 발급하는 <strong>인증서 + 프로비저닝 프로파일</strong>이 그 역할을
          합니다. 특히 안드로이드에서 <strong>keystore와 비밀번호는 목숨처럼 지켜야</strong> 해요 —
          잃어버리면 "같은 도장"을 다시 만들 수 없어서, 사용자들이 쓰고 있는 앱을
          영영 업데이트하지 못하게 됩니다.
        </p>
        <div className="warn">
          <b>왜 하필 도장이 하나여야 할까</b> 만약 아무 도장으로나 업데이트를 허용하면,
          공격자가 "같은 이름의 가짜 새 버전"을 만들어 사용자 폰의 진짜 앱을 덮어쓸 수 있어요.
          "같은 도장만 덮어쓰기 가능"이라는 규칙이 그걸 막는 자물쇠입니다. 그래서
          <span className="icode"> keystore</span>는 소스코드가 아니라 비밀(secret)로 다뤄
          Git에 절대 올리지 않아요 — 우리 백엔드가 <span className="icode">JWT_SECRET</span>을
          코드가 아니라 <span className="icode">.env</span>로 다루는 것과 똑같은 이치예요.
        </div>
      </Section>

      <Section n={3} title="Google Play 등록 흐름" sub="개발자 계정 → AAB 업로드 → 테스트 → 출시">
        <p>
          준비물과 도장이 갖춰졌으면 이제 매대에 올릴 차례예요. 안드로이드는
          <strong> Google Play Console</strong>이라는 웹 관리 화면에서 진행합니다.
          한 번만 내는 <strong>$25</strong>로 개발자 계정을 열고 나면, 아래 순서대로
          빈칸을 채워 나가는 방식이에요.
        </p>
        <Code>{CODE_PLAY_FLOW}</Code>
        <p>
          여기서 웹 개발자가 꼭 짚고 갈 개념이 <strong>AAB(Android App Bundle)</strong>예요.
          예전엔 <span className="icode">.apk</span> 한 덩어리를 올렸지만, 2021년부터 새 앱은
          <span className="icode"> .aab</span>로 올립니다. AAB는 '완성품'이 아니라
          <strong> 재료 묶음</strong>이라, 구글이 사용자의 기기(화면 크기·CPU 종류·언어)에 맞춰
          꼭 필요한 부분만 골라 APK를 재조립해 배달해요. 덕분에 다운로드 용량이 줄죠.
        </p>
        <div className="tip">
          <b>테스트 트랙이라는 안전벨트</b> Play는 곧바로 전 세계에 뿌리기 전에
          <strong> 내부 → 비공개 → 공개 테스트</strong> 단계를 줍니다. 우리 백엔드에
          <Link to="/learn/cicd"><strong>CI/CD</strong></Link>의 스테이징 환경이 있는 것과 같은
          발상이에요 — "진짜 사용자에게 가기 전에, 좁은 무대에서 먼저 확인한다."
        </div>
      </Section>

      <Section n={4} title="App Store 등록·심사" sub="왜 애플이 더 엄격한가">
        <p>
          iOS는 <strong>App Store Connect</strong>(웹 관리 화면)와 <strong>Xcode</strong>(맥 전용
          개발 도구)를 오가며 올립니다. 연 <strong>$99</strong>의 개발자 프로그램에 가입하고,
          빌드를 업로드한 뒤, <strong>TestFlight</strong>로 베타 테스트를 거쳐 심사에 제출해요.
          큰 흐름은 Play와 비슷하지만, 결정적으로 다른 한 가지가 있습니다.
        </p>
        <Code>{CODE_APP_STORE_FLOW}</Code>
        <p>
          바로 <strong>사람이 직접 심사</strong>한다는 점이에요. 애플 리뷰어가 실제로 앱을
          내려받아 실행해 보고, 가이드라인(App Review Guidelines)에 맞는지 하나하나 확인합니다.
          그래서 Play보다 <strong>더 오래 걸리고 더 깐깐</strong>해요. 이유는 애플의 생태계 구조에
          있습니다 — iOS 앱은 원칙적으로 <strong>App Store를 통해서만 설치</strong>되는
          닫힌 정원이라, 애플이 품질·보안·프라이버시의 문지기 역할을 스스로 떠안는 거예요.
        </p>
        <div className="tip">
          <b>닫힌 정원 vs 열린 마당</b> 안드로이드는 스토어 밖에서도 앱 설치(사이드로딩)가
          비교적 자유로운 '열린 마당'이고, iOS는 문이 하나뿐인 '닫힌 정원'이에요.
          문이 하나라 통제가 강한 대신, 그 문(심사)을 통과하는 문턱도 높습니다.
          어느 쪽이 옳다기보다 <strong>철학이 다른 것</strong> — 개발자는 두 세계의 규칙을
          모두 존중해야 하죠.
        </div>
      </Section>

      <Section n={5} title="심사 리젝, 흔한 사유" sub="리젝은 탈락이 아니라 '보완 요청서'">
        <p>
          첫 제출이 한 번에 통과되는 일은 흔치 않아요. 특히 App Store는 리젝(반려)이
          자주 일어나는데, 다행히 사유는 대체로 <strong>정해진 몇 가지 패턴</strong>이에요.
          미리 알면 대부분 피할 수 있습니다.
        </p>
        <Code>{CODE_REJECT}</Code>
        <p>
          이 중 초보가 가장 많이 걸리는 건 4번, <strong>"웹사이트를 그냥 감싸기만 한 앱"</strong>
          이에요. 애플은 이런 앱을 '최소한의 기능(4.2)'에 못 미친다고 봅니다 — 브라우저로 그
          사이트에 가면 되는데 굳이 앱일 이유가 없다는 거죠. 이건 우리가
          <Link to="/learn/pwa"><strong>PWA</strong></Link> 코스에서 다룬 경계와도 맞닿아 있어요:
          "웹을 앱처럼 쓰고 싶다"면 스토어 심사를 통과하려 억지 껍데기를 씌우기보다,
          애초에 PWA로 가는 게 정직한 선택일 때가 많습니다.
        </p>
        <div className="warn">
          <b>리젝을 대하는 태도</b> 리젝 사유서는 채점표가 아니라 <strong>수정 가이드</strong>예요.
          "우리 앱은 이래서 반려됐고, 이렇게 고치면 된다"를 담담히 읽고, 고쳐서 재제출하면 됩니다.
          이건 우리가 <Link to="/learn/debugging"><strong>디버깅</strong></Link>에서 배운 자세와
          똑같아요 — <strong>에러 메시지는 적이 아니라 단서</strong>다.
        </div>
      </Section>

      <Section n={6} title="업데이트·버전 관리" sub="사람용 이름 하나, 기계용 번호 하나">
        <p>
          앱은 한 번 내고 끝이 아니라 계속 고쳐 올리는 물건이에요. 그래서 <strong>버전</strong>을
          체계적으로 붙여야 합니다. 핵심은 숫자를 <strong>두 종류</strong>로 관리한다는 점 —
          하나는 사용자에게 보여 주는 <strong>이름</strong>(예: 1.2.0), 다른 하나는 스토어가
          "이게 더 새 거야?"를 판단하는 <strong>내부 번호</strong>예요.
        </p>
        <Code>{CODE_VERSION}</Code>
        <p>
          사람용 이름은 <strong>시맨틱 버저닝(SemVer)</strong> 규칙을 따르는 게 관례예요.
          <span className="icode"> MAJOR.MINOR.PATCH</span> — 버그만 고치면 맨 뒤 PATCH를,
          기능을 더하면 가운데 MINOR를, 크게 갈아엎어 호환이 깨지면 맨 앞 MAJOR를 올립니다.
          우리 백엔드 라이브러리(예: <span className="icode">Spring Boot 4.0.7</span>)의 버전도
          정확히 이 규칙으로 붙어 있어요 — 낯설어 보이던 숫자가 사실은 약속이었던 거죠.
        </p>
        <div className="warn">
          <b>웹과 가장 다른 지점, 롤백</b> 우리 웹은 문제가 생기면 이전 Docker 이미지로
          <strong> 몇 초 만에 되돌립니다</strong>. 하지만 스토어에 잘못 올린 버전은 그렇게
          즉시 못 물려요 — 대개 <strong>고친 새 버전을 다시 올려 심사를 또 받아야</strong> 합니다.
          그래서 앱 세계에선 "올리기 전 테스트"의 무게가 웹보다 훨씬 무거워요.
          <strong> 단계적 출시(staged rollout)</strong>로 5% → 20% → 100%처럼 조금씩 푸는 이유도
          바로 이 되돌리기의 어려움 때문입니다.
        </div>
      </Section>

      <Section n={7} title="우리 웹 배포와 비교하며 마무리" sub="Caddy·Docker vs 스토어 — 지형을 한눈에">
        <p>
          마지막으로 이 코스 전체를, 우리가 실제로 쓰는 <strong>웹 배포</strong>와 나란히 놓아
          봅시다. 우리 플랫폼은 코드를 <span className="icode">git push</span>하면 빌드되어
          <strong> Docker</strong> 이미지로 서버에 올라가고, <strong>Caddy</strong>가 HTTPS로
          바깥에 내보내죠. 여기엔 <strong>문지기(심사)가 없어요</strong> — 배포하는 순간
          모든 사용자가 즉시 같은 최신 버전을 봅니다.
        </p>
        <Code>{CODE_COMPARE}</Code>
        <svg className="diagram" viewBox="0 0 640 384" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="우리 웹 배포와 앱 스토어 배포의 파이프라인 비교. 왼쪽 웹은 git push부터 Caddy HTTPS까지 문지기 없이 흘러 전원이 즉시 최신이 되고, 오른쪽 앱은 빌드와 서명 뒤 스토어 업로드, 심사라는 게이트를 통과해 승인된 다음에도 사용자가 직접 설치해야 하므로 기기마다 버전이 제각각이 된다.">
          <text x="165" y="26" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--primary)">우리 웹 배포</text>
          <text x="475" y="26" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">앱 스토어 배포</text>

          {/* 왼쪽: 웹 파이프라인 */}
          <rect x="80" y="48" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="165" y="71" textAnchor="middle" fontSize="12.5" fill="var(--ink)">git push</text>
          <line x1="165" y1="86" x2="165" y2="102" stroke="var(--teal)" strokeWidth="2" />
          <path d="M159 101 L171 101 L165 108 Z" fill="var(--teal)" />
          <rect x="80" y="108" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="165" y="131" textAnchor="middle" fontSize="12.5" fill="var(--ink)">빌드 · Docker</text>
          <line x1="165" y1="146" x2="165" y2="162" stroke="var(--teal)" strokeWidth="2" />
          <path d="M159 161 L171 161 L165 168 Z" fill="var(--teal)" />
          <rect x="80" y="168" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="165" y="191" textAnchor="middle" fontSize="12.5" fill="var(--ink)">서버 배포</text>
          <line x1="165" y1="206" x2="165" y2="222" stroke="var(--teal)" strokeWidth="2" />
          <path d="M159 221 L171 221 L165 228 Z" fill="var(--teal)" />
          <rect x="80" y="228" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="165" y="251" textAnchor="middle" fontSize="12.5" fill="var(--ink)">Caddy · HTTPS</text>
          <line x1="165" y1="266" x2="165" y2="282" stroke="var(--teal)" strokeWidth="2" />
          <path d="M159 281 L171 281 L165 288 Z" fill="var(--teal)" />
          <rect x="80" y="288" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="2" />
          <text x="165" y="311" textAnchor="middle" fontSize="12.5" fill="var(--ink)">전원 즉시 최신</text>
          <text x="165" y="352" textAnchor="middle" fontSize="12" fill="var(--muted)">문지기 없음 · 롤백도 즉시</text>

          {/* 오른쪽: 앱 파이프라인 */}
          <rect x="390" y="48" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="475" y="71" textAnchor="middle" fontSize="12.5" fill="var(--ink)">빌드 · 서명</text>
          <line x1="475" y1="86" x2="475" y2="102" stroke="var(--line)" strokeWidth="2" />
          <path d="M469 101 L481 101 L475 108 Z" fill="var(--line)" />
          <rect x="390" y="108" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="475" y="131" textAnchor="middle" fontSize="12.5" fill="var(--ink)">스토어 업로드</text>
          <line x1="475" y1="146" x2="475" y2="162" stroke="var(--line)" strokeWidth="2" />
          <path d="M469 161 L481 161 L475 168 Z" fill="var(--line)" />
          <rect x="390" y="168" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--amber)" strokeWidth="2" />
          <text x="475" y="191" textAnchor="middle" fontSize="12.5" fill="var(--ink)">심사 (사람이 검사)</text>
          <text x="574" y="182" textAnchor="start" fontSize="11" fill="var(--amber)">← 게이트</text>
          <text x="574" y="196" textAnchor="start" fontSize="11" fill="var(--amber)">여기서 대기</text>
          <line x1="475" y1="206" x2="475" y2="222" stroke="var(--line)" strokeWidth="2" />
          <path d="M469 221 L481 221 L475 228 Z" fill="var(--line)" />
          <rect x="390" y="228" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="475" y="251" textAnchor="middle" fontSize="12.5" fill="var(--ink)">승인 · 출시</text>
          <line x1="475" y1="266" x2="475" y2="282" stroke="var(--line)" strokeWidth="2" />
          <path d="M469 281 L481 281 L475 288 Z" fill="var(--line)" />
          <rect x="390" y="288" width="170" height="36" rx="8" fill="var(--card)" stroke="var(--rose)" strokeWidth="2" />
          <text x="475" y="311" textAnchor="middle" fontSize="12.5" fill="var(--ink)">사용자가 직접 설치</text>
          <text x="475" y="352" textAnchor="middle" fontSize="12" fill="var(--muted)">버전이 기기마다 제각각</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 웹은 문지기 없이 흘러 전원이 즉시 최신, 앱은 심사 게이트를 지나도 사용자가 설치해야 최신이 된다</p>
        <p>
          정리하면 이래요. 웹은 <strong>내 서버, 내 마음</strong> — 배포도 롤백도 즉시고,
          모두가 같은 버전을 봅니다. 앱은 <strong>남의 백화점 매대</strong> — 준비물·서명·심사라는
          관문을 지나야 하고, 통과해도 사용자가 각자 업데이트를 받아야 최신이 되죠.
          더 번거로워 보이지만, 그 관문이 곧 <strong>품질과 신뢰의 안전장치</strong>이기도 해요.
        </p>
        <div className="tip">
          <b>이 코스의 진짜 목표</b> 여러분이 이번 수습에서 앱 배포를 직접 다 해 볼 일은
          없을 거예요. 그래도 이 지형을 알아 두면, 나중에 "우리도 앱 낼까?"라는 대화에서
          <strong> 비용·시간·제약을 현실적으로 가늠</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>
            개발자예요. 그 지도와, 지금 손에 익힌 React·Spring·Docker·보안 지식이면
            필요할 때 스스로 다음 한 걸음을 뗄 수 있습니다. 그동안 쌓은 코스들을 다시 둘러보고
            싶다면 언제든 <Link to="/learn"><strong>학습 센터</strong></Link>로 —
            수습 8주가 곧 여러분의 포트폴리오가 될 거예요.
          </p>
        </div>
      </div>
    </div>
  );
}
