// 이 파일이 하는 일: "미니 프로젝트: 설계부터 배포까지" 코스 — 11주간 배운 조각(React·API·DB·git)을
// 하나의 실제 서비스로 조립하는 마무리 강좌. 기획을 유저 스토리로 쪼개는 것에서 출발해,
// 명세 → 커밋 전략 → 프론트·백 조립(CORS) → 배포 준비 → 실제 배포 → 배포 후 운영 → 회고까지
// 8개 섹션으로 안내하는 정적 학습 페이지. 프레이밍은 "포트폴리오가 되는 첫 서비스".
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 styles/global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용하고 색은 var(--*)만 쓴다(하드코딩 금지).
// 예제 코드는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

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

const CODE_STORY = `아이디어: "내 블로그 서비스를 만들고 싶다"
        ↓ 유저 스토리로 쪼개기 — "누가, 무엇을, 왜"

"방문자는 글 목록을 최신순으로 볼 수 있다"          [필수]  난이도 하
"방문자는 글 하나를 눌러 본문을 읽을 수 있다"        [필수]  난이도 하
"나는 로그인해서 글을 쓰고 지울 수 있다"            [필수]  난이도 중
"방문자는 글에 댓글을 달 수 있다"                  [나중]  난이도 중
"글을 카테고리로 묶어서 볼 수 있다"                [나중]  난이도 중
"다크 모드를 지원한다"                            [안 함] 난이도 하

핵심: [안 함]과 [나중] 목록이 [필수]보다 중요합니다.
기능을 포기하는 게 아니라, '끝나는 날'을 지키는 거예요.
전부 만들다 미완성으로 끝난 프로젝트는 포트폴리오가 못 됩니다.`;

const CODE_SPEC = `화면에서 '거꾸로' API를 도출한다:

[글 목록 화면]  이 화면에 뭐가 필요하지?  →  GET  /api/posts
[글 상세 화면]  글 하나의 본문이 필요해   →  GET  /api/posts/1
[글쓰기 화면]   작성한 글을 저장해야 해    →  POST /api/posts

명세서 양식 (3강에서 배운 그대로 재사용):
메서드  경로            요청 본문             응답
GET    /api/posts      -                    글 배열 (id·제목·날짜)
GET    /api/posts/1    -                    글 하나 (본문 포함)
POST   /api/posts      제목·본문 (+토큰)      만든 글 · 201 Created
DELETE /api/posts/1    (+토큰)              204 No Content

이 표가 곧 '프론트와 백엔드의 계약서'입니다.
계약서가 있으면 두 쪽을 따로 만들어도 나중에 맞아 들어가요.`;

const CODE_COMMIT = `# 나쁜 하루 — 저녁에 몰아서 한 번
git add .
git commit -m "오늘 작업"        # 한 달 뒤의 나: "...뭘 했다는 거지?"

# 좋은 하루 — 의미 단위로 여러 번
git commit -m "feat: 글 목록 API 연동"
git commit -m "fix: 빈 목록일 때 안내 문구 표시"
git commit -m "feat: 글 상세 페이지 라우팅"

# 브랜치 기본 흐름 — main은 '항상 돌아가는 상태'로 지킨다
git switch -c feature/post-detail   # 새 브랜치에서 작업 시작
# ... 작업하고 커밋 (여러 번) ...
git switch main
git merge feature/post-detail       # 다 되면 main에 합치기`;

const CODE_CORS = `Access to fetch at 'http://localhost:8080/api/posts'
from origin 'http://localhost:5173' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the
requested resource.

번역: "프론트(5173)와 백엔드(8080)는 서로 다른 출처(origin)인데,
백엔드가 '5173 출신 요청 허용'이라고 밝히지 않아서 브라우저인 내가
응답을 압수했다."

해결은 항상 '서버 쪽'에서 — (Spring Boot 예)
@CrossOrigin(origins = "http://localhost:5173")   // 컨트롤러에 붙이거나
// 전역 CORS 설정에 허용할 출처를 등록한다.
// 프론트 코드를 아무리 고쳐도 CORS는 안 풀립니다. 서버의 허락이니까요.`;

