기존 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>
461 lines
30 KiB
JavaScript
461 lines
30 KiB
JavaScript
// 이 파일이 하는 일: "코드 리뷰와 리팩터링 전략" 코스 — 고급 과정의 종착점.
|
||
// 동작을 바꾸지 않고 구조를 개선하는 규율(리팩터링)에서 출발해, 코드 냄새 목록,
|
||
// 작은 단계 기법, 우선순위 정하기, 리뷰의 진짜 목적, 좋은 리뷰 실천을 거쳐,
|
||
// 앞 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>
|
||
);
|
||
}
|