// 이 파일이 하는 일: "앱의 상태와 데이터" 코스 — 앱이 다루는 데이터를 '세 층'
// (화면 상태·기기 로컬·서버)으로 나눠 보는 지도에서 출발해, 우리가 이미 배운
// React useState를 다리 삼아 로컬 저장·서버 연동·오프라인/동기화·상태관리
// 라이브러리·웹과 앱의 같고 다름까지 7개 섹션으로 안내하는 정적 학습 페이지.
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색은 여기서 하드코딩하지 않는다.
// 예제 코드는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.
// 프레이밍: "웹 개발자가 보는 모바일" — 우리 React/JS 지식을 그대로 앱으로 옮겨 보는 관점.

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

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

const CODE_LAYERS = `앱 데이터의 세 층 — "어디에, 얼마나 오래 사는가"

층                  저장 위치         수명                     범위
──────────────────────────────────────────────────────────────────
① 화면 상태          메모리(RAM)       화면 닫으면 사라짐         이 화면 하나
   useState                          (앱 종료 = 리셋)
② 기기 로컬          디스크(저장소)     앱을 껐다 켜도 남음        이 기기 1대
   AsyncStorage·SQLite               (직접 지우기 전까지)
③ 서버              남의 컴퓨터        영구 보관(백업까지)       모든 기기·모든 사용자
   Spring + Postgres

고르는 법(위→아래로 질문):
  "앱을 껐다 켜도 남아야 하나?"        → 아니오면 ① 화면 상태
  "다른 기기·다른 사람도 봐야 하나?"   → 예면   ③ 서버
  둘 다 애매하면(이 폰에서만, 오래)     → 그 사이 ② 기기 로컬`;

const CODE_USESTATE = `// 화면 상태 = 이 화면이 '잠깐' 기억하는 값 (웹 React와 100% 동일)
import { useState } from 'react';

function SearchScreen() {
  const [keyword, setKeyword] = useState('');    // 검색창에 친 글자
  const [loading, setLoading] = useState(false); // 스피너 켬/끔
  const [tab, setTab]         = useState('전체'); // 지금 고른 탭

  // 이 셋의 공통점: "화면을 나가면 없어져도 되는" 값들이에요.
  // 검색어·스크롤 위치·열린 탭·모달 on/off · 폼 입력 … 전부 화면 상태.
}

// 포인트: 우리 mirim-app 웹의 DashboardPage가 쓰는 그 useState와
//         문법이 한 글자도 다르지 않아요. "앱이라 특별한 상태"는 없습니다.
//         (화면을 그리는 태그만 <div> → <View>로 바뀔 뿐, 상태 로직은 그대로.)`;

const CODE_LOCAL = `// 기기 로컬 저장 — 앱을 껐다 켜도 남는 값 (React Native 기준)
import AsyncStorage from '@react-native-async-storage/async-storage';

// 저장 (문자열 키-값 창고. 웹의 localStorage와 사고방식이 똑같아요)
await AsyncStorage.setItem('theme', 'dark');

// 읽기 (앱을 새로 켤 때 마지막 설정을 복원)
const theme = await AsyncStorage.getItem('theme');  // 'dark' 또는 null

// 지우기
await AsyncStorage.removeItem('theme');

// 무엇을 어디에 둘까?
//   ○ 테마·언어·"튜토리얼 봤음" 같은 간단한 설정  → AsyncStorage
//   ○ 수천 줄 목록을 검색·정렬·조인해야 함        → SQLite (기기 속 미니 관계형 DB)
//   × 비밀번호·토큰 같은 민감정보                 → 보안 저장소에!
//     iOS Keychain / Android Keystore (또는 expo-secure-store)
//     — AsyncStorage는 '평문'이라 민감정보 저장 금지.`;