const CODE_ENV = `// 나쁜 예 — 주소를 코드에 박아 두면, 배포 날 전부 찾아 바꿔야 한다
fetch('http://localhost:8080/api/posts')

// 좋은 예 — 주소는 환경변수로, 코드는 그대로
// .env.development  →  VITE_API_URL=http://localhost:8080
// .env.production   →  VITE_API_URL=https://api.myblog.dev
const API = import.meta.env.VITE_API_URL;
fetch(API + '/api/posts')

// 같은 코드가 내 컴퓨터에선 localhost로, 운영에선 실서버로 향합니다.
// "환경마다 달라지는 값은 코드 밖으로" — 배포의 제1원칙이에요.`;

const CODE_PROXY = `// vite.config.js — 개발 프록시: 프론트 개발 서버가 /api를 대신 전달
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api': 'http://localhost:8080',   // /api/* 요청을 백엔드로 넘겨 줘
    },
  },
});

// 브라우저 입장에선 5173 하고만 대화 → 출처가 하나 → CORS가 아예 안 생김
// 운영에선 게이트웨이·웹서버가 이 '중간 전달자' 역할을 그대로 이어받습니다.`;

const CODE_BUILD = `npm run build     # dist/ 폴더에 배포용 정적 파일이 만들어진다

개발 모드 (npm run dev)          프로덕션 빌드 (npm run build)
──────────────────────────────────────────────────────────
소스 그대로 · 즉시 새로고침        압축·난독화 · 파일명에 지문(hash)
에러 메시지가 친절하고 김          에러 메시지 최소화 (내부 노출 방지)
나 혼자 쓰니까 느려도 됨           성능이 곧 사용자 경험
환경변수 .env.development        환경변수 .env.production

포인트: 개발 모드는 '나를 위한 모드', 빌드는 '사용자를 위한 모드'.
배포 전에 반드시 빌드 결과물로 한 번 열어 봐야(npm run preview) 합니다.`;

const CODE_SECRET = `# .gitignore 에 반드시 — 시크릿 파일은 저장소에 올리지 않는다
.env
.env.production

# .env.example 은 올린다 — '어떤 키가 필요한지' 목록만, 값은 비워서
# DB_PASSWORD=            ← 이렇게. 동료는 이걸 복사해 자기 값을 채운다

# 경고: 한 번이라도 커밋된 비밀번호는 '지워도' git 이력에 영원히 남습니다.
# 실수로 올렸다면 → 커밋 삭제가 아니라 '비밀번호 교체'가 정답이에요.`;

const CODE_DEPLOY = `# ① 백엔드 + DB — 클라우드 서버(예: EC2 한 대)에 올리기
./mvnw -q -DskipTests package              # 서버용 jar 빌드
scp target/blog.jar ubuntu@서버IP:~/app/    # 서버로 복사
ssh ubuntu@서버IP
java -jar app/blog.jar                     # (실무에선 서비스로 등록해 상시 실행)

# ② 프론트 — 정적 호스팅(Netlify·Vercel·S3 등)에 배포
VITE_API_URL=https://api.myblog.dev npm run build
# → dist/ 를 업로드하면 끝. 정적 파일은 서버 프로세스가 필요 없어요.

# ③ HTTPS 확인 — 요즘 웹의 기본값
curl -I https://myblog.dev                 # 200 OK + 주소창 자물쇠 확인`;

const CODE_LOG = `# 친구: "글쓰기 버튼 눌렀는데 아무 일도 안 일어나!"
# 내 컴퓨터에선 재현이 안 된다 → 서버 로그를 본다

tail -f app.log                  # 실시간으로 로그 지켜보기
grep "ERROR" app.log | tail -20  # 최근 에러만 추려 보기

2026-07-20 21:14:03 ERROR PostController -
IllegalArgumentException: title must not be null
        at PostService.create(PostService.java:31)

읽는 법: 언제(21:14) · 어디서(PostController→PostService 31행) ·
무엇이(title이 null). → 프론트가 제목 없이 POST를 보냈다
→ 프론트에 유효성 검사를 넣고 핫픽스 배포. 로그가 곧 목격자입니다.`;

