// 이 파일이 하는 일: "클라우드 네트워크 입문" 코스 — 데이터센터·리전에서 출발해
// VPC·서브넷·탄력적 IP·로드밸런서를 거쳐, 우리 플랫폼의 실제 네트워크 구성도와
// CDN까지 7개 섹션으로 안내하는 정적 학습 페이지.
// (리전/가용영역 → VPC → 서브넷 → 탄력적 IP → 로드밸런서 → 우리 구성도 총정리 → CDN)
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색을 여기서 하드코딩하지 않는다.
// 텍스트 다이어그램은 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

// ── 텍스트 다이어그램 · 표 상수들 ──

const CODE_REGION_AZ = `AWS의 세계 지도 — 리전(Region)과 가용영역(AZ)

리전 = 도시 단위의 '데이터센터 캠퍼스' 묶음
가용영역(AZ) = 그 안의 독립된 건물(데이터센터) 그룹

  ap-northeast-2 (서울 리전)  ← 우리 서버가 사는 곳!
  ├── ap-northeast-2a  ┐
  ├── ap-northeast-2b  │ 서로 떨어진 별개 건물들.
  ├── ap-northeast-2c  │ 한 곳이 정전·화재가 나도
  └── ap-northeast-2d  ┘ 나머지는 멀쩡하도록 설계

다른 리전 예) us-east-1(미국 버지니아), ap-northeast-1(도쿄)

→ 서울 리전을 고른 이유: 사용자가 한국에 있으니
  물리적으로 가까울수록 응답이 빨라요 (빛도 이동 시간이 걸립니다!)`;

const CODE_VPC = `VPC(Virtual Private Cloud) = 클라우드 속에 그린 '내 네트워크'

AWS 데이터센터라는 거대한 아파트 단지 안에,
울타리를 치고 "여기부터는 어썸데브 땅"이라고 선언한 구역이에요.

┌─ AWS 서울 리전 ──────────────────────────────┐
│                                              │
│  ┌─ 우리 회사 VPC (10.0.0.0/16) ──────────┐  │
│  │                                        │  │
│  │   울타리 안에서는 우리가 정한 규칙대로    │  │
│  │   IP를 나눠 쓰고, 문(게이트웨이)을 통해   │  │
│  │   서만 바깥과 통해요                     │  │
│  └────────────────────────────────────────┘  │
│                                              │
│  (다른 회사들의 VPC — 서로 절대 안 보임)       │
└──────────────────────────────────────────────┘

10.0.0.0/16 → "10.0.으로 시작하는 주소 65,536개를 내 땅으로 쓸게"
지난 코스의 공유기(192.168.x.x)가 만든 집 네트워크의 회사 버전이라
생각하면 딱 맞아요.`;

const CODE_SUBNET = `서브넷 = VPC라는 땅을 용도별로 나눈 '구역'

┌─ VPC (10.0.0.0/16) ─────────────────────────────┐
│                                                 │
│  ┌─ 퍼블릭 서브넷 (10.0.1.0/24) ─────────────┐   │
│  │  가게가 늘어선 '대로변'                     │   │
│  │  인터넷 게이트웨이(정문)와 연결됨            │   │
│  │  예) 웹 서버, Caddy — 손님을 맞아야 하는 것  │   │
│  └───────────────────────────────────────────┘   │
│                                                 │
│  ┌─ 프라이빗 서브넷 (10.0.2.0/24) ───────────┐   │
│  │  외부인 출입 금지 '직원 전용 구역'           │   │
│  │  인터넷에서 직접 못 들어옴                  │   │
│  │  예) 데이터베이스 — 절대 노출되면 안 되는 것 │   │
│  └───────────────────────────────────────────┘   │
└─────────────────────────────────────────────────┘

핵심 원칙: 손님(인터넷)을 맞을 것만 대로변에,
금고(DB)는 안쪽 방에. 은행 창구와 금고실의 관계예요.`;

