// 이 파일이 하는 일: "서버란 무엇인가" 코스 — 서버가 특별한 컴퓨터가 아니라
// '요청을 기다리는 프로그램'이라는 사실에서 출발해, 웹서버/WAS/DB 3계층과
// 우리 회사(AWESOMEDEV)의 실제 구성(Caddy → Spring Boot → PostgreSQL),
// 동시 접속 처리와 확장(스케일업/아웃)까지 7개 섹션으로 안내하는 정적 학습 페이지.
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색을 여기서 하드코딩하지 않는다.
// 텍스트 다이어그램은 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

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

const CODE_WHAT_IS_SERVER = `서버(server) = serve(대접하다) + er(하는 자)
→ "요청이 오면 응답을 대접하는 프로그램"

[클라이언트]  ── "이 페이지 주세요" (요청) ──>  [서버]
[클라이언트]  <── "여기요, 200 OK" (응답)  ──  [서버]

핵심 3가지:
1) 서버는 '기계'가 아니라 '역할'이에요. 프로그램이 그 역할을 맡습니다.
2) 서버 프로그램은 특정 포트를 열고 하루 종일 '기다리는' 게 일이에요.
3) 여러분의 노트북도 서버 프로그램을 켜는 순간 서버가 됩니다.

식당 비유: 서버 = 주문을 기다리는 직원.
손님(클라이언트)이 없으면 그냥 대기 중이고,
주문(요청)이 들어오는 순간 일해서 음식(응답)을 내놓습니다.`;

const CODE_THREE_TIER = `웹 서비스의 3계층 — 식당 한 곳에 비유하면:

[웹서버]          [WAS]              [DB]
홀 직원            주방               냉장고·창고
─────────       ─────────         ─────────
손님 맞이          요리(비즈니스        재료(데이터)
주문 접수          로직) 담당           보관·꺼내기
완성된 반찬은
바로 서빙
(정적 파일)

우리 스택으로 번역하면:
웹서버 = Caddy          (HTTPS 처리, 요청 안내, 정적 파일 서빙)
WAS    = Spring Boot    (로그인 검사, 데이터 가공, JSON 응답 조립)
DB     = PostgreSQL     (회원·코스·진도 데이터를 표(테이블)로 보관)

왜 셋으로 나눌까?
→ 역할이 다르면 잘하는 도구도 다르고, 하나가 바빠져도
  그 층만 따로 보강할 수 있기 때문이에요. (섹션 6에서 다시!)`;

const CODE_OUR_STACK = `여러분이 이 페이지를 여는 순간, 서울 EC2 서버 안에서 벌어지는 일:

인터넷
  │  https://edu.awesomedevapp.com (443 포트)
  ▼
[Caddy]  ── "HTTPS 자물쇠 풀기 + 어디로 보낼지 안내"
  │
  ├─ /assets/... 같은 정적 파일 요청 → 파일을 바로 건네줌 (React 빌드 결과물)
  │
  └─ /api/... 요청 → 뒤의 Spring Boot로 전달
        ▼
     [Spring Boot]  ── "누구지? 로그인 토큰 확인. 무슨 데이터가 필요하지?"
        │  SQL 질의 (5432 포트)
        ▼
     [PostgreSQL]  ── "요청한 데이터, 표에서 찾아서 여기요"

응답은 이 길을 거꾸로: DB → Spring Boot(JSON 조립) → Caddy → 브라우저.
이 전부가 Docker 컨테이너로 포장되어 AWS EC2(서울) 한 대 위에서 돌아갑니다.
코드가 태어나는 곳은 Gitea(edu.awesomedevapp.com의 Git 서버)고요.`;