const CODE_README = `# 미림 블로그
글을 쓰고 나누는 미니 블로그 — 데모: https://myblog.dev
(첫 화면 스크린샷 1~2장)

## 기술 스택
React 19 · Vite · Spring Boot · PostgreSQL

## 문제 → 해결 → 배운 점
- 문제: 배포하니 상세 페이지에서 새로고침하면 404가 떴다
  해결: 호스팅에 SPA 폴백(모든 경로 → index.html) 설정
  배운 점: 라우팅은 브라우저만이 아니라 '서버도' 알아야 한다

- 문제: 로컬에선 되던 API가 배포 후 CORS로 막혔다
  해결: 서버 허용 출처에 운영 도메인을 추가
  배운 점: CORS는 프론트가 아니라 서버가 푸는 문제다

## 다음에 다르게 할 것
- 기능을 다 만들고 배포하는 대신, 1주차에 '빈 화면'부터 배포하기`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: '화면·API 명세' },
  { n: 3, label: '마일스톤·커밋' },
  { n: 4, label: '프론트·백 조립' },
  { n: 5, label: '배포 준비' },
  { n: 6, label: '실제 배포' },
  { n: 7, label: '배포 후' },
  { n: 8, label: '회고·포트폴리오' },
];

export default function Lv2MiniProjectDeployPage() {
  return (
    <div>
      {/* 히어로: 이 코스가 어디서 출발해 어디까지 가는지 */}
      <div className="hero">
        <div className="eyebrow">Course · 중급 과정</div>
        <h1>🚀 미니 프로젝트<br />— 설계부터 배포까지</h1>
        <p>
          11주 동안 화면을 만들고, API를 부르고, DB에 저장하는 법을 각각 배웠어요. 이번 주엔
          그 조각들을 <strong>하나의 진짜 서비스</strong>로 조립합니다. 기획을 기능 목록으로
          자르고, 조각을 이어 붙이고, <strong>실제 URL로 배포</strong>해서 친구가 써 보게
          하고, 회고까지 — 끝나면 여러분 손에 <strong>포트폴리오가 되는 첫 서비스</strong>가
          남습니다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 90분</span>
          <span className="chip">실습: 내 서비스를 실제 URL로 배포</span>
          <span className="chip">선수 지식: React·API·git 기초</span>
        </div>
      </div>

      {/* 섹션 바로가기 — 학습 포인트: SECTIONS 배열 하나로 링크를 map 렌더링 */}
      <div className="chip-row">
        {SECTIONS.map((s) => (
          <a key={s.n} className="chip" href={`#sec-${s.n}`}>
            {s.n}. {s.label}
          </a>
        ))}
      </div>

      <Section n={1} title="기획을 기능 목록으로" sub="아이디어를 유저 스토리로 쪼개기">
        <p>
          프로젝트가 산으로 가는 첫 번째 이유는 코드가 아니라 <strong>기획이 덩어리째</strong>인
          거예요. "블로그를 만든다"는 문장으론 오늘 뭘 해야 할지 알 수 없죠. 그래서 아이디어를{' '}
          <strong>유저 스토리</strong> — "누가, 무엇을 할 수 있다" 형태의 문장 — 로 쪼갭니다.
          문장 하나가 만들 기능 하나가 되도록요.
        </p>
        <Code>{CODE_STORY}</Code>
        <p>
          쪼갰으면 이제 <strong>MVP(최소 기능 제품)를 자릅니다.</strong> 위 예시에서 [필수]
          세 줄만 있어도 "글을 쓰고 읽는 블로그"는 성립해요. 나머지는 붙이면 좋은 장식이고요.
          역설적이지만 <strong>'이번엔 안 만든다' 목록이 더 중요합니다.</strong> 그 목록이
          없으면 마감 전날의 여러분이 다크 모드를 만지고 있을 테니까요.
        </p>
        <p>
          마지막으로 각 스토리에 <strong>난이도(상·중·하)와 우선순위</strong>를 매기세요.
          난이도 '하'인 [필수]부터 시작하면 초반에 완성 경험이 쌓여 탄력이 붙고, 난이도 '상'을
          일찍 발견하면 일정을 미리 조정할 수 있습니다.
        </p>
        <div className="tip">
          <b>판별법</b> 어떤 기능이 MVP인지 헷갈리면 이렇게 물어보세요 — "이게 없으면 이
          서비스가 <strong>서비스로서 성립하지 않는가?</strong>" 성립한다면 [나중]입니다.
          블로그는 댓글 없이도 블로그지만, 글 읽기가 없으면 블로그가 아니에요.
        </div>
      </Section>

      <Section n={2} title="화면과 API 명세 먼저" sub="코드보다 계약서를 먼저 쓴다">
        <p>
          기능 목록이 생겼으면 바로 코딩하고 싶겠지만, 한 단계만 참으세요.{' '}
          <strong>손그림 와이어프레임</strong>으로 화면 흐름부터 확정합니다. 종이에 네모
          몇 개면 충분해요 — "목록 화면에서 글을 누르면 상세로, 글쓰기 버튼은 로그인해야
          보이고..." 이 흐름을 그려 보면 <strong>빠뜨린 화면</strong>(로그인은? 글이 0개일
          땐?)이 코드 짜기 전에 드러납니다.
        </p>
        <p>
          화면이 정해지면 <strong>API는 화면에서 거꾸로 도출</strong>돼요. "이 화면을 그리려면
          무슨 데이터가 필요하지?"를 화면마다 물으면 필요한 API 목록이 저절로 나옵니다.
        </p>
        <Code>{CODE_SPEC}</Code>
        <p>
          양식은 3강에서 배운 명세서 그대로 재사용하면 됩니다. 이 표는{' '}
          <strong>프론트와 백엔드 사이의 계약서</strong>예요. 계약서가 있으면 프론트는 가짜
          데이터로 화면을 먼저 만들고, 백엔드는 표대로 API를 만들어서, 나중에 둘을 붙이기만
          하면 맞아 들어갑니다.
        </p>
        <div className="tip">
          <b>혼자 프로젝트여도 명세를 쓰는 이유</b> 일주일 뒤의 나는 남입니다. "POST에 뭘
          보내기로 했더라?"를 기억에 의존하면 프론트와 백엔드가 <strong>서로 다른 계약</strong>을
          구현하게 돼요. 표 한 장이 그 어긋남을 막습니다.
        </div>
      </Section>

      <Section n={3} title="마일스톤과 커밋 전략" sub="'항상 돌아가는 상태'를 지키는 개발 리듬">
        <p>
          4주짜리 프로젝트를 통째로 만들고 마지막에 실행해 보는 것만큼 위험한 계획은 없어요.
          대신 <strong>'항상 돌아가는 상태'를 유지하는 작은 단위</strong>로 갑니다. 1주차 끝엔
          글 목록만 나오는 앱이, 2주차 끝엔 상세까지 되는 앱이 <strong>돌아가는 채로</strong>{' '}
          존재하는 거예요. 무언가 깨져도 "어제까진 됐다"는 확실한 복귀 지점이 있고요.
        </p>
        <p>
          그 리듬을 기록하는 도구가 <strong>커밋</strong>입니다. 커밋은 저장 버튼이 아니라{' '}
          <strong>의미 있는 변화 한 덩이</strong>예요. 메시지 규칙은 우리가 계속 봐 온 그대로 —
          새 기능은 <span className="icode">feat:</span>, 버그 수정은{' '}
          <span className="icode">fix:</span>.
        </p>
        <Code>{CODE_COMMIT}</Code>
        <p>
          브랜치는 이 원칙을 지키는 안전장치입니다. <span className="icode">main</span>은 항상
          돌아가는 버전으로 남겨 두고, 새 기능은 <strong>브랜치에서 작업하다가 완성되면
          합치는</strong>(merge) 흐름이 기본이에요. 실험이 실패하면 브랜치만 버리면 되니
          부담 없이 시도할 수 있습니다.
        </p>
        <div className="tip">
          <b>커밋 크기 감각</b> "커밋 메시지를 한 문장으로 못 쓰겠다"면 커밋이 너무 큰
          거예요. 반대로 <span className="icode">fix: 오타</span>가 하루 20개면 너무 잘게 썬
          거고요. <strong>유저 스토리 하나를 커밋 2~5개로</strong> 만드는 정도가 좋은
          출발점입니다.
        </div>
      </Section>

      <Section n={4} title="프론트·백 조립" sub="CORS라는 통과 의례, 그리고 환경변수">
        <p>
          이제 각자 만들던 프론트와 백엔드를 연결합니다. 그리고 거의 모든 사람이 여기서{' '}
          <strong>같은 벽</strong>에 부딪혀요. 콘솔을 빨갛게 물들이는 그 이름, CORS.
        </p>
        <Code>{CODE_CORS}</Code>
        <p>
          CORS는 버그가 아니라 <strong>브라우저의 보안 규칙</strong>이에요. 브라우저는 "다른
          출처(도메인·포트가 다르면 다른 출처!)에서 온 응답은, 그 서버가 명시적으로 허락한
          경우에만 JS에게 준다"는 원칙을 지킵니다. 그래서 해결도 항상{' '}
          <strong>서버 쪽에서 허용 출처를 등록</strong>하는 것이지, 프론트 코드를 고치는 게
          아니에요.
        </p>
        <p>
          연결이 되면 다음 함정은 <strong>주소 하드코딩</strong>입니다. 코드 곳곳에{' '}
          <span className="icode">localhost:8080</span>이 박혀 있으면 배포하는 날 전부 찾아
          바꿔야 하죠. API 주소는 <strong>환경변수로 분리</strong>합니다.
        </p>
        <Code>{CODE_ENV}</Code>
        <p>
          그리고 로컬 개발을 매끄럽게 해 주는 마지막 조각, <strong>개발 프록시</strong>예요.
          프론트 개발 서버가 <span className="icode">/api</span> 요청을 백엔드로 대신
          전달하게 하면, 브라우저는 출처 하나와만 대화하니 CORS가 아예 생기지 않습니다.
        </p>
        <Code>{CODE_PROXY}</Code>
        <div className="tip">
          <b>에러를 읽는 태도</b> CORS 에러 메시지엔 어느 출처에서 어디로 보낸 요청이 왜
          막혔는지가 <strong>전부 적혀</strong> 있어요. 빨간 글씨를 닫지 말고 한 줄씩
          번역해 보는 습관 — 이게 이번 프로젝트에서 얻어 갈 가장 큰 근육입니다.
        </div>
      </Section>

      <Section n={5} title="배포 준비" sub="개발 모드와 헤어질 결심, 그리고 시크릿 관리">
        <p>
          <span className="icode">npm run dev</span>로 보던 화면을 그대로 서버에 올리면 될까요?
          안 됩니다. 개발 모드는 <strong>나를 위한 모드</strong>예요 — 소스를 압축하지 않고,
          에러를 장황하게 보여 주고, 즉시 새로고침을 위해 무거운 도구를 달고 있죠. 사용자에게
          내보내는 건 <strong>프로덕션 빌드</strong>입니다.
        </p>
        <Code>{CODE_BUILD}</Code>
        <p>
          빌드와 함께 챙길 것이 <strong>개발·운영 환경 분리</strong>예요. DB 주소, API 주소,
          로그 수준 — 환경마다 달라지는 값은 전부 <span className="icode">.env</span> 계열
          파일로 나눠 관리합니다. 개발용은{' '}
          <span className="icode">.env.development</span>, 운영용은{' '}
          <span className="icode">.env.production</span>. 코드는 하나, 설정만 갈아 끼우는
          구조죠.
        </p>
        <p>
          그리고 이 강좌에서 딱 하나만 기억해야 한다면 이것입니다.{' '}
          <strong>시크릿(DB 비밀번호, API 키)은 절대 커밋하지 않는다.</strong>
        </p>
        <Code>{CODE_SECRET}</Code>
        <div className="tip">
          <b>왜 '교체'가 정답인가</b> git은 이력을 보존하는 도구라, 다음 커밋에서 지워도{' '}
          <strong>과거 커밋엔 그대로</strong> 남아요. 저장소를 본 적 있는 모든 사람(과 봇)이
          그 비밀번호를 가졌다고 가정해야 합니다. 그래서 유출된 시크릿은 지우는 게 아니라{' '}
          <strong>새 값으로 바꾸는</strong> 것이 유일한 수습이에요.
        </div>
      </Section>

      <Section n={6} title="실제 배포" sub="내 서비스에 진짜 주소가 생기는 날">
        <p>
          드디어 인터넷에 올립니다. 우리 구조는 두 갈래예요. <strong>백엔드와 DB는 클라우드
          서버</strong>(계속 켜져 있어야 하는 프로세스니까)에, <strong>프론트는 정적
          호스팅</strong>(빌드 결과물은 그냥 파일이니까)에 올립니다.
        </p>
        <Code>{CODE_DEPLOY}</Code>
        <p>
          프론트를 올릴 때 핵심은 <strong>빌드 시점에 운영 API 주소를 넣는 것</strong>이에요.
          섹션 4에서 분리해 둔 <span className="icode">VITE_API_URL</span>이 여기서 빛을
          냅니다 — 코드 수정 없이 환경변수만 바꿔 빌드하면 프론트가 실서버를 바라봐요.
        </p>
        <p>
          마지막 확인은 <strong>HTTPS</strong>. 주소창의 자물쇠는 장식이 아니라 "사용자와 서버
          사이의 대화를 중간에서 <strong>훔쳐보거나 바꿀 수 없다</strong>"는 보증입니다.
          로그인 비밀번호가 오가는 서비스라면 선택이 아니라 기본값이에요. 요즘 정적 호스팅은
          HTTPS를 자동으로 켜 주지만, <strong>백엔드 API 주소도 https인지</strong>까지 꼭
          확인하세요 — 페이지는 https인데 API가 http면 브라우저가 혼합 콘텐츠로 차단합니다.
        </p>
        <div className="tip">
          <b>배포는 이벤트가 아니라 반복</b> 첫 배포에서 한 번에 다 되길 기대하지 마세요.
          보통 <strong>대여섯 번의 시행착오</strong>(환경변수 누락, CORS에 운영 도메인 추가
          누락, 새로고침 404...)가 정상입니다. 그래서 마감 전날이 아니라{' '}
          <strong>1~2주차에 '빈 화면'부터 배포</strong>해 두는 팀이 이깁니다.
        </div>
      </Section>

      <Section n={7} title="배포 후: 진짜 시작" sub="첫 사용자, 첫 버그, 첫 핫픽스">
        <p>
          배포가 끝이 아니라 <strong>여기서부터가 진짜</strong>예요. 친구 두 명에게 URL을
          보내고 아무 설명 없이 써 보게 하세요. 장담하는데, 10분 안에 여러분이 한 번도 못 본
          버그가 나옵니다. 내가 만든 순서대로만 써 온 나와 달리, 사용자는{' '}
          <strong>내가 상상 못 한 순서</strong>로 누르거든요.
        </p>
        <p>
          "안 돼요"라는 제보를 받으면 어디를 볼까요? 사용자의 화면은 못 보지만,{' '}
          <strong>서버 로그</strong>는 모든 요청의 목격자입니다.
        </p>
        <Code>{CODE_LOG}</Code>
        <p>
          원인을 찾아 고쳤으면 <strong>핫픽스 배포</strong> — 고치고, 커밋하고(
          <span className="icode">fix:</span>), 다시 배포하는 한 바퀴를 직접 돌아 보세요.
          이 사이클을 한 번이라도 돌아 본 사람과 아닌 사람은 실무 감각이 완전히 다릅니다.
        </p>
        <p>
          그리고 이번 주 내내 만나게 될 문장, <strong>"내 컴퓨터에선 됐는데..."</strong>의
          원인은 대개 이 유형들이에요. <strong>① 환경 차이</strong> — 내 PC에만 있는 파일·설정·
          환경변수(운영 <span className="icode">.env</span> 누락이 단골). <strong>② 데이터
          차이</strong> — 내 DB엔 늘 데이터가 있지만 실서버 첫 화면은 글 0개, 빈 배열에서
          터지는 코드. <strong>③ 주소·경로 차이</strong> — 하드코딩된 localhost, http/https
          혼용, 새로고침 404. 셋 다 공통점은 <strong>코드가 아니라 코드를 둘러싼 환경</strong>이
          문제라는 것 — 그래서 로그와 설정부터 의심하는 게 지름길입니다.
        </p>
        <div className="tip">
          <b>피드백 받는 요령</b> "어때?"라고 물으면 "좋네"만 돌아와요. 대신{' '}
          <strong>과제를 주세요</strong> — "글 하나 쓰고, 지워 봐 줘." 그리고 막히는 순간에
          입을 떼지 말고 지켜보세요. 사용자가 3초 머뭇거린 그 화면이 여러분의 다음 이슈입니다.
        </div>
      </Section>

      <Section n={8} title="회고와 포트폴리오" sub="코드는 잊혀도 README는 남는다">
        <p>
          마지막 조각은 코드가 아니라 <strong>글</strong>입니다. 몇 달 뒤 이 프로젝트를 보게 될
          사람(면접관, 동료, 그리고 미래의 나)은 코드 1만 줄을 읽어 주지 않아요.{' '}
          <strong>README</strong>가 곧 프로젝트의 얼굴입니다. 구조는{' '}
          <strong>문제 → 해결 → 배운 점</strong> — "뭘 만들었다"보다 "뭘 만났고 어떻게
          넘었다"가 백 배 힘이 셉니다.
        </p>
        <Code>{CODE_README}</Code>
        <p>
          필수 요소 세 가지 — <strong>스크린샷</strong>(안 열어 봐도 뭔지 알게),{' '}
          <strong>데모 URL</strong>(직접 눌러 볼 수 있게), <strong>기술 스택</strong>(뭘 다뤄
          봤는지 한눈에). 이 셋이 있는 저장소와 없는 저장소는 받는 대접이 다릅니다.
        </p>
        <p>
          그리고 회고. 거창할 필요 없이 <strong>"다음에 다르게 할 것" 딱 하나</strong>만
          뽑으세요. "테스트를 미루지 않기"든 "배포를 1주차에 하기"든 — 하나를 골라 적는 행위
          자체가, 이번 경험을 다음 프로젝트의 실력으로 바꿔 줍니다. 열 개의 반성보다 실행되는
          하나가 낫습니다.
        </p>
        <div className="tip">
          <b>회고 3질문</b> ① 계획대비 뭐가 달랐나(예측 훈련) ② 가장 오래 막힌 곳은
          어디였고 결국 뭐가 문제였나(디버깅 복기) ③ 다음에 다르게 할 것 하나(실행 항목).
          30분이면 충분하고, 이 세 답이 그대로 README의 뼈대가 됩니다.
        </div>
      </Section>

      {/* 마무리 카드 — 다음 행선지 안내 */}
      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>🚀 여기까지 왔다면</h3>
          <p className="muted">
            축하해요. 이제 여러분에겐 <strong>실제 URL로 열리는 서비스</strong>가 있습니다.
            아이디어를 유저 스토리로 자르고, 명세라는 계약서를 쓰고, 항상 돌아가는 상태를
            지키며 조립하고, CORS와 환경변수를 넘어 배포하고, 로그로 버그를 쫓고, README로
            갈무리하는 — <strong>서비스 하나의 전체 생애</strong>를 한 바퀴 돌았어요. 이
            사이클은 규모가 커져도 모양이 같습니다. 앞으로 만들 모든 것의 축소판을 이미 손에
            쥔 셈이에요. 다음 여정은 <Link to="/learn"><strong>학습 센터</strong></Link>에서 —
            방금 배포한 그 서비스를 더 단단하게 만들 다음 강좌를 골라 이어 가 봅시다.
          </p>
        </div>
      </div>
    </div>
  );
}