const CODE_EIP = `탄력적 IP(Elastic IP) = 서버에 붙이는 '평생 전화번호'

문제 상황:
  EC2 서버는 껐다 켜면 공인 IP가 바뀔 수 있어요.
  → 도메인이 가리키던 주소가 어긋남 → 사이트 먹통!

해결:
  탄력적 IP를 하나 빌려서 서버에 '고정'으로 붙여요.
  서버를 재부팅해도, 심지어 새 서버로 갈아타도
  IP를 떼서 새 서버에 다시 붙이면 끝.

  우리 플랫폼의 탄력적 IP: 3.36.160.246

  edu.awesomedevapp.com ──(DNS)──> 3.36.160.246 ──> 우리 EC2

비유: 휴대폰(서버)을 바꿔도 번호이동으로
전화번호(탄력적 IP)는 그대로 가져가는 것과 같아요.`;

const CODE_NSLOOKUP_EIP = `# 우리 도메인이 정말 그 탄력적 IP를 가리키는지 직접 확인:
nslookup edu.awesomedevapp.com

Name:    edu.awesomedevapp.com
Address: 3.36.160.246   ← 위에서 배운 그 주소가 나오나요?

# 한 발 더: 그 IP까지 몇 개의 환승역을 거치는지도 보세요.
tracert 3.36.160.246
# 마지막 홉이 AWS 서울 리전의 우리 서버입니다.`;

const CODE_LB = `로드밸런서(Load Balancer) = 서버 여러 대 앞의 '번호표 기계'

서버 1대일 때:                손님이 몰리면?
[사용자들] → [서버]           줄이 길어지고, 서버가 지쳐 쓰러짐

로드밸런서를 두면:
                    ┌→ [서버 A]
[사용자들] → [LB] ──┼→ [서버 B]   요청을 골고루 분배!
                    └→ [서버 C]

로드밸런서가 하는 일:
1) 분배 — 요청을 여러 서버에 나눠 보냄 (은행 번호표 기계)
2) 헬스체크 — "살아 있니?" 주기적으로 확인,
   쓰러진 서버는 명단에서 빼고 나머지로 배분
3) 무중단 배포의 열쇠 — 서버를 한 대씩 빼서 업데이트해도
   나머지가 손님을 받으니 서비스가 안 끊겨요

우리 플랫폼은 아직 서버 1대라 LB가 없어요.
대신 Caddy가 '건물 안의 작은 안내데스크' 역할로
컨테이너들에게 요청을 나눠 주고 있죠. 사용자가 늘면
LB를 앞에 세우는 게 자연스러운 다음 단계입니다.`;

const CODE_OUR_STACK = `우리 학습 플랫폼의 실제 네트워크 구성 — 총정리!

[여러분의 브라우저]
      │  ① "edu.awesomedevapp.com 주세요"
      ▼
[DNS]  이름 → 3.36.160.246 (탄력적 IP) 로 번역
      │  ② HTTPS 요청 출발
      ▼
━━━━━ AWS 서울 리전 (ap-northeast-2) ━━━━━
┌─ VPC (어썸데브의 울타리) ──────────────────┐
│  ┌─ 퍼블릭 서브넷 ────────────────────────┐ │
│  │ [EC2 서버] ← 탄력적 IP 3.36.160.246   │ │
│  │    │                                  │ │
│  │    ▼ ③ 443 포트로 도착                │ │
│  │ [Caddy] 현관 안내데스크                 │ │
│  │    │  HTTPS 암호 해제 + 길 안내        │ │
│  │    ├──> [React 프론트]  화면 담당       │ │
│  │    ├──> [Spring Boot]   API·로직 담당  │ │
│  │    │        │                         │ │
│  │    │        ▼ (Docker 내부 네트워크)   │ │
│  │    │    [PostgreSQL]  데이터 금고      │ │
│  │    └──> [Gitea]  우리 코드 저장소       │ │
│  └───────────────────────────────────────┘ │
└────────────────────────────────────────────┘

컨테이너들은 Docker의 내부 네트워크로만 서로 대화해요.
PostgreSQL은 바깥(인터넷)에서 직접 두드릴 수 없습니다 —
프라이빗 서브넷 개념을 Docker 네트워크로 실현한 셈이에요.`;

