mirim-app/frontend/src/pages/levels/Lv3CodeReviewRefactoringPage.jsx
AWESOMEDEV c5f875d990
All checks were successful
CI / backend-test (push) Successful in 1m1s
CI / frontend-build (push) Successful in 42s
CI / backend-dep-scan (push) Successful in 30s
feat(learn): 수준별 학습 과정 4종 — 초급·중급·고급·특급 48강좌 + 퀴즈 240문항
기존 84강좌는 수습용으로 유지하고, 헤더 셀렉트 박스로 과정을 전환한다.

과정 구성 (각 12강좌, 배우는 순서대로):
- 초급 — 코딩 첫걸음: 프로그램의 개념→HTML→JS→변수·조건·반복·함수→에러읽기→첫 게임
- 중급 — 실전 웹 개발: 비동기·배열→API설계→Express CRUD→DB·트랜잭션→인증→React 실전→테스트→배포
- 고급 — 아키텍처·성능·보안: 레이어드·패턴→인덱스·격리→캐싱·최적화→OWASP·인증설계→관측·무중단배포
- 특급 — 대규모 시스템: 분산기초·CAP→메시지큐→MSA→샤딩→대용량 설계→k8s→SRE→시스템설계 3연작→ADR

구현:
- trackContext(localStorage 유지) + 헤더 TrackSelect + 테스트 5개
- levelCatalog(lv1-~lv4- 슬러그, 기존과 충돌 없음) + 라우트·퀴즈 자동 연결
- LearnHubPage 과정 인식(과정별 인사말·진도), CertificatePage 분모 버그 선제 수정
- 퀴즈 240문항: quizzes.json 415→655 + 운영 DB 삽입(655/131 확인)

생성 파이프라인: 설계 4 → 작성 48(병렬) → 검수 4 에이전트.
검수 BLOCKER 2건(백틱 충돌·OpenAPI 표기) 수정 후 빌드 통과.
운영 검증: 4개 과정 전환·강좌 렌더·퀴즈 로드·선택 유지 브라우저 확인, 스모크 11/11.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:13:25 +09:00