const CODE_SERVER = `// 앱에서 우리 Spring API 호출 — fetch는 앱(React Native)에도 그대로 있어요
async function loadStats(token) {
  const res = await fetch(
    'https://edu.awesomedevapp.com/api/dashboard/stats', // ① 전체 주소
    {
      headers: { Authorization: 'Bearer ' + token },      // ② 신분증(토큰)
    }
  );
  if (!res.ok) throw new Error('요청 실패: ' + res.status);
  return await res.json();                                 // ③ JSON 파싱
}

// 웹과 뭐가 다른가? (딱 두 줄이에요)
//  ① 웹은 '/api/...'만 써도 됐다(같은 출처). 앱엔 '출처'라는 게 없어서
//     https:// 전체 주소를 적어야 한다.
//  ② 우리 웹은 로그인 '쿠키'를 브라우저가 요청마다 자동으로 붙여 줬다
//     (api/client.js의 withCredentials). 앱은 브라우저가 아니라 쿠키 자동첨부가
//     없으니, 토큰을 보안 저장소에 넣어 두고 Authorization 헤더에 직접 실어 보낸다.`;

const CODE_OFFLINE = `오프라인 대응 3단계 — "지하철에서 신호가 끊겨도 앱이 안 죽게"

① 읽기: 마지막으로 받은 데이터를 로컬(②층)에 캐시해 두고,
        오프라인이면 그걸 보여 준다. → 빈 화면 대신 '방금 전의 목록'.

② 쓰기: 오프라인에서 '좋아요'를 눌렀다면?
        → 화면은 먼저 바꾸고(= 낙관적 UI, optimistic update),
          서버로 보낼 요청은 '대기 큐'에 차곡차곡 쌓아 둔다.

③ 재연결: 인터넷이 돌아오면 큐에 쌓인 요청을 순서대로 전송한다.
        성공하면 큐에서 지우고, 실패하면 잠시 뒤 재시도.

⚠ 충돌(conflict): 같은 글을 폰과 PC에서 동시에 고치면 누가 이기나?
   - 마지막에 저장한 쪽이 이김 (last-write-wins)  ← 구현이 간단, 데이터 손실 가능
   - 양쪽 변경을 합침 (merge)                      ← 안전하지만 어렵다
   '정답'은 없다 — 데이터 성격에 맞게 규칙을 미리 정하는 게 정답이다.`;

const CODE_STATE_LIBS = `상태가 여기저기서 필요해질 때 — '도구의 사다리'

 작을 때 ────────────────────────────────────────────── 커질 때
 useState          Context            Zustand / Redux       TanStack Query
 (한 컴포넌트        (로그인 정보처럼      (앱 '전역' 상태를       (서버 데이터 전용:
  안에서만)          여러 화면이 공유)     한 곳에 모아 관리)      캐시·로딩·재요청 자동)

 └─ 클라이언트 상태(내 화면이 만든 값) ─┘   └─ 서버 상태(서버가 주인인 값) ─┘

우리 mirim-app은 지금?
  · 화면 하나짜리 상태  → useState (거의 모든 페이지)
  · 로그인 사용자 정보  → React Context (AuthContext) — 모든 화면이 공유
  아직 Redux 같은 큰 도구는 안 씁니다. "필요해지기 전엔 안 꺼낸다"가 원칙.

핵심: 이 라이브러리들은 React 웹과 React Native가 '그대로' 공유해요.
      앱이라고 새로 배워야 할 상태관리 도구는 없습니다.`;

const CODE_WEB_VS_APP = `웹(React) vs 앱(React Native) — 데이터 관점에서 같은 것·다른 것

                      웹 (우리 mirim-app)       앱 (React Native)
────────────────────────────────────────────────────────────────────
화면 상태             useState                  useState          (똑같음)
서버 호출             fetch / axios             fetch / axios     (똑같음)
상태관리 라이브러리    Context·Zustand·Query     그대로 사용        (똑같음)
────────────────────────────────────────────────────────────────────
로컬 저장             localStorage              AsyncStorage      (이름만 다름)
로그인 유지           쿠키(브라우저가 자동)       토큰 + 보안저장소   (직접 챙김)
API 주소             '/api/...' (같은 출처)     'https://...' 전체 주소
CORS                브라우저가 검사·차단        앱엔 없음(브라우저가 아니라서)
평문 HTTP            브라우저 경고              iOS·Android 기본 차단

결론: '로직'은 거의 그대로 옮겨진다. 달라지는 건 대부분
      "저장 위치"와 "로그인 유지 방법", 이 두 가지다.`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다(props만 바꿔 재사용).