const CODE_CDN = `CDN(Content Delivery Network) = 전국(전 세계) 편의점 체인

문제: 브라질 사용자가 서울 서버의 이미지를 받으면?
      지구 반대편 → 왕복 수백 ms, 매번 느림

해결: 세계 곳곳의 '중간 창고(엣지 서버)'에 복사본을 미리 배치

[서울 원본 서버] ──복사──> [도쿄 엣지] [미국 엣지] [유럽 엣지]...

브라질 사용자 → 가장 가까운 엣지에서 받음 → 훨씬 빠름!

편의점 비유: 본사 공장(원본 서버)이 서울에 있어도,
동네 편의점(엣지)에 물건을 미리 깔아 두면
어디서든 걸어가서 바로 사죠.

무엇을 CDN에 두나? 자주 바뀌지 않는 것들 —
이미지, 동영상, JS/CSS 파일. (개인 학습 기록처럼
매번 달라지는 데이터는 원본 서버가 만들어야 해요.)

우리 플랫폼은 사용자가 한국에 몰려 있어 아직 CDN이 없지만,
유튜브·넷플릭스가 전 세계에서 끊김 없이 도는 비결이 바로 이거예요.`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: 'VPC' },
  { n: 3, label: '퍼블릭/프라이빗 서브넷' },
  { n: 4, label: '탄력적 IP' },
  { n: 5, label: '로드밸런서 한 입' },
  { n: 6, label: '우리 플랫폼 구성도' },
  { n: 7, label: 'CDN 맛보기' },
];

