// 이 파일이 하는 일: "SQL 입문" 코스 — 데이터베이스가 뭔지부터 시작해
// SELECT/WHERE/ORDER BY/쓰기 3형제/JOIN/JPA와의 관계/psql 실습까지 8개 섹션으로 안내하는 정적 학습 페이지.
// 특징: 예제 테이블이 전부 '이 학습 플랫폼의 진짜 스키마'다 —
// users / assignment / submission / checklist_item / progress.
// 여러분이 체크리스트를 하나 누를 때마다 progress 테이블에 행이 하나 생긴다. 그걸 직접 조회해 본다.
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용한다 — 색을 여기서 하드코딩하지 않는다.
// SQL 예제·표는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.

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

// ── SQL 예제 · 표 상수들 ──

const CODE_TABLES = `우리 학습 플랫폼의 진짜 테이블 5개 (PostgreSQL, JPA가 생성)

테이블            무엇을 담나                      주요 열
──────────────────────────────────────────────────────────────────
users            계정 (여러분!)                   id, username, password_hash,
                                                 name, role, track, created_at
assignment       과제                             id, week, day, kind, title, summary
submission       과제 제출물                      id, assignment_id, user_id, content,
                                                 link, submitted_at, feedback, status
checklist_item   주차별 체크리스트 항목            id, week, day, label, sort_order
progress         "체크했다"는 기록                 id, user_id, item_id, checked_at

→ 엑셀로 치면: 파일 하나(데이터베이스) 안에 시트 5장(테이블).
  시트의 행 하나 = 데이터 한 건, 열 하나 = 속성.`;

const CODE_SELECT_BASIC = `-- 기본형: SELECT 보고싶은열 FROM 테이블;

-- users 테이블에서 이름과 트랙만 보기
SELECT name, track FROM users;

   name   | track
----------+--------
 김미림   | FE
 이마이   | BE
 ...

-- * 는 "열 전부 다"라는 뜻 (편하지만 실무에선 필요한 열만!)
SELECT * FROM users;`;

const CODE_SELECT_HASH = `-- password_hash 열을 구경해 보면...
SELECT username, password_hash FROM users;

 username |            password_hash
----------+--------------------------------------
 mirim01  | $2a$10$N9qo8uLOickgx2ZMRZoMye...

-- 비밀번호가 아니라 뜻 모를 문자열이 저장돼 있어요.
-- 원래 비밀번호는 DB 어디에도 없습니다 — 해시만 저장!
-- (보안 코스에서 배운 "비밀번호는 해시로"가 바로 이 열이에요.)`;

const CODE_WHERE = `-- WHERE = 조건에 맞는 행만 골라내기 (엑셀의 '필터'와 같은 역할)

-- 1주차 과제만
SELECT title FROM assignment WHERE week = 1;

-- MAIN 과제만 (문자열은 작은따옴표!)
SELECT title FROM assignment WHERE kind = 'MAIN';

-- 1주차이면서 MAIN인 과제 (AND = 둘 다 만족)
SELECT title FROM assignment WHERE week = 1 AND kind = 'MAIN';

-- 1주차 또는 2주차 (OR = 하나만 맞아도)
SELECT title FROM assignment WHERE week = 1 OR week = 2;

-- 특정 학생(user_id가 3번)의 제출물만
SELECT content, status FROM submission WHERE user_id = 3;`;

const CODE_WHERE_OPS = `자주 쓰는 비교 연산자

연산자    뜻                예시
─────────────────────────────────────────────────────
=        같다              WHERE week = 1
<>       같지 않다          WHERE status <> 'DONE'
>  <     크다/작다          WHERE week > 2
>=  <=   이상/이하          WHERE week <= 3
LIKE     패턴 검색          WHERE title LIKE '%Git%'
                           (% = 아무 글자나 몇 개든)

-- LIKE 예: 제목에 'Git'이 들어간 과제 전부
SELECT title FROM assignment WHERE title LIKE '%Git%';`;

const CODE_ORDER_LIMIT = `-- ORDER BY = 정렬, LIMIT = 위에서 N개만 자르기

-- 제출물을 최신순으로 (DESC = 내림차순, ASC = 오름차순이 기본)
SELECT id, user_id, submitted_at
FROM submission
ORDER BY submitted_at DESC;

-- "최근 제출 5건" — 정렬하고 5개만!
SELECT id, user_id, submitted_at
FROM submission
ORDER BY submitted_at DESC
LIMIT 5;

-- 조합 순서 기억: FROM → WHERE → ORDER BY → LIMIT
-- (골라내고 → 정렬하고 → 자른다)
SELECT title FROM assignment
WHERE kind = 'MAIN'
ORDER BY week ASC
LIMIT 3;`;