// ReactIntroPage와 동일한 Section/Code 헬퍼를 그대로 쓴다 — 하우스 스타일 통일.
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: '상태관리 도구' },
  { n: 7, label: '웹과 같은 것·다른 것' },
];

export default function AppStateDataPage() {
  return (
    <div>
      {/* 히어로: 이 코스가 어디서 출발해 어디까지 가는지 */}
      <div className="hero">
        <div className="eyebrow">Course · 앱 개발</div>
        <h1>앱의 상태와 데이터<br />— 화면·기기·서버, 데이터의 세 집</h1>
        <p>
          앱이 다루는 데이터는 저마다 사는 집이 달라요. 방금 친 검색어는 화면이 잠깐
          기억하고, 다크모드 설정은 기기에 저장되고, 내 학습 기록은 서버에 남죠.
          이 셋을 <strong>화면 상태·기기 로컬·서버</strong>라는 세 층으로 나눠 보면
          "이 값은 어디에 둬야 하지?"가 훤해집니다. 우리가 이미 배운{' '}
          <span className="icode">useState</span>와 <span className="icode">fetch</span>를
          다리 삼아, <strong>웹 개발자의 눈으로 모바일 데이터</strong>를 읽어 봅시다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 80분</span>
          <span className="chip">실습: 세 층 분류</span>
          <span className="chip">선수 지식: React 입문(useState·fetch)</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>은 이 폰에만 오래 남는 저장,
          맨 아래 <strong>서버</strong>는 모든 기기가 공유하는 영구 보관소예요.
        </p>
        <Code>{CODE_LAYERS}</Code>
        <p>
          왜 굳이 나눌까요? 층을 잘못 고르면 바로 사고가 나거든요. 로그인 정보를 화면
          상태에만 두면 앱을 껐다 켤 때마다 로그아웃되고, 반대로 스크롤 위치까지 서버에
          저장하면 쓸데없는 요청이 폭발합니다. "이 값의 <strong>수명</strong>과{' '}
          <strong>범위</strong>는 어디까지인가?" — 이 한 질문이 층을 결정해요.
        </p>
        <svg className="diagram" viewBox="0 0 640 340" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="앱 데이터의 세 층 다이어그램. 맨 위 화면 상태는 메모리에 있고 앱을 끄면 사라진다. 가운데 기기 로컬 저장은 AsyncStorage와 SQLite로 이 기기에만 남는다. 맨 아래 서버는 Spring과 PostgreSQL로 모든 기기가 공유하며 영구 보관된다. 데이터는 아래로 저장되고 위로 읽어 온다.">
          {/* 층 3개 박스 */}
          <rect x="70" y="36" width="250" height="64" rx="10" fill="var(--card)" stroke="var(--teal)" strokeWidth="2" />
          <text x="195" y="66" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">① 화면 상태 (useState)</text>
          <text x="195" y="88" textAnchor="middle" fontSize="12" fill="var(--muted)">메모리(RAM)</text>

          <rect x="70" y="138" width="250" height="64" rx="10" fill="var(--card)" stroke="var(--primary)" strokeWidth="2" />
          <text x="195" y="168" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">② 기기 로컬 저장</text>
          <text x="195" y="190" textAnchor="middle" fontSize="12" fill="var(--muted)">AsyncStorage · SQLite · 디스크</text>

          <rect x="70" y="240" width="250" height="64" rx="10" fill="var(--card)" stroke="var(--amber)" strokeWidth="2" />
          <text x="195" y="270" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">③ 서버</text>
          <text x="195" y="292" textAnchor="middle" fontSize="12" fill="var(--muted)">Spring API · PostgreSQL</text>

          {/* 층 사이 화살표 (①↔②) */}
          <line x1="150" y1="102" x2="150" y2="130" stroke="var(--muted)" strokeWidth="2" />
          <path d="M150 136 L144 125 L156 125 Z" fill="var(--muted)" />
          <line x1="240" y1="136" x2="240" y2="108" stroke="var(--muted)" strokeWidth="2" />
          <path d="M240 102 L234 113 L246 113 Z" fill="var(--muted)" />
          <text x="142" y="124" textAnchor="end" fontSize="11" fill="var(--teal)">저장</text>
          <text x="248" y="124" textAnchor="start" fontSize="11" fill="var(--primary)">읽기</text>

          {/* 층 사이 화살표 (②↔③) */}
          <line x1="150" y1="204" x2="150" y2="232" stroke="var(--muted)" strokeWidth="2" />
          <path d="M150 238 L144 227 L156 227 Z" fill="var(--muted)" />
          <line x1="240" y1="238" x2="240" y2="210" stroke="var(--muted)" strokeWidth="2" />
          <path d="M240 204 L234 215 L246 215 Z" fill="var(--muted)" />

          {/* 오른쪽 범위/수명 설명 */}
          <text x="340" y="72" textAnchor="start" fontSize="12.5" fill="var(--muted)">앱을 끄면 사라짐</text>
          <text x="340" y="174" textAnchor="start" fontSize="12.5" fill="var(--muted)">이 기기에만 · 재시작해도 남음</text>
          <text x="340" y="276" textAnchor="start" fontSize="12.5" fill="var(--muted)">모든 기기 공유 · 영구 보관</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 위로 갈수록 잠깐 살고(화면 상태), 아래로 갈수록 오래·널리 공유된다 — 데이터는 아래로 '저장'되고 위로 '읽어' 온다</p>
        <div className="tip">
          <b>세 층 판별 연습</b> 다음 값들은 몇 층일까요? — (가) 지금 누른 탭,
          (나) "알림 켜기" 설정, (다) 내 프로필 사진. 정답: (가)=①화면 상태,
          (나)=②기기 로컬, (다)=③서버(다른 기기에서도 같은 사진이 보여야 하니까).
          이 감각이 이 코스 전체의 뼈대예요.
        </div>
      </Section>

      <Section n={2} title="화면 상태" sub="우리가 배운 useState가 앱에서도 똑같이 산다">
        <p>
          가장 위층 <strong>화면 상태</strong>는 사실 새로 배울 게 없어요. React 입문에서
          만난 <span className="icode">useState</span> 그대로거든요. 검색창 글자, 스피너
          on/off, 지금 열린 탭 — "화면을 나가면 없어져도 되는" 값이면 전부 화면 상태이고,
          <span className="icode"> useState</span>로 관리합니다.
        </p>
        <Code>{CODE_USESTATE}</Code>
        <p>
          웹에서든 앱에서든 규칙은 똑같아요. 값을 직접 고치지 말고 반드시{' '}
          <span className="icode">set</span> 함수를 부를 것 — 그래야 React가 "상태가
          바뀌었으니 화면을 다시 그리자"를 알아챕니다. 섹션 1의 "화면 = 상태의 함수"가
          모바일에서도 그대로 성립하는 거죠. 바뀌는 건 화면을 그리는 태그뿐이에요:
          웹의 <span className="icode">&lt;div&gt;</span>·<span className="icode">&lt;p&gt;</span>가
          React Native에선 <span className="icode">&lt;View&gt;</span>·<span className="icode">&lt;Text&gt;</span>로 바뀝니다.
        </p>
        <div className="tip">
          <b>이미 실력이 있다는 신호</b> "앱의 화면 상태"라는 말이 어렵게 들렸다면, 방금
          그게 그냥 <span className="icode">useState</span>였다는 걸 확인하세요. 웹에서
          쌓은 React 실력의 <strong>절반 이상이 그대로 앱으로 건너갑니다.</strong> 이게
          이 코스가 "웹 개발자가 보는 모바일"을 표방하는 이유예요.
        </div>
      </Section>

      <Section n={3} title="로컬 저장" sub="AsyncStorage · SQLite · 앱 캐시 — 이 기기에 남기기">
        <p>
          "앱을 껐다 켜도 남아야 하지만, 서버까지 갈 필요는 없는" 값들이 가운데 층에
          삽니다. 대표 도구가 <strong>AsyncStorage</strong>예요. 웹의{' '}
          <span className="icode">localStorage</span>를 써 봤다면 사고방식이 똑같습니다 —
          <strong> 문자열 키-값 창고</strong>죠. "이름표(key)에 값(value)을 적어 서랍에
          넣어 둔다"고 생각하면 돼요.
        </p>
        <Code>{CODE_LOCAL}</Code>
        <p>
          저장할 게 <strong>구조가 복잡하거나 양이 많으면</strong> AsyncStorage로는
          벅차요(전부 문자열이라 검색·정렬이 힘드니까). 그럴 땐{' '}
          <strong>SQLite</strong> — 기기 안에 들어 있는 작은 관계형 데이터베이스를 씁니다.
          우리가 배운 <span className="icode">SELECT ... WHERE</span> 같은 SQL을 폰 안에서
          그대로 쓸 수 있어요. 채팅 앱이 비행기 모드에서도 지난 대화를 보여 주는 게 보통
          이 방식입니다. 여기에 OS가 알아서 관리하는 <strong>앱 캐시</strong>(이미지·임시
          파일 저장 공간, 기기 설정에서 "캐시 지우기"로 비워지는 그것)까지 더하면 로컬
          저장의 큰 그림이 완성됩니다.
        </p>
        <div className="warn">
          <b>민감정보는 로컬 평문 금지</b> AsyncStorage와 SQLite는 기본적으로{' '}
          <strong>암호화되지 않은 평문</strong>이에요. 로그인 토큰·비밀번호를 여기 그냥
          두면, 기기를 잃어버렸을 때 통째로 새어 나갑니다. 민감정보는 반드시 OS의 보안
          저장소 — <span className="icode">iOS Keychain</span> /{' '}
          <span className="icode">Android Keystore</span>(또는{' '}
          <span className="icode">expo-secure-store</span>) — 에 넣으세요. 이건 우리{' '}
          <Link to="/learn/firewall">방화벽·보안</Link> 코스의 "민감한 건 안전한 곳에"
          원칙이 모바일로 그대로 이어지는 지점이에요.
        </div>
      </Section>

      <Section n={4} title="서버 연동" sub="우리 Spring API를 앱에서 부르기 — fetch는 그대로다">
        <p>
          맨 아래층, <strong>서버</strong>는 모든 기기가 공유하는 진짜 데이터 창고예요.
          우리 회사 백엔드는 <strong>Spring Boot + PostgreSQL</strong>이고, 웹 프론트는
          이미 <span className="icode">fetch('/api/...')</span>로 그 API를 부르고 있죠
          (React 입문의 DashboardPage 예제 기억나죠?). 좋은 소식은,{' '}
          <strong>앱에서도 fetch가 그대로 있다</strong>는 겁니다. React Native에 fetch가
          내장돼 있어요.
        </p>
        <Code>{CODE_SERVER}</Code>
        <p>
          그러니 "서버에서 데이터를 받아 <span className="icode">useState</span>에 담고
          화면을 다시 그린다"는 흐름 — React 입문의 <span className="icode">useEffect</span> +
          fetch 패턴 — 이 앱에서도 판박이예요. 달라지는 건 딱 두 가지: 주소를 전체 URL로
          적는 것, 그리고 로그인 유지 방법입니다. 우리 웹은{' '}
          <span className="icode">api/client.js</span>의{' '}
          <span className="icode">withCredentials</span>로 <strong>쿠키</strong>를 자동
          첨부하지만, 앱은 브라우저가 아니라 쿠키 자동첨부가 없어요. 그래서 로그인 때 받은{' '}
          <strong>토큰</strong>을 보안 저장소(섹션 3)에 넣어 두고, 요청마다{' '}
          <span className="icode">Authorization</span> 헤더에 직접 실어 보냅니다.
        </p>
        <div className="tip">
          <b>개발할 땐 'localhost'가 함정</b> 폰(또는 에뮬레이터)에게 "localhost"는{' '}
          <strong>내 PC가 아니라 폰 자신</strong>을 뜻해요. 그래서 개발 중엔 안드로이드
          에뮬레이터는 <span className="icode">10.0.2.2</span>, 실제 기기는 PC의 사설
          IP(예: <span className="icode">192.168.x.x</span>)로 백엔드에 접속합니다.
          "웹에선 됐는데 앱에선 서버 연결이 안 돼요"의 8할이 이 주소 문제예요.
        </div>
      </Section>

      <Section n={5} title="오프라인과 동기화" sub="신호가 끊겨도 죽지 않는 앱 — 개념만 잡기">
        <p>
          웹과 앱의 가장 큰 체감 차이가 여기예요. 웹은 대개 "인터넷이 있다"고 가정하지만,
          앱은 <strong>지하철·엘리베이터·비행기 모드</strong>에서도 손에 들려 있죠. 그래서
          좋은 앱은 "서버에 못 닿을 때"를 미리 설계합니다. 완벽히 구현하는 건 어렵지만,
          <strong>개념의 뼈대</strong>는 셋으로 단순해요: 읽기 캐시, 쓰기 큐, 그리고
          재연결 동기화.
        </p>
        <Code>{CODE_OFFLINE}</Code>
        <p>
          핵심 아이디어는 <strong>낙관적 UI(optimistic update)</strong>예요. 서버 응답을
          기다렸다가 화면을 바꾸면 느리고 답답하니, "어차피 성공하겠지"하고{' '}
          <strong>화면부터 먼저 바꾸고</strong> 요청은 뒤에서 처리하는 거죠. 만약 나중에
          실패하면 화면을 되돌립니다(rollback). 그리고 오프라인 동안 쌓인 변경들을 다시
          연결됐을 때 순서대로 흘려보내는 게 <strong>동기화(sync)</strong>예요. 이 과정에서
          같은 데이터를 두 기기가 동시에 고치는 <strong>충돌</strong>이 생기는데, 여기엔
          만능 정답이 없다는 걸 아는 것 자체가 중요합니다.
        </p>
        <div className="warn">
          <b>여기서 눈높이 맞추기</b> 2개월 만에 오프라인 동기화를 완성하는 게 목표가
          아니에요. "왜 앱은 오프라인을 신경 써야 하고, 낙관적 UI·대기 큐·충돌이라는
          <strong> 지형</strong>이 있다"는 걸 아는 게 목표입니다. 실무에선 이걸 직접
          짜기보다 <strong>TanStack Query·WatermelonDB</strong> 같은 도구의 오프라인
          기능에 기대는 경우가 많아요 — 바퀴를 다시 발명하지 않는 것도 실력이니까요.
        </div>
      </Section>

      <Section n={6} title="상태관리 라이브러리 개요" sub="useState → Context → Zustand/Redux → TanStack Query">
        <p>
          앱이 커지면 "이 상태를 저 멀리 있는 화면도 알아야 하는" 순간이 와요. 로그인
          사용자 정보처럼요. 그때마다 부모에서 자식으로 props를 몇 단계씩 넘기는 건
          고통스럽죠(이걸 <span className="icode">props drilling</span>이라 불러요). 그래서
          상태를 좀 더 넓게 공유하는 <strong>도구의 사다리</strong>가 있습니다.
        </p>
        <Code>{CODE_STATE_LIBS}</Code>
        <p>
          여기서 중요한 구분 하나. <strong>클라이언트 상태</strong>(내 화면이 만든 값:
          열린 탭, 입력 중인 글)와 <strong>서버 상태</strong>(서버가 주인인 값: 코스 목록,
          내 프로필)는 성격이 달라요. 서버 상태는 "캐시해 두고, 낡으면 다시 받아오고,
          로딩·에러를 관리하는" 일이 반복돼서, 그걸 전문으로 해 주는{' '}
          <strong>TanStack Query</strong> 같은 도구를 씁니다. 반면 순수 클라이언트 상태는{' '}
          <strong>Zustand·Redux</strong> 쪽이죠. 우리 mirim-app은 아직 이 큰 도구들
          없이 <span className="icode">useState</span>와 Context(<span className="icode">AuthContext</span>)만으로
          충분한 규모예요.
        </p>
        <div className="tip">
          <b>우리 코드에서 확인</b> 에디터로{' '}
          <span className="icode">frontend/src/AuthContext.jsx</span>를 열어 보세요.
          로그인한 사용자 정보를 <strong>Context</strong>로 앱 전체에 뿌리고, 각 화면은{' '}
          <span className="icode">useAuth()</span>로 꺼내 씁니다. "props를 층층이 넘기지
          않고 필요한 곳에서 바로 꺼내 쓰는" 상태관리의 가장 작은 형태를, 이미 우리 앱이
          쓰고 있는 거예요. 이 개념도 <strong>웹과 앱이 100% 공유</strong>합니다.
        </div>
      </Section>

      <Section n={7} title="웹과 같은 것 · 다른 것" sub="옮겨지는 것 vs 새로 챙길 것 — 지형 정리">
        <p>
          마지막으로 오늘 배운 걸 "웹 지식 대비 얼마나 옮겨지나"로 정리해 봅시다. 결론부터
          말하면 <strong>대부분 그대로 옮겨져요.</strong> 화면 상태, 서버 호출, 상태관리
          라이브러리는 웹과 앱이 <strong>같은 도구·같은 문법</strong>을 씁니다. 진짜로
          달라지는 건 "데이터를 <strong>어디에</strong> 저장하나"와 "로그인을{' '}
          <strong>어떻게</strong> 유지하나" 정도예요.
        </p>
        <Code>{CODE_WEB_VS_APP}</Code>
        <p>
          그래서 "2개월 만에 네이티브 앱 전문가"는 과장이지만, "웹에서 쌓은 실력으로 앱의
          지형을 읽고, 내 손으로 앱까지 가는 길을 아는" 것은 충분히 현실적인 목표입니다.
          오늘 세운 <strong>세 층 지도</strong> 하나면, 앞으로 어떤 앱 튜토리얼을 봐도
          "아, 이건 화면 상태 얘기구나 / 이건 로컬 저장이네 / 이건 서버 연동이구나" 하고
          제자리에 꽂아 넣을 수 있어요.
        </p>
        <div className="tip">
          <b>미니 실습</b> 여러분이 매일 쓰는 앱 하나(인스타그램·카카오톡·유튜브 등)를
          떠올려, 화면에 보이는 데이터 5개를 골라 세 층으로 분류해 보세요. 예: 좋아요 수(③
          서버), 다크모드 설정(② 로컬), 지금 보고 있는 탭(① 화면 상태). 정답을 맞히는
          것보다 "왜 그 층인지"를 한 줄로 말할 수 있는 게 핵심입니다. 멘토에게 그 표를
          공유하면 좋은 대화가 시작돼요.
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>📱 여기까지 왔다면</h3>
          <p className="muted">
            이제 앱의 데이터를 <strong>화면 상태·기기 로컬·서버</strong> 세 층으로 나눠
            보는 눈이 생겼어요. 그리고 그 대부분이 우리가 웹에서 이미 배운{' '}
            <span className="icode">useState</span>·<span className="icode">fetch</span>·Context
            그대로라는 것도요. 남은 건 "저장 위치"와 "로그인 유지"라는 두 갈래의 차이뿐.
            다음은 이 지식을 가장 적은 비용으로 실전에 써 보는 길 —{' '}
            <Link to="/learn/pwa"><strong>PWA(설치되는 웹앱)</strong></Link> 코스로
            이어집니다. 우리가 만든 React 웹을 <strong>설치·오프라인 지원</strong>까지
            갖춘 '앱처럼' 만드는, 웹 개발자에게 가장 가까운 첫 번째 앱을 만나 보세요.
          </p>
        </div>
      </div>
    </div>
  );
}