export default function CloudNetworkPage() {
  return (
    <div>
      {/* 히어로: 이 코스가 어디서 출발해 어디까지 가는지 */}
      <div className="hero">
        <div className="eyebrow">Course · 네트워크</div>
        <h1>클라우드 네트워크 입문<br />— 우리 서버는 어디에, 어떻게 살고 있을까</h1>
        <p>
          "서버가 클라우드에 있다"는 말은 사실 <strong>지구 어딘가의 진짜 데이터센터</strong>에
          있다는 뜻이에요. 우리 플랫폼이 사는 AWS 서울 리전에서 출발해, VPC라는 울타리와
          서브넷이라는 구역을 지나 여러분의 브라우저까지 — 실제 우리 서비스의 네트워크를
          지도를 그리듯 따라가 봅니다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 50분</span>
          <span className="chip">실습 2개</span>
          <span className="chip">선수 코스: 네트워크의 이해</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> — 서버 수만 대가 랙에 꽂혀 웅웅 돌아가는 거대한
          건물이에요. AWS 같은 클라우드 회사는 이런 건물을 전 세계에 지어 두고
          "서버 필요하면 우리 건물 것 빌려 쓰세요" 하고 임대업을 하는 겁니다.
          PC방에 가면 내 컴퓨터 없이도 게임을 할 수 있는 것처럼, 우리는 서버를
          사지 않고 빌려 써요.
        </p>
        <p>
          이 건물들은 <strong>리전</strong>(도시 단위 묶음)과 <strong>가용영역</strong>(그 안의
          독립 건물 그룹)으로 조직돼요. 우리 플랫폼은 <strong>서울 리전(ap-northeast-2)</strong>의
          EC2 서버에서 돌아갑니다.
        </p>
        <Code>{CODE_REGION_AZ}</Code>
        <p>
          가용영역이 여러 개인 이유는 <strong>계란을 한 바구니에 담지 않기</strong> 위해서예요.
          한 건물에 불이 나도 몇 km 떨어진 다른 건물은 멀쩡하니까, 중요한 서비스는
          여러 가용영역에 복사본을 두고 운영합니다.
        </p>
      </Section>

      <Section n={2} title="VPC — 클라우드 속 내 네트워크" sub="거대한 단지 안에 울타리 치기">
        <p>
          데이터센터에는 수천 개 회사의 서버가 함께 살아요. 그런데 우리 서버와 옆 회사
          서버가 아무렇게나 통신할 수 있다면 큰일이겠죠? 그래서 클라우드에서는 회사마다
          <strong> VPC</strong>(Virtual Private Cloud)라는 <strong>가상의 울타리</strong>를
          쳐 줍니다. 같은 건물을 쓰지만 서로의 존재가 전혀 보이지 않는, 완전히 분리된
          '내 네트워크'예요.
        </p>
        <Code>{CODE_VPC}</Code>
        <p>
          지난 <Link to="/learn/network"><strong>네트워크의 이해</strong></Link> 코스에서
          공유기가 집 안에 <span className="icode">192.168.x.x</span> 사설 네트워크를
          만들어 주는 걸 봤죠? VPC는 그것의 <strong>회사·클라우드 버전</strong>입니다.
          울타리 안 IP 대역을 우리가 정하고, 바깥과는 <strong>게이트웨이</strong>라는
          정문을 통해서만 통해요. 개념이 정확히 평행이라, 하나를 이해하면 둘 다 이해한 거예요.
        </p>
      </Section>

      <Section n={3} title="퍼블릭 서브넷 vs 프라이빗 서브넷" sub="대로변 가게와 직원 전용 구역">
        <p>
          VPC라는 땅이 생겼으면, 이제 <strong>구역 정리</strong>를 해요. 땅을 몇 개의
          <strong> 서브넷</strong>(부분 네트워크)으로 나누고, 각 구역에 어울리는 서버를
          배치합니다. 핵심 구분은 딱 하나 — <strong>인터넷에서 직접 들어올 수 있느냐</strong>예요.
        </p>
        <Code>{CODE_SUBNET}</Code>
        <ol className="olist">
            <li>
              <strong>퍼블릭 서브넷</strong> — 인터넷 게이트웨이(정문)와 연결된 대로변.
              웹 서버처럼 <strong>손님(사용자)을 직접 맞아야 하는 것</strong>만 여기에 둬요.
            </li>
            <li>
              <strong>프라이빗 서브넷</strong> — 정문과 이어지지 않은 안쪽 구역. 데이터베이스처럼
              <strong> 절대 외부에 노출되면 안 되는 것</strong>을 숨겨 둡니다. 해커가 인터넷에서
              아무리 두드려도 애초에 길이 없어요.
            </li>
          </ol>
        <svg className="diagram" viewBox="0 0 640 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="퍼블릭 서브넷과 프라이빗 서브넷 구조: 인터넷에서 게이트웨이(정문)를 지나 퍼블릭 서브넷의 웹 서버까지만 길이 있고, 프라이빗 서브넷의 데이터베이스로 가는 길은 없다">
          <rect x="196" y="24" width="420" height="260" rx="10" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <text x="210" y="46" fontSize="12.5" fontWeight="600" fill="var(--ink)">VPC (10.0.0.0/16)</text>
          <rect x="216" y="58" width="380" height="96" rx="8" fill="none" stroke="var(--teal)" strokeDasharray="5 4" />
          <text x="230" y="78" fontSize="12" fill="var(--teal)">퍼블릭 서브넷 (10.0.1.0/24) — 대로변</text>
          <rect x="260" y="92" width="150" height="48" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="335" y="111" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">웹 서버 · Caddy</text>
          <text x="335" y="130" fontSize="12" fill="var(--muted)" textAnchor="middle">손님을 맞는 곳</text>
          <rect x="216" y="178" width="380" height="92" rx="8" fill="none" stroke="var(--amber)" strokeDasharray="5 4" />
          <text x="230" y="198" fontSize="12" fill="var(--amber)">프라이빗 서브넷 (10.0.2.0/24) — 안쪽 방</text>
          <rect x="260" y="212" width="150" height="48" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="335" y="231" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">데이터베이스</text>
          <text x="335" y="250" fontSize="12" fill="var(--muted)" textAnchor="middle">금고 — 노출 금지</text>
          <rect x="28" y="90" width="104" height="52" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="80" y="112" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">인터넷</text>
          <text x="80" y="130" fontSize="12" fill="var(--muted)" textAnchor="middle">손님들</text>
          <rect x="158" y="92" width="78" height="48" rx="8" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="197" y="112" fontSize="12" fontWeight="600" fill="var(--ink)" textAnchor="middle">게이트웨이</text>
          <text x="197" y="129" fontSize="12" fill="var(--muted)" textAnchor="middle">정문</text>
          <line x1="132" y1="116" x2="150" y2="116" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="144,111 152,116 144,121" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <line x1="236" y1="116" x2="252" y2="116" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="246,111 254,116 246,121" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <line x1="335" y1="140" x2="335" y2="204" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="330,198 335,206 340,198" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <text x="345" y="170" fontSize="12" fill="var(--muted)">내부에서만 접근</text>
          <polyline points="80,142 80,236 204,236" fill="none" stroke="var(--rose)" strokeWidth="1.5" strokeDasharray="4 4" />
          <line x1="210" y1="230" x2="222" y2="242" stroke="var(--rose)" strokeWidth="2" strokeLinecap="round" />
          <line x1="222" y1="230" x2="210" y2="242" stroke="var(--rose)" strokeWidth="2" strokeLinecap="round" />
          <text x="90" y="200" fontSize="12" fill="var(--rose)">길이 없음!</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 손님은 정문을 지나 대로변의 웹 서버까지만 — 금고(DB)로 가는 길은 애초에 없어요</p>
        <div className="warn">
          <b>실무에서 가장 흔한 보안 사고 중 하나</b>가 DB를 퍼블릭에 두는 거예요.
          "은행 금고를 길가에 내놓은 것"과 같습니다. <strong>손님 맞을 것만 대로변에,
          귀중품은 안쪽에</strong> — 이 원칙 하나만 기억해도 사고의 절반은 막아요.
        </div>
      </Section>

      <Section n={4} title="탄력적 IP — 서버의 평생 전화번호" sub="우리 서버의 주소, 3.36.160.246">
        <p>
          클라우드 서버(EC2)는 껐다 켜면 <strong>공인 IP가 바뀔 수 있어요</strong>.
          빌린 자취방을 옮길 때마다 주소가 바뀌는 것과 같죠. 그런데 도메인은 특정 IP를
          가리키고 있으니, IP가 바뀌면 <strong>도메인이 허공을 가리키는</strong> 사고가 납니다.
        </p>
        <Code>{CODE_EIP}</Code>
        <p>
          그래서 우리 플랫폼은 <strong>탄력적 IP(Elastic IP)</strong>를 하나 빌려서 EC2에
          붙여 뒀어요. 그게 바로 <span className="icode">3.36.160.246</span> — 서버를
          재부팅해도, 언젠가 더 큰 서버로 이사해도 이 번호는 그대로 가져갑니다.
          휴대폰을 바꿔도 번호이동으로 전화번호를 유지하는 것과 같아요.
        </p>
        <div className="tip">
          <b>직접 확인해 보기</b> 터미널에서 우리 도메인이 정말{' '}
          <span className="icode">3.36.160.246</span>을 가리키는지 DNS에 직접 물어보세요.
          지난 코스에서 배운 <span className="icode">nslookup</span>과{' '}
          <span className="icode">tracert</span>가 여기서 다시 등장합니다!
        </div>
        <Code>{CODE_NSLOOKUP_EIP}</Code>
      </Section>

      <Section n={5} title="로드밸런서 한 입" sub="손님이 몰리면 번호표 기계가 필요해요">
        <p>
          서버 한 대가 감당 못 할 만큼 사용자가 늘면 어떻게 할까요? 더 비싼 서버로
          바꾸는 방법(수직 확장)도 있지만, 한계가 있어요. 그래서 실무에서는
          <strong> 같은 서버를 여러 대 두고 요청을 나눠 받게</strong> 합니다(수평 확장).
          이때 맨 앞에서 교통정리를 하는 장치가 <strong>로드밸런서</strong>예요.
        </p>
        <Code>{CODE_LB}</Code>
        <p>
          은행에 창구가 하나뿐이면 줄이 끝없이 길어지지만, 번호표 기계가 손님을
          여러 창구로 나눠 주면 전체가 빨라지죠. 게다가 한 창구 직원이 자리를 비워도
          (서버 장애·업데이트) 나머지 창구가 손님을 받으니 <strong>서비스가 멈추지 않아요</strong> —
          이게 여러분이 쓰는 큰 서비스들이 "점검 중" 없이 업데이트되는 비결입니다.
        </p>
      </Section>

      <Section n={6} title="우리 플랫폼의 실제 구성도 — 총정리" sub="1~5섹션을 한 장의 지도로">
        <p>
          이제 배운 조각을 전부 이어 붙일 시간이에요. 여러분이 지금 이 페이지를 보기까지,
          요청이 실제로 지나온 길 전체입니다. <strong>이 다이어그램 한 장을 그릴 수 있으면
          이 코스는 통과</strong>예요.
        </p>
        <Code>{CODE_OUR_STACK}</Code>
        <p>
          눈여겨볼 포인트: <strong>Caddy</strong>는 밖을 향해 열린 유일한 현관이고,
          <strong> PostgreSQL</strong>은 Docker 내부 네트워크에만 있어서 인터넷에서 직접
          접근할 수 없어요. 섹션 3에서 배운 "손님 맞을 것만 대로변에, 금고는 안쪽에"
          원칙이 우리 서버 안에서 실제로 작동하고 있는 겁니다.
        </p>
        <svg className="diagram" viewBox="0 0 640 420" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="우리 플랫폼 네트워크 구성도: 브라우저가 DNS로 도메인을 3.36.160.246으로 번역한 뒤 HTTPS 요청이 AWS 서울 리전 EC2의 Caddy에 도착하고, Caddy가 React 프론트·Spring Boot·Gitea로 분배하며, Spring Boot만 Docker 내부망으로 PostgreSQL과 통신한다">
          <rect x="36" y="16" width="168" height="50" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="120" y="46" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">여러분의 브라우저</text>
          <rect x="436" y="16" width="168" height="50" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="520" y="37" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">DNS</text>
          <text x="520" y="55" fontSize="12" fill="var(--muted)" textAnchor="middle">이름 → IP 번역</text>
          <line x1="204" y1="41" x2="428" y2="41" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="420,36 429,41 420,46" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <text x="316" y="30" fontSize="12" fill="var(--muted)" textAnchor="middle">① edu.awesomedevapp.com은 어디?</text>
          <text x="316" y="58" fontSize="12" fontWeight="600" fill="var(--primary)" textAnchor="middle">3.36.160.246</text>
          <line x1="100" y1="66" x2="100" y2="170" stroke="var(--primary)" strokeWidth="1.5" />
          <polyline points="95,162 100,171 105,162" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="112" y="96" fontSize="12" fill="var(--muted)">② HTTPS 요청 (443 포트)</text>
          <rect x="24" y="104" width="592" height="306" rx="10" fill="none" stroke="var(--line)" strokeDasharray="6 4" />
          <text x="602" y="124" fontSize="12" fill="var(--muted)" textAnchor="end">AWS 서울 리전 (ap-northeast-2) · VPC</text>
          <rect x="44" y="138" width="552" height="256" rx="8" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <text x="582" y="158" fontSize="12.5" fontWeight="600" fill="var(--ink)" textAnchor="end">EC2 서버 · 탄력적 IP 3.36.160.246 (퍼블릭 서브넷)</text>
          <rect x="50" y="176" width="120" height="56" rx="8" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="110" y="200" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">Caddy</text>
          <text x="110" y="219" fontSize="12" fill="var(--muted)" textAnchor="middle">현관 안내데스크</text>
          <line x1="170" y1="204" x2="225" y2="204" stroke="var(--line)" strokeWidth="1.5" />
          <line x1="225" y1="193" x2="225" y2="329" stroke="var(--line)" strokeWidth="1.5" />
          <line x1="225" y1="193" x2="270" y2="193" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="264,188 272,193 264,198" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <line x1="225" y1="261" x2="270" y2="261" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="264,256 272,261 264,266" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <line x1="225" y1="329" x2="270" y2="329" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="264,324 272,329 264,334" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <rect x="280" y="170" width="140" height="46" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="350" y="189" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">React 프론트</text>
          <text x="350" y="207" fontSize="12" fill="var(--muted)" textAnchor="middle">화면 담당</text>
          <rect x="280" y="238" width="140" height="46" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="350" y="257" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">Spring Boot</text>
          <text x="350" y="275" fontSize="12" fill="var(--muted)" textAnchor="middle">API·로직 담당</text>
          <rect x="280" y="306" width="140" height="46" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="350" y="325" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">Gitea</text>
          <text x="350" y="343" fontSize="12" fill="var(--muted)" textAnchor="middle">코드 저장소</text>
          <line x1="420" y1="261" x2="452" y2="261" stroke="var(--line)" strokeWidth="1.5" />
          <polyline points="452,256 460,261 452,266" fill="none" stroke="var(--line)" strokeWidth="1.5" />
          <text x="443" y="253" fontSize="12" fill="var(--muted)" textAnchor="middle">내부망</text>
          <rect x="466" y="238" width="124" height="46" rx="8" fill="var(--card)" stroke="var(--amber)" strokeWidth="1.5" />
          <text x="528" y="257" fontSize="13" fontWeight="600" fill="var(--ink)" textAnchor="middle">PostgreSQL</text>
          <text x="528" y="275" fontSize="12" fill="var(--muted)" textAnchor="middle">데이터 금고</text>
          <text x="320" y="382" fontSize="12" fill="var(--muted)" textAnchor="middle">PostgreSQL은 Docker 내부 네트워크에만 — 인터넷에서 직접 두드릴 수 없어요</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 브라우저에서 PostgreSQL까지 — 우리 플랫폼 요청의 전체 여정</p>
        <div className="tip">
          <b>직접 확인해 보기</b> 지금 이 페이지에서 <span className="kbd">F12</span> →{' '}
          <span className="kbd">Network</span> 탭 → <span className="kbd">F5</span> 새로고침.
          아무 요청이나 클릭해 <strong>Remote Address</strong>를 찾아보세요.{' '}
          <span className="icode">3.36.160.246:443</span> — 방금 다이어그램에서 본
          탄력적 IP와 HTTPS 포트가 진짜로 찍혀 있을 거예요. 지도와 실물을 맞춰 보는 순간입니다!
        </div>
      </Section>

      <Section n={7} title="CDN 맛보기" sub="전 세계에 미리 깔아 두는 복사본 창고">
        <p>
          마지막으로, 큰 서비스들이 쓰는 기술 하나만 맛봅시다. 서울 서버가 아무리 빨라도
          지구 반대편 사용자에게는 <strong>물리적 거리</strong> 때문에 느릴 수밖에 없어요.
          빛조차 왕복에 시간이 걸리니까요. 그래서 등장한 것이 <strong>CDN</strong>입니다.
        </p>
        <Code>{CODE_CDN}</Code>
        <div className="tip">
          <b>생각해 보기</b> 우리 플랫폼에 브라질 지사가 생긴다면, 무엇을 CDN에 올리고
          무엇을 서울 원본 서버에 남겨야 할까요? 힌트: "모두에게 똑같은 것"과
          "사람마다 다른 것"을 나눠 보세요. 답을 멘토에게 말로 설명해 보면 완벽!
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>☁️ 여기까지 왔다면</h3>
          <p className="muted">
            이제 "클라우드"라는 말을 들으면 구름 대신 <strong>서울 리전의 데이터센터,
            VPC 울타리, 퍼블릭/프라이빗 구역, 그리고 3.36.160.246</strong>이 떠오를 거예요.
            우리 플랫폼의 네트워크 지도를 통째로 그릴 수 있게 됐습니다. 아직{' '}
            <Link to="/learn/network"><strong>네트워크의 이해</strong></Link> 코스를 안 들었다면
            먼저 다녀오고, 다음은 이 지도 위에 서비스를 올리는 방법 —{' '}
            <Link to="/learn/docker-intro"><strong>Docker 입문</strong></Link> 코스에서 Docker 컨테이너가
            EC2 위에서 어떻게 굴러가는지 이어서 배워 보세요.
          </p>
        </div>
      </div>
    </div>
  );
}