const CODE_STATIC_DYNAMIC = `정적 파일 vs 동적 응답 — 자판기와 주방의 차이

정적(static)                       동적(dynamic)
──────────────────            ──────────────────
미리 만들어 둔 파일을 그대로         요청이 올 때마다 그 자리에서
건네줌                             새로 만들어서 건네줌
예) HTML, CSS, JS, 이미지          예) "내 진도율", "내 이름이 든 화면"
누가 요청해도 내용이 같음            누가·언제 요청하냐에 따라 다름
자판기: 버튼 누르면 그대로 나옴      주방: 주문 듣고 지금 요리함
Caddy가 바로 처리 (빠름!)           Spring Boot + DB가 처리

이 코스 페이지의 글 자체는 정적이지만,
로그인 후 보이는 "○○님, 진도 40%"는 동적 응답이에요.
한 화면 안에 둘이 섞여 있는 게 보통입니다.`;

const CODE_CONCURRENCY = `동시 접속 — 손님 30명이 한꺼번에 들어오면?

나쁜 식당: 직원 1명이 1번 손님 요리가 끝날 때까지
          2번 손님을 쳐다도 안 봄 → 줄이 밀림

서버의 해법:
1) 스레드(thread) — 직원을 여러 명 두기.
   Spring Boot는 기본으로 요청 처리 직원을 수백 명(스레드 풀)
   대기시켜 두고, 요청마다 한 명씩 붙입니다.
2) 커넥션 풀 — 주방↔창고(DB) 사이 통로를 미리 몇 개 뚫어 두고
   돌려쓰기. 매번 새로 뚫으면 그게 더 오래 걸리거든요.

그래도 한계는 있어요:
직원(스레드)이 다 바쁘면 새 손님은 대기열에서 기다리고,
대기열마저 차면 "지금은 못 받아요"(에러)가 납니다.
→ 그때 필요한 게 다음 섹션, '서버 키우기'입니다.`;

const CODE_SCALING = `서버가 힘들어할 때, 두 가지 처방

스케일 업(Scale Up)                스케일 아웃(Scale Out)
"더 힘센 한 대"                     "여러 대로 나누기"
──────────────────            ──────────────────
CPU·메모리를 좋은 걸로 교체          같은 서버를 여러 대 두고
(EC2라면 인스턴스 타입 변경)         앞에 '교통정리 담당'
                                  (로드 밸런서)을 세움
식당 비유: 주방장을                 식당 비유: 지점을 냄
슈퍼 주방장으로 교체
장점: 코드 수정 없이 간단            장점: 한 대가 죽어도 나머지가 버팀
단점: 비용이 가파르게 늘고,          단점: 서버들끼리 상태(세션 등)를
      아무리 좋아도 '한 대'의         공유하는 설계가 필요해서
      한계와 한 대가 죽으면           구조가 복잡해짐
      전부 멈추는 위험은 그대로

지금 우리 플랫폼은 EC2 한 대 = 스케일 업으로 충분한 단계.
사용자가 늘면 스케일 아웃을 고민하게 되고, 그때 3계층으로
나눠 둔 설계(섹션 2)가 빛을 발합니다 — WAS만 여러 대로 늘리면 되니까요.`;

const CODE_LOCALHOST = `# 내 PC를 서버로 만드는 데 필요한 명령, 단 한 줄:
# (mirim-app/frontend 폴더에서)
npm run dev

  VITE ready in 400 ms
  ➜  Local:   http://localhost:5173/   ← 이 줄이 "저 지금 서버예요" 선언!

# localhost = "내 PC 자신"을 가리키는 특별한 이름 (IP로는 127.0.0.1)
# 5173 = Vite 개발 서버가 열고 기다리는 포트(방 번호)

# 브라우저에서 http://localhost:5173 접속
#  → 브라우저(클라이언트)가 내 PC(서버)에 요청
#  → 같은 컴퓨터 안에서 요청과 응답이 오간 것!

# 서버가 '기다리는 중'이라는 증거:
# 터미널을 그대로 두면 커서가 안 돌아와요. 프로그램이 끝난 게 아니라
# 다음 요청을 기다리는 중이거든요. Ctrl+C 를 누르면 서버가 내려가고,
# 그 순간 브라우저를 새로고침하면 "연결할 수 없음" — 서버의 생사를
# 직접 쥐어 본 겁니다.`;