const CODE_WRITE = `-- 쓰기 3형제: INSERT(추가) / UPDATE(수정) / DELETE(삭제)

-- INSERT: 체크리스트 항목 하나 추가
INSERT INTO checklist_item (week, day, label, sort_order)
VALUES (1, 1, 'SQL 입문 코스 읽기', 10);

-- UPDATE: 3번 제출물의 상태를 바꾸기 (WHERE 필수!!)
UPDATE submission SET status = 'REVIEWED' WHERE id = 3;

-- DELETE: 방금 실수로 넣은 항목 지우기 (WHERE 필수!!)
DELETE FROM checklist_item WHERE label = 'SQL 입문 코스 읽기';`;

const CODE_TX = `-- 트랜잭션 = "일단 해보고, 마음에 안 들면 되돌리기"

BEGIN;                                   -- 되돌리기 지점 만들기
UPDATE submission SET status = 'DONE';   -- 앗, WHERE를 빼먹었다!
SELECT status FROM submission LIMIT 5;   -- 전부 DONE이 돼버림...
ROLLBACK;                                -- 휴, 전부 원상복구!

-- 반대로 확정하고 싶으면 COMMIT;
-- 실무에서 UPDATE/DELETE 전에 BEGIN부터 치는 습관 =
-- 게임에서 보스전 앞 세이브 포인트 만드는 습관이에요.`;

const CODE_JOIN_PROBLEM = `-- submission만 조회하면 이렇게 보여요:
SELECT id, user_id, status FROM submission;

 id | user_id | status
----+---------+--------
  1 |       3 | DONE
  2 |       5 | PENDING

-- user_id = 3... 이게 누구지? 숫자만 있으니 알 수가 없죠.
-- 이름은 users 테이블에 있으니까, 두 테이블을 '이어야' 합니다.`;

const CODE_JOIN_TWO = `-- JOIN: "submission의 user_id와 users의 id가 같은 행끼리 붙여라"

SELECT s.id, u.name, s.status
FROM submission s
JOIN users u ON s.user_id = u.id;

 id | name   | status
----+--------+--------
  1 | 김미림 | DONE
  2 | 이마이 | PENDING

-- s, u 는 테이블의 별명(alias) — 매번 긴 이름 쓰기 귀찮으니까.
-- ON 뒤가 '잇는 조건': 명찰(user_id)과 학생부(users.id)를 맞춰보는 것.`;

const CODE_JOIN_THREE = `-- 3테이블 JOIN: 제출물 + 학생 이름 + 과제 제목까지 한 번에!

SELECT u.name       AS 학생,
       a.title      AS 과제,
       s.status     AS 상태,
       s.submitted_at
FROM submission s
JOIN users u      ON s.user_id = u.id
JOIN assignment a ON s.assignment_id = a.id
ORDER BY s.submitted_at DESC;

 학생   | 과제              | 상태    | submitted_at
--------+-------------------+---------+---------------
 김미림 | Git 브랜치 실습    | DONE    | 2026-07-15 ...
 이마이 | 리눅스 명령어 정리 | PENDING | 2026-07-14 ...

-- 이게 관리자 화면 '제출물 목록'의 정체입니다.
-- 화면에 이름과 제목이 나란히 보이는 건, 뒤에서 이런 JOIN이 돌기 때문!`;

const CODE_JPA = `// 우리 백엔드(SubmissionRepository.java)엔 SQL이 한 줄도 없어요:
List<Submission> findByUserIdOrderBySubmittedAtDesc(Long userId);

// 그런데 Hibernate(JPA)가 메서드 이름을 읽고 SQL을 '지어서' 실행합니다:
//   findBy UserId        →  WHERE user_id = ?
//   OrderBy SubmittedAt  →  ORDER BY submitted_at
//   Desc                 →  DESC

SELECT * FROM submission
WHERE user_id = ?
ORDER BY submitted_at DESC;

-- 메서드 이름이 곧 SQL의 설계도인 셈이에요.
-- 번역기를 쓰더라도 원문(SQL)을 읽을 줄 알아야
-- "번역이 이상한데?"를 알아챌 수 있습니다.`;