461 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 이 파일이 하는 일: "코드 리뷰와 리팩터링 전략" 코스 — 고급 과정의 종착점.
// 동작을 바꾸지 않고 구조를 개선하는 규율(리팩터링)에서 출발해, 코드 냄새 목록,
// 작은 단계 기법, 우선순위 정하기, 리뷰의 진짜 목적, 좋은 리뷰 실천을 거쳐,
// 앞 11강에서 배운 설계·성능·보안·운영 관점을 하나의 리뷰 체크리스트로 통합한다.
// 프레이밍은 "혼자 잘 짜는 사람"에서 "팀이 잘 만들게 하는 사람"으로의 전환.
// 학습 포인트: API 호출이 없는 '내용 고정' 페이지다. 스타일은 styles/global.css의
// 가이드 전용 클래스(step-card, code-block, tip 등)를 재사용하고 색은 var(--*)만 쓴다(하드코딩 금지).
// 예제 코드는 JSX 중괄호/백틱 충돌을 피하려고 파일 상단의 백틱 문자열 상수로 정의한다.
import { Link } from 'react-router-dom';
// ── 예제 코드 상수들 ──
// 학습 포인트: 리뷰·리팩터링은 언어보다 '판단'이 핵심이라, 예제도 표·디프·체크리스트
// 같은 텍스트 형태를 주로 쓴다. 코드가 필요한 곳은 파이썬풍 의사코드로 적었다.
const CODE_TWO_HATS = `기능 개발 모자 🎩 리팩터링 모자 🧢
─────────────────────────────────────────────────────
새 동작을 추가한다 동작은 그대로, 구조만 바꾼다
테스트를 '새로' 쓴다 기존 테스트가 '계속' 통과해야 한다
결과: 사용자가 차이를 느낀다 결과: 사용자는 차이를 못 느낀다
규율: 한 커밋 안에서 두 모자를 동시에 쓰지 않는다.
"기능도 넣고 김에 정리도 했어요" 커밋은 —
버그가 나면 어느 쪽 때문인지 아무도 모르게 됩니다.`;
const CODE_SAFETY_NET = `리팩터링의 순서 — 안전망이 먼저다
① 바꾸려는 코드에 테스트가 있는가?
없다 → 먼저 '현재 동작'을 고정하는 테스트를 쓴다
있다 → ②로
② 테스트를 돌려 전부 초록불인지 확인한다
③ 구조를 '조금' 바꾼다 (한 번에 한 기법)
④ 테스트를 다시 돌린다 — 초록불이면 커밋, 빨간불이면 즉시 되돌린다
⑤ ③~④를 반복한다
포인트: 테스트 없는 리팩터링은 리팩터링이 아니라 '수정 도박'입니다.
초록불 → 초록불 사이를 짧은 보폭으로 건너는 것이 전부예요.`;
const CODE_SMELL_MAP = `코드 냄새 → 리팩터링 기법 매핑 (자주 만나는 것부터)
냄새 증상 처방
──────────────────────────────────────────────────────────────
긴 함수 스크롤해야 끝이 보인다 함수 추출(Extract Function)
큰 클래스 이름에 Manager·Util·Service만 클래스 분리, 책임별로 쪼개기
중복 코드 복붙 흔적, 세 번째 복사 중 공통 함수로 추출 후 호출
기능 편애 남의 데이터만 주무르는 함수 그 데이터가 사는 곳으로 이동
매직 넘버 코드에 3, 86400, 0.1 같은 숫자 이름 있는 상수로 추출
긴 매개변수 목록 인자가 5개 이상 객체 하나로 묶기
주석으로 때우기 "여기서 X를 함" 주석 필수 이름을 바꿔 주석을 지운다
앞 강의와의 연결:
· N+1 쿼리(성능 강의) → '반복문 속 쿼리'라는 냄새의 DB 버전
· God Service(설계 강의) → '큰 클래스' 냄새가 서비스 계층에서 자란 것`;
const CODE_EXTRACT = `# 함수 추출 — 가장 자주 쓰는 기본기 (의사코드)
# before: 주문 처리 함수 하나가 검증·계산·알림을 다 한다 (긴 함수 냄새)
def 주문_처리(주문):
if 주문.수량 <= 0: raise 검증오류("수량은 1 이상")
if not 주문.회원: raise 검증오류("로그인이 필요합니다")
금액 = 주문.단가 * 주문.수량
if 주문.회원.등급 == "VIP": 금액 = 금액 * 0.9
알림서비스.보내기(주문.회원, "주문 완료: " + str(금액))
# after: 세 덩어리에 '이름'을 붙여 꺼냈다 — 동작은 완전히 동일
def 주문_처리(주문):
주문_검증(주문)
금액 = 결제금액_계산(주문)
주문완료_알림(주문.회원, 금액)
# 무엇이 좋아졌나: 함수 본문이 곧 목차가 됐다.
# 리뷰어는 세 줄만 읽고 흐름을 파악하고, 의심 가는 덩어리만 파고든다.`;
const CODE_SMALL_COMMITS = `커밋을 작게 쪼개기 — 되돌리기 쉬운 역사 만들기
나쁜 예 (커밋 1개):
fix: 정산 버그 수정 및 코드 정리 ← 300줄 변경, 뭐가 뭔지 모름
좋은 예 (커밋 4개, 순서대로):
test: 정산 금액 계산 현재 동작 고정 ← ① 안전망 먼저
refactor: 정산 계산 함수 추출 ← ② 구조만 변경 (동작 동일)
refactor: 매직 넘버 0.033을 수수료율 상수로 ← ③ 역시 구조만
fix: VIP 등급 수수료 누락 버그 수정 ← ④ 이제야 '동작' 변경 1줄
효과: ④에서 문제가 생기면 ④만 되돌리면 된다(revert).
IDE 자동 리팩터링(이름 변경·함수 추출)은 참조를 기계가 전부 따라가므로
손으로 찾아 바꾸기(Ctrl+H)보다 훨씬 안전하다 — 단, 커밋은 사람이 쪼갠다.`;
const CODE_PRIORITY = `어디부터 리팩터링할까 — 변경 빈도 × 코드 상태
│ 코드가 깨끗함 코드가 지저분함
─────────────────────┼──────────────────────────────────────
자주 바뀌는 파일 │ 그대로 두기 ★ 최우선 정리 대상
거의 안 바뀌는 파일 │ 그대로 두기 급하지 않음(놔둬도 됨)
이유: 지저분해도 아무도 안 여는 코드는 이자를 물리지 않는다.
빚(기술 부채)의 이자는 '그 코드를 만질 때마다' 나가기 때문에,
자주 만지는 곳의 빚부터 갚는 게 이득이 가장 크다.
변경 빈도는 감이 아니라 기록으로 확인한다:
git log --since="3 months ago" --name-only 로 파일별 등장 횟수 세기
→ 상위 파일 = 우리 팀의 '뜨거운 길목'`;
const CODE_PR_CONVENTION = `우리 팀 커밋·PR 매너 (한국어 컨벤션)
커밋 메시지 — 접두어 + 한국어 요약:
feat: 관심종목 알림 조건 추가
fix: 정산 반올림 오차 수정
refactor: 주문 검증 로직 함수 추출
docs: 배포 절차에 롤백 단계 보강
PR 설명에 꼭 들어갈 3가지:
· 무엇을: 한 줄 요약 (제목만 읽어도 알게)
· 왜: 배경 — 이슈 번호, 장애 링크, 요구 근거
· 어떻게 확인: 리뷰어가 따라 할 수 있는 재현/검증 절차
리뷰 코멘트 — 사람이 아니라 코드를 향해 쓴다:
✗ "이렇게 짜시면 안 되죠" (사람을 평가)
○ "이 함수가 검증과 저장을 같이 해서, (코드를 서술)
검증만 실패하는 케이스 테스트가 어려워 보여요.
검증부를 추출하면 어떨까요?" (근거 + 제안)`;
const CODE_REVIEW_CHECKLIST = `리뷰어 체크리스트 — 관점별로 한 바퀴 (블로킹/제안 표시)
[설계]
□ 이 변경이 한 가지 일만 하는가? (PR이 크면 분리 요청 — 블로킹)
□ 새 의존성 방향이 기존 계층 규칙을 거스르지 않는가? — 블로킹
□ 이름만 읽고 역할을 알 수 있는가? — 제안
[성능]
□ 반복문 안에 쿼리·외부 호출이 없는가? (N+1) — 블로킹
□ 큰 목록에 페이징·상한이 있는가? — 블로킹
□ 캐시를 얹을 만한 반복 조회인가? — 제안
[보안]
□ 입력 검증이 서버 쪽에 있는가? (프론트 검증만으론 부족) — 블로킹
□ 시크릿·토큰이 코드/로그에 남지 않는가? — 블로킹
□ 권한 확인이 '누락된 경로'는 없는가? — 블로킹
[운영]
□ 실패 시 로그로 원인 추적이 되는가? — 제안
□ 롤백이 가능한 변경인가? (스키마 변경 주의) — 블로킹
블로킹 = 머지 전에 반드시 해결. 제안 = 다음에 개선해도 됨(작성자 재량).
이 구분을 코멘트에 명시하면 "다 고쳐야 하나요?" 소모전이 사라진다.`;
const CODE_FINAL_LADDER = `12강의 사다리 — 전부 리뷰 한 판에서 만난다
설계 강의 → "이 책임, 이 계층에 있는 게 맞나?"
성능 강의 → "이 쿼리, 데이터가 10배 늘면 버티나?"
보안 강의 → "이 입력, 악의적인 사용자가 보내면?"
운영 강의 → "이거 장애 나면 새벽에 로그만 보고 찾을 수 있나?"
테스트 강의 → "이 변경, 안전망이 같이 왔나?"
이번 강의 → 위 질문들을 '체크리스트'로 만들어 매 PR마다 돌린다
좋은 팀의 비밀은 천재가 아니라 '반복되는 좋은 질문'입니다.
체크리스트는 그 질문을 개인의 컨디션에서 떼어내 팀의 습관으로 만들어요.`;
// ── 이 페이지 안에서만 쓰는 작은 부품들 ──
// 학습 포인트: 같은 모양이 반복되면 컴포넌트로 뽑는다. 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: '코드 냄새 목록' },
{ n: 3, label: '작은 단계 리팩터링' },
{ n: 4, label: '우선순위' },
{ n: 5, label: '리뷰의 목적' },
{ n: 6, label: '좋은 리뷰 실천' },
{ n: 7, label: '종합: 잘 만드는 팀' },
];
export default function Lv3CodeReviewRefactoringPage() {
return (
<div>
{/* 히어로: 이 코스가 고급 과정 전체의 종착점임을 선언 */}
<div className="hero">
<div className="eyebrow">Course · 고급 과정</div>
<h1>코드 리뷰와<br />리팩터링 전략</h1>
<p>
여기까지 오면서 설계·성능·보안·운영을 하나씩 배웠어요. 그런데 지식이 실제로
팀의 코드 품질을 지키는 현장은 어디일까요? 바로 <strong>매일 열리는 PR 리뷰</strong>{' '}
<strong>일상의 리팩터링</strong>. 11 {' '}
<strong>리뷰 판에 녹여내는</strong> .
</p>
<div className="chip-row">
<span className="chip">예상 소요 75</span>
<span className="chip">실습: 우리 코드리뷰 체크리스트 만들기</span>
<span className="chip">선수 지식: 고급 과정 강의들</span>
</div>
</div>
{/* 섹션 바로가기 — 학습 포인트: SECTIONS 배열 하나로 목차를 생성(데이터 → 화면) */}
<nav className="chip-row" style={{ marginBottom: 18 }}>
{SECTIONS.map((s) => (
<a key={s.n} className="chip" href={`#sec-${s.n}`}>
<b>{s.n}</b> {s.label}
</a>
))}
</nav>
<Section n={1} title="리팩터링이라는 규율" sub="동작은 그대로, 구조만 좋아진다">
<p>
<strong>리팩터링(Refactoring)</strong> .{' '}
<strong>"겉으로 보이는 동작을 바꾸지 않으면서, 코드의 내부 구조를 개선하는 것."</strong>{' '}
입력이 같으면 출력도 같아야 하고, 사용자는 아무 변화도 느끼지 못해야 합니다.
"정리하는 김에 버그도 고쳤어요" 리팩터링이 아니에요 순간 동작이 바뀌었으니까요.
</p>
<p>
그래서 번째 규율은 <strong>모자를 하나만 쓰는 </strong>.
기능 개발 모자를, 구조를 고칠 리팩터링 모자를 <strong> 커밋 안에서 둘을 섞지
않아요.</strong> , " ,
건지" 아무도 구분할 수 없게 됩니다.
</p>
<Code>{CODE_TWO_HATS}</Code>
<p>
두 번째 규율은 <strong>안전망이 먼저</strong>라는 거예요. "동작이 바뀌었다"를
사람 눈으로 보장할 수는 없습니다. 그걸 보장하는 건 <strong>테스트</strong>예요.
바꾸려는 코드에 테스트가 없다면, 리팩터링을 시작하기 전에 <strong>현재 동작을
고정하는 테스트부터</strong> 씁니다. 그다음 초록불 → 작은 변경 → 초록불 확인, 이
짧은 보폭을 반복하는 게 리팩터링의 전부입니다.
</p>
<Code>{CODE_SAFETY_NET}</Code>
<div className="tip">
<b>비유</b> 리팩터링은 <strong>영업 중인 가게의 진열대 정리</strong>예요. 손님(사용자)이
사는 물건(동작)은 그대로인데, 점원(개발자)이 물건을 찾는 속도가 빨라지죠. 진열대를
엎고 가게를 새로 차리는 것(재작성)과는 완전히 다른 행위입니다 — 그 차이는 섹션 4에서
다시 봐요.
</div>
</Section>
<Section n={2} title="코드 냄새 목록" sub="고치기 전에 먼저 '맡을' 알아야 한다">
<p>
<strong>코드 냄새(Code Smell)</strong>는 "당장 버그는 아니지만, 깊은 문제가 있을
가능성을 풍기는 코드의 특징"이에요. 냄새라는 표현이 절묘한 게, 그 자체로 유죄는
아니지만 <strong>코를 킁킁거리며 원인을 찾아보라는 신호</strong>거든요. 대표 냄새
네 가지만 기억해도 리뷰의 눈이 달라집니다.
</p>
<p>
<strong>① 긴 함수</strong> — 한 함수가 검증·계산·저장·알림을 다 해요. 읽는 사람이
중간에 길을 잃죠. <strong>② 큰 클래스</strong> — 이름이{' '}
<span className="icode">OrderManager</span>, <span className="icode">CommonUtil</span>{' '}
같이 두루뭉술하고, 필드와 메서드가 수십 개예요. <strong>③ 중복 코드</strong> —
같은 로직이 복붙으로 세 군데 살아요. 한 곳만 고치면 나머지 둘이 조용히 낡습니다.{' '}
<strong>④ 기능 편애(Feature Envy)</strong> — 어떤 함수가 자기 클래스보다{' '}
<strong>남의 클래스 데이터</strong>를 더 열심히 주물러요. 그 로직은 데이터가 사는
곳으로 이사 가야 한다는 신호입니다.
</p>
<Code>{CODE_SMELL_MAP}</Code>
<p>
표의 아래 두 줄을 보세요. 사실 우리는 이미 냄새 훈련을 해 왔어요. 성능 강의에서 잡은{' '}
<strong>N+1 쿼리</strong>는 "반복문 안의 외부 호출"이라는 냄새의 DB 버전이고, 설계
강의의 <strong>God Service</strong>는 " 클래스" 냄새가 서비스 계층에서 자란
모습입니다. 냄새 목록은 새 지식이 아니라, <strong>흩어져 있던 감각에 이름을 붙여
목록으로 만든 것</strong>이에요. 이름이 붙어야 리뷰 코멘트로 쓸 수 있습니다.
</p>
<div className="tip">
<b>냄새 ≠ 즉시 수리</b> 냄새를 발견했다고 그 자리에서 다 고치는 게 아니에요.
기록해 두고(이슈, TODO 주석보다는 이슈가 좋아요) <strong>섹션 4의 우선순위</strong>로
갚을 순서를 정합니다. 냄새 감지와 수리 결정은 별개의 판단이에요.
</div>
</Section>
<Section n={3} title="작은 단계 리팩터링" sub="추출·이름 변경·인라인, 그리고 잘게 쪼갠 커밋">
<p>
리팩터링 기법은 책 한 권 분량이지만, 실무 체감 8할은 세 가지 기본기예요.{' '}
<strong>① 추출(Extract)</strong> — 긴 함수에서 덩어리를 꺼내 이름을 붙입니다.{' '}
<strong>② 이름 변경(Rename)</strong> — <span className="icode">data2</span>,{' '}
<span className="icode">temp</span>를 역할이 보이는 이름으로 바꿉니다. 이름이 좋아지면
주석이 필요 없어져요. <strong>③ 인라인(Inline)</strong> — 반대로, 한 줄짜리 의미 없는
함수·변수는 다시 안으로 밀어 넣어 단계를 줄입니다. 추출의 되감기 버튼이죠.
</p>
<Code>{CODE_EXTRACT}</Code>
<p>
이 기본기들은 <strong>IDE의 자동 리팩터링</strong>으로 하는 게 원칙이에요. IntelliJ나
VS Code에서 함수 추출·이름 변경을 실행하면, <strong>프로젝트 전체의 참조를 기계가
전부 따라가며</strong> 바꿔 줍니다. 손으로 찾아 바꾸기(Ctrl+H)는 문자열이 우연히 같은
엉뚱한 곳까지 바꿔 버릴 수 있어요. 기계가 잘하는 일은 기계에게 맡기고, 사람은{' '}
<strong>무엇을 추출하고 뭐라고 이름 붙일지</strong>라는 판단에 집중합니다.
</p>
<p>
그리고 판단의 마지막 조각 — <strong>커밋을 작게 쪼개기</strong>. 리팩터링 커밋과 동작
변경 커밋을 분리하면, 문제가 생겼을 때 <strong>해당 커밋 하나만 되돌리면(revert)</strong>{' '}
됩니다. 섞인 300줄 커밋은 되돌리는 순간 멀쩡한 변경까지 같이 날아가요.
</p>
<Code>{CODE_SMALL_COMMITS}</Code>
<div className="tip">
<b>직접 확인해 보기</b> 에디터에서 아무 함수나 블록을 선택하고 리팩터링 메뉴(IntelliJ는{' '}
<span className="icode">Ctrl+Alt+M</span>, VS Code는 전구 아이콘)를 눌러 보세요.
함수 추출이 참조까지 한 번에 정리되는 걸 눈으로 보면, 다시는 손으로 안 하게 됩니다.
</div>
</Section>
<Section n={4} title="리팩터링 우선순위" sub="빚은 이자가 나가는 곳부터 갚는다">
<p>
냄새 목록을 만들면 항상 같은 고민이 옵니다. "이걸 언제 고치지?" — 답은{' '}
<strong>다 고치지 않는 것</strong>이에요. 기술 부채의 이자는{' '}
<strong>그 코드를 만질 때마다</strong> 나갑니다. 아무도 안 여는 낡은 코드는 지저분해도
이자가 0원이에요. 그러니 <strong>자주 바뀌는 곳부터</strong>(변경 빈도 × 코드 상태)
정리하는 게 투자 대비 효과가 가장 큽니다.
</p>
<Code>{CODE_PRIORITY}</Code>
<p>
일상에서는 <strong>보이스카웃 규칙</strong>이 이 우선순위를 자동으로 실행해 줘요.
"캠핑장을 떠날 때는 왔을 때보다 조금 깨끗하게" — 코드에 적용하면,{' '}
<strong>어떤 파일을 고치러 들어갔다면 나올 때 아주 조금만 더 깨끗하게 하고
나온다</strong>는 규칙입니다. 이름 하나 고치기, 매직 넘버 하나 상수로 빼기 정도요.
어차피 자주 만지는 파일에 자주 들어가게 되니, 뜨거운 길목일수록 저절로 자주
정리됩니다.
</p>
<p>
마지막으로 <strong>전면 재작성의 유혹</strong>을 경계해야 해요. 지저분한 코드를 보면
"이건 갈아엎는 빠르겠다"는 생각이 들죠. 하지만 낡은 코드에는{' '}
<strong>수년치 버그 수정과 엣지 케이스 처리</strong>가 배어 있고, 재작성은 그걸 전부
다시 발견해야 합니다. 재작성하는 몇 달 동안 기능 개발은 멈추고, 새 코드는 새 버그를
데려와요. 정말 재작성이 답인 경우도 있지만(플랫폼 수명 종료 등), 대부분은{' '}
<strong>돌아가는 시스템을 작은 단계로 고쳐 가는 쪽</strong>이 이깁니다.
</p>
<div className="tip">
<b>비유</b> 재작성은 <strong>살면서 집을 허물고 새로 짓기</strong>예요. 그동안 어디서
살죠? 리팩터링은 <strong>살면서 방 하나씩 고치기</strong>고요. 불편해도 집은 계속
집이어야 합니다 — 서비스는 재작성 중에도 계속 돌아가야 하니까요.
</div>
</Section>
<Section n={5} title="리뷰의 목적" sub="결함 발견은 절반, 나머지 절반은 지식 공유">
<p>
코드 리뷰를 "버그 잡는 관문"으로만 알고 있다면 절반만 아는 거예요. 리뷰의 효용은
크게 셋입니다. <strong>① 결함 발견</strong> — 물론 중요하죠. 그런데{' '}
<strong>② 지식 공유</strong>가 못지않게 커요. 리뷰를 거친 코드는{' '}
<strong>최소 두 사람</strong>이 이해하는 코드가 됩니다. 담당자가 휴가를 가도, 퇴사해도
팀이 멈추지 않는 이유죠. <strong>③ 표준 정착</strong> — 네이밍, 계층 규칙, 에러 처리
방식 같은 팀의 합의가 리뷰 코멘트를 통해 매일 조금씩 전파되고 단단해집니다.
</p>
<p>
그리고 리뷰 문화의 제1원칙 — <strong>사람이 아니라 코드를 리뷰한다.</strong>{' '}
"당신이 틀렸다"가 아니라 " 코드가 이런 상황에서 이렇게 동작할 같다"로 말해요.
작성자도 마찬가지로, 코멘트를 <strong>내 실력에 대한 판정이 아니라 코드에 대한
정보</strong>로 받습니다. 이 분리가 무너지면 리뷰는 방어전이 되고, 방어전이 된 팀에선
아무도 솔직한 코멘트를 달지 않아요 — 그때부터 리뷰는 요식 행위가 됩니다.
</p>
<Code>{CODE_PR_CONVENTION}</Code>
<p>
우리 팀은 커밋 메시지·PR 설명·리뷰 코멘트를 <strong>한국어</strong>로 쓰고, 커밋
접두어(<span className="icode">feat:</span> <span className="icode">fix:</span>{' '}
<span className="icode">refactor:</span> <span className="icode">docs:</span>)로 변경의
성격을 먼저 밝힙니다. 접두어 덕에 <span className="icode">git log</span>만 훑어도
"동작이 바뀐 커밋"과 "구조만 바뀐 커밋"이 구분돼요 — 섹션 1의 두 모자 규율이 기록에도
남는 거죠.
</p>
<div className="tip">
<b>리뷰는 저자에게도 이득</b> 신기하게도, "누가 읽는다"는 사실만으로 코드가 좋아져요.
PR을 올리기 전에 스스로 한 번 훑으며 설명을 쓰다 보면 본인이 먼저 문제를 발견하는
일이 흔합니다. 이걸 <strong>셀프 리뷰</strong>라고 하고, 리뷰어의 시간을 아껴 주는
기본 매너이기도 해요.
</div>
</Section>
<Section n={6} title="좋은 리뷰 실천" sub="작은 PR, 재현 가능한 근거, 블로킹과 제안의 구분">
<p>
좋은 리뷰의 첫째 조건은 리뷰어가 아니라 <strong>작성자</strong>가 만듭니다 — 바로{' '}
<strong>작은 PR</strong>이에요. 변경 1,000줄짜리 PR은 아무리 성실한 리뷰어도 대충 훑고
"LGTM"을 누르게 됩니다(다 읽을 수 없으니까요). 통계적으로도 리뷰 품질은 변경량이
커질수록 급격히 떨어져요. 대략 <strong>200~400줄 이내</strong>, " 가지 "만 하는
PR이 리뷰가 가장 정확합니다. 커밋을 쪼갰듯 PR도 쪼개는 거예요.
</p>
<p>
둘째는 <strong>재현 가능한 근거</strong>입니다. "느려질 같아요"보다{' '}
<strong>" 쿼리를 1 데이터로 돌려 보니 3.2 걸렸어요, 실행계획은 이렇습니다"</strong>가
강해요. 리뷰 코멘트도, PR 설명도 <strong>따라 해 볼 수 있는 절차와 숫자</strong>가
붙으면 논쟁이 짧아집니다. 취향의 문제는 취향이라고 인정하고, 사실의 문제는 사실로
판정하는 거죠.
</p>
<p>
셋째, 리뷰어는 <strong>관점 체크리스트</strong>를 돌립니다. 즉흥적으로 눈에 띄는
것만 보면 그날 컨디션에 따라 품질이 출렁여요. 설계 → 성능 → 보안 → 운영, 관점별로
한 바퀴 도는 습관이 누락을 막습니다.
</p>
<Code>{CODE_REVIEW_CHECKLIST}</Code>
<p>
체크리스트 끝의 구분을 눈여겨보세요. <strong>블로킹(blocking)</strong>은 "이건 머지
전에 반드시 해결돼야 한다"(보안 구멍, N+1, 롤백 불가), <strong>제안(non-blocking)</strong>은
"나는 이렇게 하겠지만 당신 판단에 맡긴다"(네이밍 취향, 사소한 개선)예요. 모든 코멘트가
블로킹처럼 읽히면 작성자는 지치고 PR은 며칠씩 묶입니다. <strong>코멘트마다 어느 쪽인지
명시</strong>하는 것만으로 리뷰 속도와 분위기가 눈에 띄게 좋아져요.
</p>
<div className="tip">
<b>승인의 의미</b> "Approve"는 "완벽하다"가 아니라 <strong>"내가 코드에 같이 책임을
진다"</strong>는 뜻이에요. 그래서 대충 누르는 LGTM이 위험한 겁니다. 반대로, 제안 코멘트만
남았다면 승인과 함께 맡기는 게 좋아요 — 완벽주의로 PR을 붙들면 팀 전체가 느려집니다.
</div>
</Section>
<Section n={7} title="종합: 만드는 " sub="12강의 관점을 하나의 체크리스트로">
<p>
이제 사다리 꼭대기에서 지나온 계단을 내려다볼 차례예요. 설계 강의는 "책임이 제자리에
있는가"를, 성능 강의는 "데이터가 늘어도 버티는가"를, 보안 강의는 "악의적인 입력에도
안전한가"를, 운영 강의는 "장애가 나면 추적할 있는가"를 물었죠. 그 질문들이 각각
따로 노는 게 아니라 — <strong>매 PR의 리뷰에서 한꺼번에 던져질 때</strong>, 비로소
팀의 품질이 됩니다.
</p>
<Code>{CODE_FINAL_LADDER}</Code>
<p>
그래서 이 강의의 실습이 <strong>체크리스트 만들기</strong>인 거예요. 섹션 6의 예시를
출발점 삼아, 앞 강의들에서 <strong>여러분이 실제로 데인 것들</strong>(우리 코드에서
만난 N+1, 우리가 놓쳤던 검증 누락)을 항목으로 옮기세요. 남의 체크리스트를 복사하면
장식이 되고, <strong>우리 팀의 사고가 담기면 도구</strong>가 됩니다. 그리고 실제 PR
하나에 적용해 보세요 — 항목이 너무 많으면 줄이고, 매번 걸리는 게 있으면 자동화(린터·
CI)로 내리는 겁니다.
</p>
<p>
마지막으로, 체크리스트는 <strong>완성품이 아니라 살아 있는 문서</strong>예요. 장애를
겪으면 항목이 하나 늘고, 린터가 대신 잡게 되면 항목이 하나 줄어요. 이렇게{' '}
<strong>회고 → 반영 → 자동화</strong>의 고리를 도는 것이 지속적 개선 문화고, " 만드는
"의 실체입니다. 천재 한 명이 아니라, <strong>반복되는 좋은 질문</strong>이 품질을
만들어요.
</p>
<div className="tip">
<b>실습 과제</b> ① 섹션 6 체크리스트를 바탕으로 <strong>우리 팀용 10~15개 항목</strong>을
추려 문서로 만드세요(블로킹/제안 표시 포함). ② 최근 머지된 실제 PR 하나를 골라 그
체크리스트로 <strong>다시 리뷰</strong>해 보세요. ③ 원 리뷰가 놓친 항목이 있었다면,
왜 놓쳤는지(항목이 없어서? 급해서?)를 한 줄로 기록하세요 — 그 한 줄이 체크리스트
버전 2의 재료입니다.
</div>
</Section>
{/* 마무리 카드: 고급 과정 전체의 마무리 — 학습 센터로 안내 */}
<div className="step-card">
<div className="step-body">
<h3 style={{ marginBottom: 6 }}>🧭 여기까지 왔다면</h3>
<p className="muted">
고급 과정의 사다리를 전부 올랐습니다. 서비스를 <strong>만드는</strong> 사람에서,
설계·성능·보안·운영을 저울에 올려 <strong>잘 만드는</strong> 사람으로 — 그리고 그
감각을 리뷰와 체크리스트로 <strong>팀에 전파하는</strong> 사람으로 한 계단씩
올라왔어요. 이제 남은 건 반복입니다. 오늘 만든 체크리스트로 내일의 PR을 리뷰하고,
데일 때마다 항목을 고치세요. 다른 과정의 지도가 궁금하다면{' '}
<Link to="/learn"><strong>학습 센터</strong></Link> .
</p>
</div>
</div>
</div>
);
}