const CODE_PORT_CHECK = `# 내 PC에서 지금 '기다리는 중'인 서버들을 구경하기 (PowerShell):
netstat -ano | findstr LISTENING

  TCP  0.0.0.0:135    ...  LISTENING  ...
  TCP  127.0.0.1:5173 ...  LISTENING  1234   ← npm run dev가 켠 서버!

# LISTENING = "포트 열고 요청 기다리는 중"이라는 뜻.
# npm run dev를 켠 상태와 끈 상태에서 각각 실행해서
# 5173 줄이 나타났다 사라지는 걸 확인해 보세요.
# 맨 끝 숫자(PID)는 그 포트를 잡고 있는 프로그램의 번호예요 —
# 작업 관리자 [세부 정보] 탭에서 같은 PID를 찾으면 node.exe가 보입니다.`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: '3계층 구조' },
  { n: 3, label: '우리 서버 해부' },
  { n: 4, label: '정적 vs 동적' },
  { n: 5, label: '동시 접속' },
  { n: 6, label: '스케일 업/아웃' },
  { n: 7, label: '내 PC로 서버 실습' },
];

export default function ServerAnatomyPage() {
  return (
    <div>
      {/* 히어로: '서버 = 비싼 기계'라는 오해를 첫 문장부터 깨고 시작 */}
      <div className="hero">
        <div className="eyebrow">Course · 서버와 데이터</div>
        <h1>서버란 무엇인가<br />— 요청을 기다리는 프로그램의 세계</h1>
        <p>
          서버는 서버실의 비싼 기계가 아니라, <strong>요청을 기다렸다가 응답하는 프로그램</strong>이에요.
          그 정체를 파헤치고, 지금 이 페이지를 여러분에게 보내 준 우리 회사 서버의 속을 열어 본 다음,
          마지막엔 <strong>여러분의 PC를 직접 서버로</strong> 만들어 봅니다.
        </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>입니다.
        </p>
        <Code>{CODE_WHAT_IS_SERVER}</Code>
        <p>
          그래서 브라우저처럼 <strong>먼저 요청을 보내는 쪽</strong>을 클라이언트(client, 손님),
          <strong> 기다렸다가 응답하는 쪽</strong>을 서버라고 불러요. 이건 프로그램의
          <strong> 역할 이름</strong>이지 기계의 등급이 아니에요 — 여러분의 노트북에 서버 프로그램을
          켜면 그 순간 노트북이 서버가 되고, 슈퍼컴퓨터라도 요청만 보내고 있으면 클라이언트입니다.
        </p>
        <div className="tip">
          <b>미리보기</b> "내 PC가 서버가 된다"는 말, 못 믿겠다면 섹션 7에서 직접 확인하게 됩니다.
          사실 여러분이 <span className="icode">npm run dev</span>를 칠 때마다 이미 서버를 켜고 있었어요.
        </div>
      </Section>

      <Section n={2} title="웹서버 · WAS · DB — 3계층 구조" sub="홀 직원, 주방, 냉장고가 각자 잘하는 일">
        <p>
          웹 서비스 하나는 보통 프로그램 하나가 아니라 <strong>역할이 다른 세 프로그램의 팀</strong>이에요.
          손님을 맞는 <strong>웹서버</strong>, 요리(계산·판단)를 하는 <strong>WAS</strong>
          (Web Application Server), 재료(데이터)를 보관하는 <strong>DB</strong>(데이터베이스)죠.
        </p>
        <Code>{CODE_THREE_TIER}</Code>
        <p>각 층이 하는 일을 조금 더 자세히 보면 이래요.</p>
        <ol className="olist">
          <li>
            <strong>웹서버</strong> — 인터넷에서 오는 요청을 가장 먼저 받아요. HTTPS 암호를 풀고,
            이미지·CSS처럼 미리 만들어 둔 파일은 직접 건네주고, 계산이 필요한 요청만 뒤로 넘깁니다.
            현관에서 손님을 맞고 간단한 반찬은 바로 내주는 홀 직원이에요.
          </li>
          <li>
            <strong>WAS</strong> — "이 학생의 진도율은?" 같은 질문에 답하려면 로그인 확인, DB 조회,
            계산, JSON 조립이 필요해요. 이 <strong>비즈니스 로직</strong>을 담당하는 게 주방, WAS입니다.
            우리 회사에선 Spring Boot가 이 역할이에요.
          </li>
          <li>
            <strong>DB</strong> — 데이터를 표(테이블) 형태로 안전하게 보관하고, 요청하면 빠르게
            찾아 주는 창고예요. 서버가 꺼져도 데이터는 남아야 하니까, 보관은 전문가(PostgreSQL)에게 맡깁니다.
          </li>
        </ol>
        <div className="warn">
          <b>왜 주방(WAS)이 냉장고(DB) 역할까지 하면 안 될까?</b> — 기술적으로는 WAS가 메모리에
          데이터를 들고 있을 수도 있어요. 하지만 WAS가 재시작되면 메모리는 통째로 사라집니다.
          "역할 분리"는 멋 부리기가 아니라 <strong>데이터를 잃지 않기 위한 생존 전략</strong>이에요.
        </div>
      </Section>

      <Section n={3} title="우리 서버 해부 — Caddy → Spring Boot → PostgreSQL" sub="지금 이 페이지가 여러분에게 도착한 실제 경로">
        <p>
          이제 3계층을 <strong>우리 회사의 진짜 서버</strong>에 겹쳐 볼게요. 여러분이 이 학습 플랫폼에
          접속하면, AWS <strong>EC2 서울</strong> 서버 안에서 이런 릴레이가 벌어집니다.
        </p>
        <Code>{CODE_OUR_STACK}</Code>
        <svg className="diagram" viewBox="0 0 640 400" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="브라우저의 요청이 Caddy, Spring Boot, PostgreSQL을 차례로 거치고 응답이 거꾸로 돌아오는 3계층 요청 흐름도">
          <rect x="170" y="112" width="440" height="272" rx="10" fill="none" stroke="var(--line)" strokeDasharray="6 5" />
          <text x="596" y="132" textAnchor="end" fontSize="12" fill="var(--muted)">AWS EC2 (서울) — 모두 Docker 컨테이너</text>
          <rect x="185" y="16" width="170" height="52" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="270" y="38" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">브라우저</text>
          <text x="270" y="57" textAnchor="middle" fontSize="12" fill="var(--muted)">클라이언트</text>
          <line x1="270" y1="68" x2="270" y2="132" stroke="var(--muted)" strokeWidth="2" />
          <path d="M270 140 L264 130 L276 130 Z" fill="var(--muted)" />
          <text x="282" y="100" fontSize="12" fill="var(--muted)">① HTTPS 요청 (443 포트)</text>
          <rect x="185" y="140" width="170" height="56" rx="8" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="270" y="163" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">Caddy</text>
          <text x="270" y="183" textAnchor="middle" fontSize="12" fill="var(--muted)">웹서버 · HTTPS 처리</text>
          <line x1="355" y1="168" x2="386" y2="168" stroke="var(--muted)" strokeWidth="2" />
          <path d="M394 168 L384 162 L384 174 Z" fill="var(--muted)" />
          <text x="402" y="163" fontSize="12" fill="var(--muted)">정적 파일(JS·CSS·이미지)은</text>
          <text x="402" y="181" fontSize="12" fill="var(--muted)">Caddy가 바로 응답</text>
          <line x1="270" y1="196" x2="270" y2="220" stroke="var(--muted)" strokeWidth="2" />
          <path d="M270 228 L264 218 L276 218 Z" fill="var(--muted)" />
          <text x="282" y="216" fontSize="12" fill="var(--muted)">② /api/* 요청만 전달</text>
          <rect x="185" y="228" width="170" height="56" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="270" y="251" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">Spring Boot</text>
          <text x="270" y="271" textAnchor="middle" fontSize="12" fill="var(--muted)">WAS · 비즈니스 로직</text>
          <line x1="270" y1="284" x2="270" y2="308" stroke="var(--muted)" strokeWidth="2" />
          <path d="M270 316 L264 306 L276 306 Z" fill="var(--muted)" />
          <text x="282" y="304" fontSize="12" fill="var(--muted)">③ SQL 질의 (5432 포트)</text>
          <rect x="185" y="316" width="170" height="56" rx="8" fill="var(--card)" stroke="var(--amber)" strokeWidth="1.5" />
          <text x="270" y="339" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">PostgreSQL</text>
          <text x="270" y="359" textAnchor="middle" fontSize="12" fill="var(--muted)">DB · 데이터 보관</text>
          <polyline points="185,344 150,344 150,42 177,42" fill="none" stroke="var(--teal)" strokeWidth="2" strokeDasharray="5 4" strokeLinecap="round" />
          <path d="M185 42 L175 36 L175 48 Z" fill="var(--teal)" />
          <text x="140" y="196" textAnchor="end" fontSize="12" fill="var(--muted)">④ 응답은</text>
          <text x="140" y="213" textAnchor="end" fontSize="12" fill="var(--muted)">이 길 거꾸로</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 요청 한 번이 EC2 안에서 밟는 릴레이 — Caddy → Spring Boot → PostgreSQL</p>
        <p>
          여기서 <strong>Docker</strong>의 역할도 짚고 갈게요. Caddy·Spring Boot·PostgreSQL은 각각
          설치법도 설정법도 다른 프로그램인데, Docker는 이들을 <strong>규격 컨테이너</strong>에
          하나씩 포장해 줍니다. 어떤 컴퓨터에서든 똑같이 실행되는 도시락 통이라, "제 PC에서는
          됐는데요..." 문제를 크게 줄여 줘요.
        </p>
        <p>
          그리고 이 구조에서 React는 어디 있냐고요? 여러분이 짠 React 코드는 빌드를 거쳐
          <strong> 정적 파일(HTML·JS·CSS)</strong>이 되고, Caddy가 그 파일을 서빙해요. 즉 React
          앱은 서버가 아니라 <strong>서버가 배달해 준 뒤 브라우저 안에서 실행되는 프로그램</strong>입니다.
          이 구분이 다음 섹션의 주제예요.
        </p>
      </Section>

      <Section n={4} title="정적 파일 vs 동적 응답" sub="자판기에서 뽑을 것인가, 주방에서 만들 것인가">
        <Code>{CODE_STATIC_DYNAMIC}</Code>
        <p>
          이 구분이 중요한 이유는 <strong>비용</strong>이에요. 정적 파일은 자판기처럼 꺼내 주기만
          하면 되니 웹서버가 순식간에 수백 개도 처리해요. 반면 동적 응답은 매번 주방(WAS)과
          창고(DB)까지 다녀와야 하니 훨씬 비쌉니다. 그래서 좋은 서비스는 <strong>정적으로 될 일은
          최대한 정적으로</strong> 처리하고, 꼭 필요한 것만 동적으로 만들어요.
        </p>
        <div className="tip">
          <b>직접 확인해 보기 ①</b> 우리 플랫폼에서 <span className="kbd">F12</span> →
          <span className="kbd">Network</span> 탭 → <span className="kbd">F5</span> 새로고침.
          목록에서 <span className="icode">.js</span>·<span className="icode">.css</span>·이미지
          요청(정적)과 <span className="icode">/api/</span>로 시작하는 요청(동적)을 구분해 보세요.
          같은 화면 한 장이 정적·동적 응답의 <strong>합작품</strong>이라는 걸 눈으로 확인할 수 있어요.
        </div>
      </Section>

      <Section n={5} title="동시 접속 — 손님이 한꺼번에 몰려오면" sub="스레드 풀과 커넥션 풀, 서버의 인력 관리">
        <p>
          우리 반 30명이 <strong>동시에</strong> 이 페이지를 열면 서버는 어떻게 될까요?
          한 명씩 순서대로 처리한다면 30번째 친구는 한참을 기다려야겠죠. 그래서 서버는
          <strong> 여러 요청을 겹쳐서</strong> 처리하는 장치들을 갖고 있어요.
        </p>
        <Code>{CODE_CONCURRENCY}</Code>
        <p>
          정리하면 — 서버의 성능은 "얼마나 빨리 하나를 처리하느냐"만이 아니라
          <strong> "동시에 몇 개를 받아 내느냐"</strong>로도 측정돼요. 수강신청 사이트가
          정각에 뻗는 것도, 대기열(스레드·커넥션)이 감당 못 할 만큼 요청이 몰렸기 때문입니다.
          그럼 감당이 안 될 땐 어떻게 할까요? 다음 섹션이 그 답이에요.
        </p>
      </Section>

      <Section n={6} title="스케일 업 vs 스케일 아웃" sub="주방장을 바꿀 것인가, 지점을 낼 것인가">
        <Code>{CODE_SCALING}</Code>
        <svg className="diagram" viewBox="0 0 640 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="스케일 업은 서버 한 대를 더 좋은 사양으로 키우고, 스케일 아웃은 로드 밸런서 뒤에 서버 여러 대를 두는 방식임을 비교한 그림">
          <line x1="320" y1="18" x2="320" y2="238" stroke="var(--line)" strokeDasharray="4 4" />
          <text x="160" y="32" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">스케일 업 — 더 힘센 한 대</text>
          <rect x="60" y="95" width="70" height="65" rx="8" fill="var(--card)" stroke="var(--line)" strokeWidth="1.5" />
          <text x="95" y="123" textAnchor="middle" fontSize="13" fill="var(--ink)">서버</text>
          <text x="95" y="142" textAnchor="middle" fontSize="12" fill="var(--muted)">보통 사양</text>
          <line x1="140" y1="127" x2="176" y2="127" stroke="var(--muted)" strokeWidth="2" />
          <path d="M184 127 L174 121 L174 133 Z" fill="var(--muted)" />
          <rect x="194" y="62" width="100" height="130" rx="8" fill="var(--card)" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="244" y="118" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">서버</text>
          <text x="244" y="140" textAnchor="middle" fontSize="12" fill="var(--muted)">CPU·메모리 ↑</text>
          <text x="160" y="226" textAnchor="middle" fontSize="12" fill="var(--muted)">간단하지만 한 대의 한계는 그대로</text>
          <text x="480" y="32" textAnchor="middle" fontSize="14" fontWeight="700" fill="var(--ink)">스케일 아웃 — 여러 대로 나누기</text>
          <rect x="420" y="58" width="120" height="44" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="480" y="77" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--ink)">로드 밸런서</text>
          <text x="480" y="94" textAnchor="middle" fontSize="12" fill="var(--muted)">교통정리 담당</text>
          <line x1="480" y1="102" x2="388" y2="136" stroke="var(--muted)" strokeWidth="2" />
          <line x1="480" y1="102" x2="480" y2="136" stroke="var(--muted)" strokeWidth="2" />
          <line x1="480" y1="102" x2="572" y2="136" stroke="var(--muted)" strokeWidth="2" />
          <path d="M388 144 L383 134 L393 134 Z" fill="var(--muted)" />
          <path d="M480 144 L475 134 L485 134 Z" fill="var(--muted)" />
          <path d="M572 144 L567 134 L577 134 Z" fill="var(--muted)" />
          <rect x="350" y="144" width="76" height="52" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <rect x="442" y="144" width="76" height="52" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <rect x="534" y="144" width="76" height="52" rx="8" fill="var(--card)" stroke="var(--teal)" strokeWidth="1.5" />
          <text x="388" y="175" textAnchor="middle" fontSize="13" fill="var(--ink)">서버 1</text>
          <text x="480" y="175" textAnchor="middle" fontSize="13" fill="var(--ink)">서버 2</text>
          <text x="572" y="175" textAnchor="middle" fontSize="13" fill="var(--ink)">서버 3</text>
          <text x="480" y="226" textAnchor="middle" fontSize="12" fill="var(--muted)">한 대가 죽어도 나머지가 버텨요</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 스케일 업(왼쪽) vs 스케일 아웃(오른쪽)</p>
        <p>
          실무의 감각은 이래요 — <strong>작을 땐 스케일 업이 정답</strong>입니다. 단순하고,
          빠르고, 구조를 바꿀 필요가 없거든요. 우리 플랫폼도 지금은 EC2 한 대로 충분해요.
          하지만 사용자가 수만 명이 되면 아무리 좋은 한 대로도 버틸 수 없고, 그 한 대가
          멈추면 서비스 전체가 멈춘다는 위험도 커집니다. 그때 <strong>스케일 아웃</strong>으로
          넘어가요.
        </p>
        <p>
          그리고 여기서 섹션 2의 복선이 회수됩니다 — 3계층으로 <strong>역할을 나눠 둔 덕분에</strong>,
          바쁜 층(보통 WAS)만 여러 대로 늘리고 DB는 그대로 두는 식의 <strong>부분 확장</strong>이
          가능해요. 처음부터 한 덩어리로 만들었다면 통째로 복제하는 수밖에 없었겠죠.
          설계는 미래의 나를 위한 보험입니다.
        </p>
      </Section>

      <Section n={7} title="실습 — 내 PC를 서버로 만들기" sub="npm run dev, 사실 여러분은 매일 서버를 켜고 있었다">
        <p>
          대망의 실습이에요. 준비물은 여러분이 늘 쓰는 그 명령어 하나 —
          다만 오늘은 <strong>"서버를 켠다"는 자각</strong>을 갖고 다시 봅니다.
        </p>
        <div className="tip">
          <b>직접 확인해 보기 ②</b> 터미널에서 <span className="icode">mirim-app/frontend</span> 폴더로
          이동해 아래를 실행하고, 주석의 관찰 포인트를 <strong>하나씩 전부</strong> 따라가 보세요.
          특히 마지막 — 서버를 끄고 새로고침해서 "연결할 수 없음"을 보는 순간이 오늘의 하이라이트입니다.
        </div>
        <Code>{CODE_LOCALHOST}</Code>
        <p>
          방금 벌어진 일을 정리하면: <strong>Vite 개발 서버</strong>(node 프로그램)가 내 PC의
          5173번 포트를 열고 기다렸고, 브라우저가 <span className="icode">localhost:5173</span>으로
          요청을 보냈고, 서버가 페이지를 응답했어요. 클라이언트와 서버가 <strong>같은 컴퓨터 안에</strong>
          있었을 뿐, 섹션 1의 그림과 완전히 같은 구조입니다.
        </p>
        <p>
          한 걸음 더 — 내 PC 안에서 지금 이 순간에도 기다리고 있는 서버들을 구경해 볼까요?
        </p>
        <Code>{CODE_PORT_CHECK}</Code>
        <div className="warn">
          <b>주의</b> <span className="icode">netstat</span>은 구경만 하세요. 모르는 포트를 잡고 있는
          프로그램을 함부로 종료하면 Windows 시스템 기능이 멈출 수 있어요. 관찰은 자유, 종료는 금지!
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>🖥️ 여기까지 왔다면</h3>
          <p className="muted">
            이제 여러분은 서버가 <strong>요청을 기다리는 프로그램</strong>이라는 것, 웹 서비스가
            <strong> 웹서버·WAS·DB 세 팀의 릴레이</strong>(우리 회사에선 Caddy → Spring Boot →
            PostgreSQL)라는 것, 그리고 몰려드는 요청을 <strong>스레드 풀로 버티고 스케일 업/아웃으로
            키운다</strong>는 것까지 알게 됐어요. 무엇보다, 내 PC로 서버를 직접 켜고 꺼 봤죠.
            다음은 그 릴레이의 마지막 주자 — 데이터를 보관하는 창고의 속을 여는{' '}
            <Link to="/learn/sql"><strong>데이터베이스 기초</strong></Link> 코스로 이어 가세요.
            멘토 과제: 오늘 배운 3계층 그림을 보지 않고 종이에 그려서 멘토에게 설명해 보기!
          </p>
        </div>
      </div>
    </div>
  );
}