const CODE_PSQL = `# 1) 로컬 개발 DB 컨테이너 안의 psql 실행 (Git Bash나 PowerShell에서)
docker exec -it mirim-postgres psql -U mirim

# 2) 접속되면 프롬프트가 이렇게 바뀌어요:
mirim=#

# 3) 테이블 목록 보기 (백슬래시 명령은 psql 전용!)
mirim=# \\dt

            List of relations
 Schema |      Name       | Type  | Owner
--------+-----------------+-------+-------
 public | assignment      | table | mirim
 public | checklist_item  | table | mirim
 public | progress        | table | mirim
 public | submission      | table | mirim
 public | users           | table | mirim

# 4) 오늘 배운 SELECT 실행 — SQL 끝엔 세미콜론(;) 필수!
mirim=# SELECT name, track FROM users;

# 5) 내 계정 행 찾아보기 (username을 본인 것으로)
mirim=# SELECT * FROM users WHERE username = '내아이디';

# 6) 내가 누른 체크가 진짜 저장됐나? (앱에서 체크 하나 누르고 실행!)
mirim=# SELECT * FROM progress ORDER BY checked_at DESC LIMIT 5;

# 나가기: \\q`;

// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: 'DB가 뭐길래' },
  { n: 2, label: 'SELECT' },
  { n: 3, label: 'WHERE' },
  { n: 4, label: 'ORDER BY / LIMIT' },
  { n: 5, label: '쓰기 3형제' },
  { n: 6, label: 'JOIN' },
  { n: 7, label: 'JPA와 SQL' },
  { n: 8, label: 'psql 실습' },
];

export default function SqlPage() {
  return (
    <div>
      {/* 히어로: 이 코스에서 다룰 데이터가 '남의 예제'가 아니라 '내 데이터'임을 강조 */}
      <div className="hero">
        <div className="eyebrow">Course · 서버와 데이터</div>
        <h1>SQL 입문<br />— 데이터베이스에게 말 거는 법</h1>
        <p>
          여러분이 이 플랫폼에서 체크리스트를 하나 누를 때마다, 어딘가의 테이블에
          행이 하나 생깁니다. 이 코스에선 그 <strong>"어딘가"</strong>를 직접 열어 봐요 —
          예제가 전부 <strong>지금 쓰고 있는 이 앱의 진짜 테이블</strong>이라서,
          오늘 배우는 SQL이 곧 내 데이터를 읽는 열쇠가 됩니다.
          마지막 섹션에선 내 PC의 개발 DB에 접속해 <strong>내 계정 행</strong>을 직접 찾아봅니다.
        </p>
        <div className="chip-row">
          <span className="chip">예상 소요 60분</span>
          <span className="chip">선수 지식: 리눅스 기초 권장</span>
          <span className="chip">준비물: 로컬 개발 환경(docker)</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>
          데이터베이스(DB)는 <strong>표 모양으로 데이터를 쌓아 두는 창고</strong>예요.
          엑셀에 비유하면 딱 맞아떨어집니다 — <strong>시트 = 테이블</strong>,
          <strong> 행(row) = 데이터 한 건</strong>, <strong>열(column) = 속성</strong>.
          "학생 명단" 시트에 한 줄이 학생 한 명이고, 열이 이름·트랙·가입일인 것처럼요.
        </p>
        <p>그런데 왜 엑셀 대신 DB를 쓸까요? 엑셀이 못 하는 세 가지 때문이에요.</p>
        <ol className="olist">
          <li>
            <strong>동시 접속</strong> — 엑셀 파일은 한 명이 열면 다른 사람은 읽기 전용.
            DB는 수습생 전원이 동시에 체크리스트를 눌러도 한 건씩 안전하게 처리해요.
          </li>
          <li>
            <strong>무결성</strong> — 엑셀은 나이 칸에 "스무살"이라고 써도 말리지 않죠.
            DB는 "이 열은 숫자만", "이 값은 반드시 users에 있는 id여야 함" 같은
            규칙을 걸어서 이상한 데이터가 아예 못 들어오게 막아요.
          </li>
          <li>
            <strong>속도</strong> — 행이 100만 개여도 조건에 맞는 한 줄을 순식간에 찾아요.
            엑셀이라면 스크롤하다 하루가 갑니다.
          </li>
        </ol>
        <p>
          우리 앱의 DB(PostgreSQL) 안에는 테이블이 5개 있어요. 이번 코스 내내
          이 다섯 개만 갖고 놉니다.
        </p>
        <Code>{CODE_TABLES}</Code>
        <div className="tip">
          <b>직접 확인해 보기</b> 이 표를 눈에 익혀 두세요. 특히
          <span className="icode">submission.user_id</span>와 <span className="icode">progress.item_id</span> 처럼
          <strong> 다른 테이블을 가리키는 숫자 열</strong>에 동그라미를 쳐 보세요 —
          섹션 6(JOIN)에서 이 동그라미들이 주인공이 됩니다.
        </div>
      </Section>

      <Section n={2} title="SELECT — 조회의 기본" sub="DB에게 건네는 첫 마디: '이것 좀 보여줘'">
        <p>
          SQL은 DB에게 말을 거는 언어예요. 그중 가장 많이 쓰는 문장이
          <strong> SELECT</strong> — "이 테이블에서 이 열들 좀 보여줘"입니다.
          문장 구조가 거의 영어 그대로라 읽기 쉬워요.
        </p>
        <Code>{CODE_SELECT_BASIC}</Code>
        <p>
          <span className="icode">*</span>는 "열 전부"라는 와일드카드예요. 구경할 땐 편하지만,
          실무 코드에선 <strong>필요한 열만 콕 집어</strong> 가져오는 게 예의입니다 —
          열이 수십 개인 테이블에서 <span className="icode">*</span>를 남발하면
          쓸데없는 데이터가 네트워크를 타고 우르르 넘어오거든요.
        </p>
        <p>
          users 테이블을 조회하다 보면 <span className="icode">password_hash</span> 열에서
          이상한 문자열을 만나게 돼요.
        </p>
        <Code>{CODE_SELECT_HASH}</Code>
        <div className="tip">
          <b>직접 확인해 보기</b> 아직 DB 접속 전이니, 종이에 SELECT 문장을 지어 보세요.
          "assignment 테이블에서 title과 summary만 보고 싶다"를 SQL로 쓰면?
          (답: <span className="icode">SELECT title, summary FROM assignment;</span> —
          세미콜론까지가 한 문장이에요.) 섹션 8에서 진짜로 실행해 봅니다.
        </div>
      </Section>

      <Section n={3} title="WHERE — 조건 걸기" sub="전체가 아니라 '조건에 맞는 행만'">
        <p>
          SELECT만 쓰면 테이블 전체가 쏟아져요. 실제로 궁금한 건 보통
          "<strong>1주차</strong> 과제만", "<strong>내</strong> 제출물만"처럼 일부죠.
          그 필터가 <strong>WHERE</strong>입니다. 엑셀에서 열 머리의 필터 버튼을 눌러
          조건을 거는 것과 똑같은 일이에요.
        </p>
        <Code>{CODE_WHERE}</Code>
        <p>연산자는 이 정도만 알면 당분간 충분해요.</p>
        <Code>{CODE_WHERE_OPS}</Code>
        <div className="warn">
          <b>따옴표 주의</b> 문자열은 <strong>작은따옴표</strong>(<span className="icode">'MAIN'</span>)로
          감싸요. 큰따옴표(<span className="icode">"..."</span>)는 SQL에서
          열·테이블 이름용이라 뜻이 달라집니다. 숫자는 따옴표 없이 그냥
          <span className="icode">week = 1</span>.
        </div>
        <div className="tip">
          <b>직접 확인해 보기</b> 말로 된 조건을 SQL로 바꾸는 연습 세 개 —
          ① "2주차 이상의 과제 제목" ② "status가 PENDING이 아닌 제출물"
          ③ "label에 'Git'이 들어간 체크리스트 항목". 각각
          <span className="icode">&gt;=</span>, <span className="icode">&lt;&gt;</span>,
          <span className="icode">LIKE</span>를 쓰면 됩니다. 답을 메모해 뒀다가 섹션 8에서 실행!
        </div>
      </Section>

      <Section n={4} title="ORDER BY / LIMIT — 정렬과 자르기" sub="최신순으로 5개만 주세요">
        <p>
          DB는 기본적으로 행을 <strong>순서 보장 없이</strong> 돌려줘요. "최근 것부터"를 원하면
          <strong> ORDER BY</strong>로 정렬하고, "다 말고 위에서 몇 개만"은
          <strong> LIMIT</strong>로 자릅니다. 유튜브 검색 결과를 '최신순 정렬'하고
          첫 페이지만 보는 것과 같은 조합이죠.
        </p>
        <Code>{CODE_ORDER_LIMIT}</Code>
        <p>
          우리 앱 관리자 화면의 "최근 제출물" 목록이 정확히 이 쿼리예요 —
          <span className="icode">ORDER BY submitted_at DESC LIMIT 5</span>.
          화면에 보이는 모든 목록 뒤에는 이런 정렬·자르기가 숨어 있습니다.
        </p>
        <svg className="diagram" viewBox="0 0 640 190" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="SQL 실행 순서 도해: FROM으로 테이블에서 가져와 WHERE로 골라내고 ORDER BY로 정렬하고 LIMIT로 자른다">
          <rect x="15" y="65" width="130" height="70" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="80" y="95" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--primary)">FROM</text>
          <text x="80" y="117" textAnchor="middle" fontSize="12" fill="var(--muted)">테이블에서</text>
          <text x="80" y="158" textAnchor="middle" fontSize="11" fill="var(--muted)">예: 전체 100행</text>
          <line x1="148" y1="100" x2="166" y2="100" stroke="var(--line)" strokeWidth="2" />
          <polyline points="160,94 169,100 160,106" fill="none" stroke="var(--line)" strokeWidth="2" strokeLinecap="round" />
          <rect x="175" y="65" width="130" height="70" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="240" y="95" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--teal)">WHERE</text>
          <text x="240" y="117" textAnchor="middle" fontSize="12" fill="var(--muted)">골라내고</text>
          <text x="240" y="158" textAnchor="middle" fontSize="11" fill="var(--muted)">조건 맞는 7행</text>
          <line x1="308" y1="100" x2="326" y2="100" stroke="var(--line)" strokeWidth="2" />
          <polyline points="320,94 329,100 320,106" fill="none" stroke="var(--line)" strokeWidth="2" strokeLinecap="round" />
          <rect x="335" y="65" width="130" height="70" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="400" y="95" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--amber)">ORDER BY</text>
          <text x="400" y="117" textAnchor="middle" fontSize="12" fill="var(--muted)">정렬하고</text>
          <text x="400" y="158" textAnchor="middle" fontSize="11" fill="var(--muted)">7행 · 최신순</text>
          <line x1="468" y1="100" x2="486" y2="100" stroke="var(--line)" strokeWidth="2" />
          <polyline points="480,94 489,100 480,106" fill="none" stroke="var(--line)" strokeWidth="2" strokeLinecap="round" />
          <rect x="495" y="65" width="130" height="70" rx="8" fill="var(--card)" stroke="var(--line)" />
          <text x="560" y="95" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--rose)">LIMIT</text>
          <text x="560" y="117" textAnchor="middle" fontSize="12" fill="var(--muted)">자른다</text>
          <text x="560" y="158" textAnchor="middle" fontSize="11" fill="var(--muted)">위에서 5행만</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: 조합 순서 — FROM → WHERE → ORDER BY → LIMIT (골라내고 → 정렬하고 → 자른다)</p>
        <div className="tip">
          <b>직접 확인해 보기</b> "체크리스트 항목을 week, day, sort_order 순으로 정렬"은
          어떻게 쓸까요? ORDER BY엔 열을 쉼표로 여러 개 나열할 수 있어요:
          <span className="icode">ORDER BY week, day, sort_order</span> —
          앞 열이 같을 때 다음 열로 순서를 가리는, '가나다순 → 같으면 나이순' 방식입니다.
        </div>
      </Section>

      <Section n={5} title="INSERT / UPDATE / DELETE — 쓰기 3형제" sub="읽기만 하던 손님에서, 쓰는 사람으로">
        <p>
          지금까지는 구경(SELECT)만 했다면, 이제 데이터를 <strong>넣고(INSERT)·고치고(UPDATE)·
          지우는(DELETE)</strong> 차례예요. 문장은 짧지만 파괴력은 SELECT와 비교가 안 됩니다.
        </p>
        <Code>{CODE_WRITE}</Code>
        <div className="warn">
          <b>실무 사고 원인 1위: WHERE 없는 UPDATE/DELETE</b>{' '}
          <span className="icode">UPDATE submission SET status = 'DONE';</span> —
          WHERE를 빼먹은 이 한 줄은 <strong>테이블의 모든 행</strong>을 DONE으로 바꿔 버립니다.
          DELETE도 마찬가지로 전멸이에요. "3번만 바꿔야지" 하고 친 명령이 전교생 성적을
          갈아치우는 셈이죠. 습관 하나로 예방합니다: <strong>UPDATE/DELETE를 치기 전에,
          같은 WHERE로 SELECT를 먼저 돌려서</strong> 바뀔 행이 몇 개인지 눈으로 확인하세요.
        </div>
        <p>
          그리고 안전벨트가 하나 더 있어요 — <strong>트랜잭션</strong>.
          <span className="icode">BEGIN</span>으로 시작하면 그 뒤의 변경은 '가안' 상태라서,
          <span className="icode">ROLLBACK</span> 한 방에 전부 되돌릴 수 있습니다.
        </p>
        <Code>{CODE_TX}</Code>
        <div className="tip">
          <b>직접 확인해 보기</b> 섹션 8에서 로컬 DB에 접속하면
          <span className="icode">BEGIN;</span> → WHERE 없는 UPDATE → <span className="icode">SELECT</span>로
          참사 확인 → <span className="icode">ROLLBACK;</span> → 다시 SELECT로 원상복구 확인 —
          이 '사고와 복구' 시나리오를 일부러 한 번 겪어 보세요. 로컬 DB니까 안전합니다.
        </div>
      </Section>

      <Section n={6} title="JOIN — 테이블 두 개 잇기" sub="user_id 3번... 그래서 그게 누군데?">
        <p>
          submission 테이블엔 학생 <strong>이름이 없어요</strong>.
          <span className="icode">user_id</span>라는 숫자만 있죠.
        </p>
        <Code>{CODE_JOIN_PROBLEM}</Code>
        <p>
          왜 이름을 안 넣었을까요? 이름을 제출물마다 복사해 두면, 개명이라도 하는 순간
          수십 곳을 고쳐야 하고 하나라도 빼먹으면 데이터가 어긋나요. 그래서 DB는
          <strong> 이름은 users에 한 번만 두고, 다른 테이블은 번호표(id)로 가리키게</strong> 합니다.
          이 번호표를 <strong>외래키</strong>(foreign key)라고 불러요 — "다른(외) 테이블의
          열쇠(키)"라는 뜻이에요.
        </p>
        <p>
          그리고 번호표를 실제 이름으로 바꿔 보는 순간이 <strong>JOIN</strong>입니다.
          명찰의 학번을 학생부와 맞춰 보며 "아, 3번은 김미림이구나" 하는 거죠.
        </p>
        <Code>{CODE_JOIN_TWO}</Code>
        <svg className="diagram" viewBox="0 0 640 368" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="JOIN 개념 도해: submission 테이블의 user_id 외래키가 users 테이블의 id를 가리키고, 두 행을 이어 붙인 JOIN 결과에 이름이 나타난다">
          <text x="155" y="40" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">users</text>
          <text x="155" y="58" textAnchor="middle" fontSize="12" fill="var(--muted)">이름은 여기 한 번만</text>
          <rect x="50" y="70" width="210" height="98" rx="8" fill="var(--card)" stroke="var(--line)" />
          <line x1="50" y1="100" x2="260" y2="100" stroke="var(--line)" />
          <line x1="50" y1="134" x2="260" y2="134" stroke="var(--line)" />
          <line x1="110" y1="70" x2="110" y2="168" stroke="var(--line)" />
          <text x="80" y="90" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">id</text>
          <text x="185" y="90" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">name</text>
          <text x="80" y="122" textAnchor="middle" fontSize="13" fill="var(--ink)">3</text>
          <text x="185" y="122" textAnchor="middle" fontSize="13" fill="var(--ink)">김미림</text>
          <text x="80" y="156" textAnchor="middle" fontSize="13" fill="var(--ink)">5</text>
          <text x="185" y="156" textAnchor="middle" fontSize="13" fill="var(--ink)">이마이</text>
          <rect x="54" y="104" width="52" height="26" rx="4" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <rect x="54" y="138" width="52" height="26" rx="4" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <text x="495" y="40" textAnchor="middle" fontSize="14" fontWeight="600" fill="var(--ink)">submission</text>
          <text x="495" y="58" textAnchor="middle" fontSize="12" fill="var(--muted)">이름 대신 번호표(user_id)</text>
          <rect x="380" y="70" width="230" height="98" rx="8" fill="var(--card)" stroke="var(--line)" />
          <line x1="380" y1="100" x2="610" y2="100" stroke="var(--line)" />
          <line x1="380" y1="134" x2="610" y2="134" stroke="var(--line)" />
          <line x1="435" y1="70" x2="435" y2="168" stroke="var(--line)" />
          <line x1="510" y1="70" x2="510" y2="168" stroke="var(--line)" />
          <text x="407" y="90" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">id</text>
          <text x="472" y="90" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">user_id</text>
          <text x="560" y="90" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">status</text>
          <text x="407" y="122" textAnchor="middle" fontSize="13" fill="var(--ink)">1</text>
          <text x="472" y="122" textAnchor="middle" fontSize="13" fill="var(--ink)">3</text>
          <text x="560" y="122" textAnchor="middle" fontSize="13" fill="var(--ink)">DONE</text>
          <text x="407" y="156" textAnchor="middle" fontSize="13" fill="var(--ink)">2</text>
          <text x="472" y="156" textAnchor="middle" fontSize="13" fill="var(--ink)">5</text>
          <text x="560" y="156" textAnchor="middle" fontSize="13" fill="var(--ink)">PENDING</text>
          <rect x="439" y="104" width="67" height="26" rx="4" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <rect x="439" y="138" width="67" height="26" rx="4" fill="none" stroke="var(--primary)" strokeWidth="1.5" />
          <line x1="260" y1="117" x2="380" y2="117" stroke="var(--primary)" strokeWidth="2" />
          <circle cx="260" cy="117" r="3.5" fill="var(--primary)" />
          <circle cx="380" cy="117" r="3.5" fill="var(--primary)" />
          <line x1="260" y1="151" x2="380" y2="151" stroke="var(--primary)" strokeWidth="2" />
          <circle cx="260" cy="151" r="3.5" fill="var(--primary)" />
          <circle cx="380" cy="151" r="3.5" fill="var(--primary)" />
          <text x="320" y="140" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--primary)">u.id = s.user_id</text>
          <line x1="155" y1="172" x2="268" y2="236" stroke="var(--muted)" strokeWidth="1.5" strokeDasharray="4 4" />
          <line x1="495" y1="172" x2="372" y2="236" stroke="var(--muted)" strokeWidth="1.5" strokeDasharray="4 4" />
          <text x="320" y="208" textAnchor="middle" fontSize="13" fontWeight="700" fill="var(--teal)">JOIN</text>
          <rect x="200" y="240" width="240" height="98" rx="8" fill="var(--card)" stroke="var(--line)" />
          <line x1="200" y1="270" x2="440" y2="270" stroke="var(--line)" />
          <line x1="200" y1="304" x2="440" y2="304" stroke="var(--line)" />
          <line x1="260" y1="240" x2="260" y2="338" stroke="var(--line)" />
          <line x1="350" y1="240" x2="350" y2="338" stroke="var(--line)" />
          <text x="230" y="260" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">id</text>
          <text x="305" y="260" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">name</text>
          <text x="395" y="260" textAnchor="middle" fontSize="12" fontWeight="600" fill="var(--muted)">status</text>
          <text x="230" y="292" textAnchor="middle" fontSize="13" fill="var(--ink)">1</text>
          <text x="305" y="292" textAnchor="middle" fontSize="13" fill="var(--ink)">김미림</text>
          <text x="395" y="292" textAnchor="middle" fontSize="13" fill="var(--ink)">DONE</text>
          <text x="230" y="326" textAnchor="middle" fontSize="13" fill="var(--ink)">2</text>
          <text x="305" y="326" textAnchor="middle" fontSize="13" fill="var(--ink)">이마이</text>
          <text x="395" y="326" textAnchor="middle" fontSize="13" fill="var(--ink)">PENDING</text>
          <text x="320" y="360" textAnchor="middle" fontSize="12" fill="var(--muted)">번호표가 이름으로 바뀌었다</text>
        </svg>
        <p className="muted" style={{ fontSize: 12.5, textAlign: 'center', marginTop: 2 }}>그림: submission의 user_id(외래키)가 users의 id를 가리키고, JOIN이 같은 번호끼리 이어 붙인다</p>
        <p>
          한 번 이었으면 두 번도 이을 수 있어요. 제출물엔
          <span className="icode">assignment_id</span>도 있으니, 과제 제목까지 붙여 봅시다 —
          테이블 3개를 한 문장으로.
        </p>
        <Code>{CODE_JOIN_THREE}</Code>
        <div className="tip">
          <b>직접 확인해 보기</b> progress 테이블로 같은 걸 스스로 지어 보세요 —
          "누가(users.name) 어떤 항목(checklist_item.label)을 언제(checked_at) 체크했나".
          잇는 조건은 <span className="icode">progress.user_id = users.id</span>와{' '}
          <span className="icode">progress.item_id = checklist_item.id</span>.
          섹션 8에서 실행해 보면, 여러분이 누른 체크가 이름과 함께 나옵니다.
        </div>
      </Section>

      <Section n={7} title="JPA와 SQL의 관계" sub="백엔드 코드엔 SQL이 없는데, 누가 쿼리를 날리지?">
        <p>
          여기서 이상한 점 하나 — 우리 백엔드(Spring Boot) 코드를 열어 보면
          <strong> SQL이 거의 안 보입니다</strong>. 대신 <strong>JPA(Hibernate)</strong>라는
          번역기가 있어요. 자바 메서드 이름을 읽고, 알맞은 SQL을 자동으로 지어서
          DB에 대신 말을 걸어 줍니다.
        </p>
        <Code>{CODE_JPA}</Code>
        <p>
          "그럼 SQL 몰라도 되는 거 아냐?" — 반대예요. 번역기를 쓸수록 원문을 알아야 합니다.
          이유는 두 가지예요.
        </p>
        <ol className="olist">
          <li>
            <strong>느린 쿼리는 SQL로만 보인다</strong> — 화면이 느릴 때 범인은 대부분
            JPA가 지어낸 비효율적인 쿼리예요. 로그에 찍힌 SQL을 읽을 줄 알아야
            "아, 여기서 JOIN이 100번 도는구나"를 잡아낼 수 있습니다.
            (티켓 풀의 <strong>BE-06</strong>이 바로 이런 '느린 쿼리 잡기' 과제예요.)
          </li>
          <li>
            <strong>번역이 항상 완벽하진 않다</strong> — 복잡한 조회는 JPA에게 맡기기보다
            SQL을 직접 쓰는 게 나을 때가 있어요. 그 판단도 SQL을 알아야 할 수 있죠.
          </li>
        </ol>
        <div className="tip">
          <b>직접 확인해 보기</b> 백엔드 저장소에서{' '}
          <span className="icode">SubmissionRepository</span> 파일을 열어 메서드 이름들을 읽고,
          각각 어떤 SQL로 번역될지 오늘 배운 문법으로 추리해 보세요.
          <span className="icode">findByStatus</span>는? <span className="icode">findByUserIdAndStatus</span>는?
          메서드 이름이 문장처럼 읽히기 시작하면 성공입니다.
        </div>
      </Section>

      <Section n={8} title="직접 해보기 — 내 PC에서 psql 접속" sub="오늘 배운 전부를 진짜 DB에 쏘기">
        <p>
          이제 진짜 DB에 접속할 시간이에요. 로컬 개발 환경에서 docker로 띄운
          PostgreSQL 컨테이너 안에는 <strong>psql</strong>이라는 SQL 대화창이 들어 있습니다.
          Git Bash(또는 PowerShell)를 열고 따라 해 보세요.
        </p>
        <Code>{CODE_PSQL}</Code>
        <p>
          6번이 이 코스의 하이라이트예요 — 브라우저에서 체크리스트 하나를 체크하고,
          곧바로 psql에서 progress를 조회하면 <strong>방금 그 클릭이 행 하나로</strong> 나타납니다.
          화면의 체크박스와 DB의 행이 같은 것이라는 감각, 그게 오늘의 목표였어요.
        </p>
        <div className="tip">
          <b>여긴 놀이터예요</b> 지금 접속한 건 <strong>내 PC의 로컬 개발 DB</strong> —
          망가뜨려도 아무 일도 안 일어나고, 컨테이너를 지우고 다시 만들면 그만이에요.
          WHERE 없는 UPDATE도, DELETE도, 트랜잭션 롤백도 마음껏 실험해 보세요.
          많이 부숴 본 사람이 안 부수는 법을 압니다.
        </div>
        <div className="warn">
          <b>단, 운영 DB는 절대 아닙니다</b> 실제 서비스가 쓰는 운영 DB에서는
          실험 쿼리를 날리지 않아요 — 거기 있는 행 하나하나가 진짜 사용자의 데이터입니다.
          나중에 운영 DB에 접근할 일이 생기면 반드시 멘토와 함께, 읽기(SELECT)부터,
          그리고 쓰기 전엔 항상 <span className="icode">BEGIN</span>부터. 이 감각의 차이가
          수습과 실무자를 가릅니다.
        </div>
      </Section>

      <div className="step-card">
        <div className="step-body">
          <h3 style={{ marginBottom: 6 }}>🗄️ 여기까지 왔다면</h3>
          <p className="muted">
            이제 화면 뒤의 데이터가 어디에 어떤 모양으로 사는지, 그리고 그 데이터에게
            직접 말을 거는 법(SELECT부터 JOIN까지)을 알게 됐어요. 다음은 그 데이터를
            화면으로 바꾸는 <strong>코드</strong> 차례 —{' '}
            <Link to="/learn/coding"><strong>코딩 기초</strong></Link> 코스로 이어 가세요.
            그리고 <span className="icode">STUDY.md</span>의 미니 과제
            (오늘 배운 3테이블 JOIN을 progress 버전으로 직접 작성해 제출)로
            배운 것을 굳혀 보세요.
          </p>
        </div>
      </div>
    </div>
  );
}
