docs: 코드리뷰 실습 가이드 + 캡스톤·데모데이 문서 추가
- code-review.html: 5~8주차 티켓 흐름에 상시 코드리뷰 도입. 왜/리뷰 흐름(PR→동료+멘토→반영→merge)/리뷰어 체크리스트/ 좋은·나쁜 코멘트 예시/받는 자세. 우리 실제 버그를 예시로. - capstone-demoday.html: 7~8주차 캡스톤 = 1인 1기능(플랫폼 위) + 8주차 금 데모데이. 밤샘 해커톤 아닌 미니 스프린트+발표. 일정·발표법·평가 연결. - 평가 루브릭 ③ 협업·소통에 코드리뷰 항목 명시(주는 것/받는 것) + dim-note 스타일 - DocsPage에 'PRACTICE(실습)' 카테고리 추가, documents.json + 운영 DB 시드(학생 노출) 검증: 학생 계정에 두 문서 노출·렌더 확인. 커리큘럼 확장 없이 기존 티켓·PR·루브릭에 얹음. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
84144a1608
commit
bb12d072b1
@ -15,5 +15,7 @@
|
||||
{ "slug": "week2-assignments", "title": "2주차 일일 과제집 — 코드 읽기 & 자료구조 적용", "category": "ASSIGNMENT", "week": 2, "audience": "STUDENT", "filePath": "week2-assignments.html", "sortOrder": 14 },
|
||||
{ "slug": "week3-assignments", "title": "3주차 일일 과제집 — 첫 소과제 실전", "category": "ASSIGNMENT", "week": 3, "audience": "STUDENT", "filePath": "week3-assignments.html", "sortOrder": 15 },
|
||||
{ "slug": "week4-assignments", "title": "4주차 일일 과제집 — 통합·발표 준비", "category": "ASSIGNMENT", "week": 4, "audience": "STUDENT", "filePath": "week4-assignments.html", "sortOrder": 16 },
|
||||
{ "slug": "ticket-pool", "title": "수습용 실전 티켓 풀 20", "category": "TICKET", "week": null, "audience": "MENTOR", "filePath": "ticket-pool.html", "sortOrder": 17 }
|
||||
{ "slug": "ticket-pool", "title": "수습용 실전 티켓 풀 20", "category": "TICKET", "week": null, "audience": "MENTOR", "filePath": "ticket-pool.html", "sortOrder": 17 },
|
||||
{ "slug": "code-review", "title": "코드리뷰 실습 가이드", "category": "PRACTICE", "week": null, "audience": "STUDENT", "filePath": "code-review.html", "sortOrder": 18 },
|
||||
{ "slug": "capstone-demoday", "title": "캡스톤 & 데모데이", "category": "PRACTICE", "week": null, "audience": "STUDENT", "filePath": "capstone-demoday.html", "sortOrder": 19 }
|
||||
]
|
||||
|
||||
172
frontend/public/docs/capstone-demoday.html
Normal file
172
frontend/public/docs/capstone-demoday.html
Normal file
@ -0,0 +1,172 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>캡스톤 & 데모데이 · AWESOMEDEV</title>
|
||||
<style>
|
||||
:root {
|
||||
--paper: #FAFBFD; --card: #FFFFFF; --ink: #101A28; --ink-soft: #46536A;
|
||||
--ink-faint: #7B8798; --line: #E4E8F0; --line-soft: #EFF2F7;
|
||||
--primary: #2159C5; --primary-soft: #E5EDFB; --primary-deep: #163F8F;
|
||||
--amber: #B9772A; --amber-soft: #F6EBDA;
|
||||
--teal: #0E8C86; --teal-soft: #E0F1EF;
|
||||
--rose: #B4463C; --rose-soft: #F7E7E4;
|
||||
--code-bg: #F1F4F9;
|
||||
--shadow: 0 1px 2px rgba(16,26,40,.05), 0 8px 24px -12px rgba(16,26,40,.16);
|
||||
--radius: 14px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--paper: #0B1017; --card: #131A24; --ink: #E8EDF4; --ink-soft: #A4B0C0;
|
||||
--ink-faint: #74808F; --line: #263140; --line-soft: #1C242F;
|
||||
--primary: #6AA0F0; --primary-soft: #172A44; --primary-deep: #AECBF8;
|
||||
--amber: #DBA25C; --amber-soft: #2C2318;
|
||||
--teal: #4FC7BE; --teal-soft: #14302E; --rose: #E08A80; --rose-soft: #331E1B;
|
||||
--code-bg: #161E28;
|
||||
--shadow: 0 1px 2px rgba(0,0,0,.35), 0 10px 30px -14px rgba(0,0,0,.65);
|
||||
}
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
|
||||
body { margin: 0; background: var(--paper); color: var(--ink);
|
||||
font-family: "Pretendard","Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",system-ui,sans-serif;
|
||||
line-height: 1.72; -webkit-font-smoothing: antialiased; letter-spacing: -0.01em; }
|
||||
.wrap { max-width: 820px; margin: 0 auto; padding: clamp(20px,4vw,48px); }
|
||||
.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--primary); display: flex; align-items: center; gap: 10px; }
|
||||
.eyebrow::before { content:""; width: 22px; height: 2px; background: currentColor; border-radius: 2px; }
|
||||
header.mast { border: 1px solid var(--line); border-radius: var(--radius);
|
||||
background: radial-gradient(120% 140% at 100% 0%, var(--primary-soft) 0%, transparent 55%), var(--card);
|
||||
box-shadow: var(--shadow); padding: clamp(26px,4vw,42px); }
|
||||
.wordmark { font-size: 13px; font-weight: 800; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.wordmark b { color: var(--primary); }
|
||||
header.mast h1 { font-size: clamp(27px,5.4vw,40px); line-height: 1.15; margin: 16px 0 0; font-weight: 800; letter-spacing: -0.03em; text-wrap: balance; }
|
||||
header.mast .lede { margin: 16px 0 0; font-size: clamp(15px,2.4vw,17px); color: var(--ink-soft); max-width: 58ch; }
|
||||
.mast-meta { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px; }
|
||||
.chip { font-size: 13px; font-weight: 600; color: var(--ink-soft); background: var(--paper); border: 1px solid var(--line); padding: 6px 12px; border-radius: 999px; display: inline-flex; align-items: center; gap: 7px; }
|
||||
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); }
|
||||
section { margin-top: clamp(30px,5vw,46px); scroll-margin-top: 20px; }
|
||||
section > h2 { font-size: clamp(20px,3.4vw,25px); font-weight: 800; letter-spacing: -0.025em; margin: 12px 0 0; text-wrap: balance; }
|
||||
section > .sub { color: var(--ink-soft); margin: 9px 0 0; font-size: 15px; max-width: 64ch; }
|
||||
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: clamp(18px,3vw,26px); }
|
||||
.mt { margin-top: 16px; }
|
||||
p { margin: 0; } strong, b { font-weight: 700; }
|
||||
h3 { font-size: 16.5px; font-weight: 800; margin: 0 0 8px; letter-spacing: -0.01em; }
|
||||
|
||||
.days { display: grid; gap: 10px; margin-top: 16px; }
|
||||
.day { display: grid; grid-template-columns: 92px 1fr; gap: 16px; align-items: start; padding: 15px 18px; border: 1px solid var(--line); border-radius: 12px; background: var(--card); }
|
||||
.day .when { font-weight: 800; color: var(--primary); font-size: 14px; }
|
||||
.day .when span { display: block; font-weight: 600; color: var(--ink-faint); font-size: 12px; margin-top: 2px; }
|
||||
.day .t { font-weight: 700; color: var(--ink); }
|
||||
.day .d { color: var(--ink-soft); font-size: 14px; margin-top: 3px; }
|
||||
@media (max-width: 560px){ .day { grid-template-columns: 1fr; gap: 6px; } }
|
||||
|
||||
.ideas { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
|
||||
@media (max-width: 620px){ .ideas { grid-template-columns: 1fr; } }
|
||||
.idea { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; background: var(--card); }
|
||||
.idea .tag { font-size: 11px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--teal); }
|
||||
.idea .h { font-weight: 700; margin: 5px 0 4px; }
|
||||
.idea .d { color: var(--ink-soft); font-size: 13.5px; }
|
||||
|
||||
.rules { margin-top: 14px; display: grid; gap: 9px; }
|
||||
.rules .item { display: grid; grid-template-columns: 22px 1fr; gap: 11px; font-size: 14.5px; color: var(--ink-soft); align-items: start; }
|
||||
.rules .mk { color: var(--teal); font-weight: 800; margin-top: 1px; }
|
||||
.rules b { color: var(--ink); }
|
||||
|
||||
.judge { width: 100%; border-collapse: collapse; margin-top: 14px; font-size: 14px; }
|
||||
.judge th, .judge td { border: 1px solid var(--line); padding: 9px 12px; text-align: left; }
|
||||
.judge th { background: var(--primary-soft); color: var(--primary-deep); font-weight: 700; white-space: nowrap; }
|
||||
.judge td.n { text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
|
||||
code, .icode { background: var(--code-bg); border-radius: 5px; padding: 1px 6px; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 0.9em; }
|
||||
.tip { margin-top: 16px; background: var(--teal-soft); border: 1px solid color-mix(in srgb, var(--teal) 28%, transparent); border-radius: 12px; padding: 14px 16px; font-size: 14px; color: var(--ink-soft); }
|
||||
.tip b { color: var(--teal); }
|
||||
.warn { margin-top: 16px; background: var(--amber-soft); border: 1px solid color-mix(in srgb, var(--amber) 32%, transparent); border-radius: 12px; padding: 14px 16px; font-size: 14px; color: var(--ink-soft); }
|
||||
.warn b { color: var(--amber); }
|
||||
footer { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; color: var(--ink-faint); font-size: 13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header class="mast">
|
||||
<div class="wordmark"><b>AWESOMEDEV</b> · 실습 가이드</div>
|
||||
<div class="eyebrow" style="margin-top:16px">Capstone & Demo Day</div>
|
||||
<h1>캡스톤 & 데모데이</h1>
|
||||
<p class="lede">8주의 마지막, 각자 <b>기능 하나를 처음부터 끝까지</b> 만들어 대표님 앞에서 시연합니다.
|
||||
밤샘 경쟁 해커톤이 아니라, 두 달간 배운 걸 종합하는 <b>미니 스프린트 + 발표</b>예요. 자기가 매일 쓰는 이 플랫폼을 더 좋게 만듭니다.</p>
|
||||
<div class="mast-meta">
|
||||
<span class="chip"><span class="dot"></span>7~8주차 · 종합</span>
|
||||
<span class="chip"><span class="dot"></span>1인 1기능 (디자이너는 페어)</span>
|
||||
<span class="chip"><span class="dot"></span>데모데이: 8주차 금요일</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">What</div>
|
||||
<h2>무엇을 만드나</h2>
|
||||
<p class="sub">거창한 새 서비스가 아니에요. "이 플랫폼에 있으면 좋겠다" 싶은 기능 하나를, 혼자 힘으로 끝까지.</p>
|
||||
<div class="card mt">
|
||||
<p>대상은 <b>여러분이 매일 쓰는 이 학습 플랫폼(mirim-app)</b>입니다. 8주간 티켓으로 이 코드를 만져 왔으니
|
||||
구조를 이미 알아요. 그 위에 "화면 → API → DB"를 관통하는 기능 하나를 스스로 설계하고 구현합니다.
|
||||
범위는 <b>4~5일에 끝낼 수 있는 크기</b> — 멘토와 함께 첫날 범위를 확정해요.</p>
|
||||
</div>
|
||||
<div class="ideas">
|
||||
<div class="idea"><div class="tag">개발</div><div class="h">학습 뱃지·연속 출석</div><div class="d">코스를 N개 완료하면 뱃지, 며칠 연속 학습했는지 표시. 진도 데이터를 활용.</div></div>
|
||||
<div class="idea"><div class="tag">개발</div><div class="h">코딩 문제 즐겨찾기</div><div class="d">어려웠던 문제를 별표해 두고 나중에 다시 풀기. 새 테이블 + 화면.</div></div>
|
||||
<div class="idea"><div class="tag">개발</div><div class="h">멘토용 주간 리포트</div><div class="d">이번 주 학생별 제출·진도 요약을 한 화면에. 집계 쿼리 연습.</div></div>
|
||||
<div class="idea"><div class="tag">디자인+개발</div><div class="h">대시보드 리디자인</div><div class="d">첫 화면을 더 동기부여되게. 디자이너가 설계, 개발자와 페어로 반영.</div></div>
|
||||
</div>
|
||||
<div class="tip"><b>아이디어는 자유예요.</b> 위는 예시일 뿐 — 자기가 "있으면 진짜 쓸 것 같은" 걸 고르는 게 제일 좋아요. 단, 첫날 멘토와 범위를 못 박아 "5일 안에 끝나는 크기"로 자릅니다.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Schedule</div>
|
||||
<h2>일정 — 7주차 기획, 8주차 스프린트</h2>
|
||||
<p class="sub">이미 7주차(기획·설계) → 8주차(구현·발표) 커리큘럼과 이어져요. 그 뼈대에 데모데이를 얹습니다.</p>
|
||||
<div class="days">
|
||||
<div class="day"><div class="when">7주차<span>기획 주간</span></div><div><div class="t">주제 정하기 · 설계 발표</div><div class="d">무엇을·왜 만들지 정하고, 화면 스케치 + 데이터 구조(어떤 테이블·API)를 한 장으로. 주 끝에 멘토·동료 앞에서 3분 설계 발표 → 피드백으로 범위 조정.</div></div></div>
|
||||
<div class="day"><div class="when">8주차 월~목<span>스프린트</span></div><div><div class="t">구현 + 매일 스탠드업</div><div class="d">매일 아침 5분 "어제 한 것·오늘 할 것·막힌 것" 공유. 중간에 PR을 열어 동료·멘토 리뷰(코드리뷰 가이드 그대로). 막히면 30분 룰로 질문.</div></div></div>
|
||||
<div class="day"><div class="when">8주차 금<span>데모데이 🎉</span></div><div><div class="t">시연 + 발표 + 회고</div><div class="d">각자 5분 시연 + 3분 Q&A. 대표님·멘토·동료가 관객. "무엇을·왜·어떻게·무엇을 배웠나"를 담아요. 끝나고 두 달 전체 회고.</div></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Demo Day</div>
|
||||
<h2>발표는 이렇게</h2>
|
||||
<p class="sub">코드가 멋진 것보다 "무엇을 배웠는지 전할 수 있는가"가 더 중요해요. 5분이면 충분합니다.</p>
|
||||
<div class="card mt">
|
||||
<div class="rules">
|
||||
<div class="item"><span class="mk">1</span><div><b>왜 만들었나</b> (30초) — 어떤 불편을 풀고 싶었는지.</div></div>
|
||||
<div class="item"><span class="mk">2</span><div><b>실제로 보여주기</b> (2분) — 슬라이드 말고 <b>돌아가는 화면</b>으로 시연. 미리 시나리오를 연습해 두기.</div></div>
|
||||
<div class="item"><span class="mk">3</span><div><b>어떻게 만들었나</b> (1분) — 화면·API·DB가 어떻게 이어지는지 한 장으로.</div></div>
|
||||
<div class="item"><span class="mk">4</span><div><b>무엇을 배웠나 · 못한 것</b> (1분) — 막혔던 지점과 배움. 못 끝낸 부분을 솔직히 말하는 게 오히려 점수예요.</div></div>
|
||||
<div class="item"><span class="mk">5</span><div><b>Q&A</b> (3분) — 관객 질문. 모르면 "그건 아직 몰라요, 찾아볼게요"가 정답.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warn"><b>완성이 목표가 아니에요.</b> 미완이어도 괜찮습니다 — 어디까지 했고 왜 거기서 멈췄는지 설명할 수 있으면 돼요. 밤새우지 마세요. 지속가능한 속도가 실무의 기본입니다.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Evaluation</div>
|
||||
<h2>무엇을 보나</h2>
|
||||
<p class="sub">캡스톤은 별도 점수가 아니라, 8주 평가 루브릭의 여러 축을 <b>한 번에 종합해 확인하는 자리</b>예요.</p>
|
||||
<table class="judge">
|
||||
<thead><tr><th>보는 것</th><th>연결되는 평가 축</th><th class="n">무게</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>혼자 힘으로 화면~DB를 관통했나</td><td>② 기본기 · 직무 역량</td><td class="n">높음</td></tr>
|
||||
<tr><td>스탠드업·리뷰·질문으로 잘 협업했나</td><td>③ 협업 · 소통</td><td class="n">높음</td></tr>
|
||||
<tr><td>막힘을 스스로 헤쳐 나갔나</td><td>① 성장 속도 · 학습력</td><td class="n">중간</td></tr>
|
||||
<tr><td>발표로 자기 일을 설명했나</td><td>③ 협업 · 소통 / ④ 태도</td><td class="n">중간</td></tr>
|
||||
<tr><td>끝까지 책임지고 마무리했나</td><td>④ 태도 · 책임감 / ⑤ 성장 가능성</td><td class="n">중간</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="tip"><b>즉, 캡스톤은 8주의 축소판이에요.</b> 평소에 티켓·리뷰·질문을 성실히 한 사람이 자연스럽게 잘합니다 — 벼락치기가 안 되는 구조예요. 자세한 채점은 <b>「어썸데브 수습 평가 루브릭」</b> 참고.</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<span>AWESOMEDEV · 캡스톤 & 데모데이</span>
|
||||
<span>7~8주차 · 데모데이 8주차 금요일</span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
196
frontend/public/docs/code-review.html
Normal file
196
frontend/public/docs/code-review.html
Normal file
@ -0,0 +1,196 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>코드리뷰 실습 가이드 · AWESOMEDEV</title>
|
||||
<style>
|
||||
:root {
|
||||
--paper: #FAFBFD; --card: #FFFFFF; --ink: #101A28; --ink-soft: #46536A;
|
||||
--ink-faint: #7B8798; --line: #E4E8F0; --line-soft: #EFF2F7;
|
||||
--primary: #2159C5; --primary-soft: #E5EDFB; --primary-deep: #163F8F;
|
||||
--amber: #B9772A; --amber-soft: #F6EBDA;
|
||||
--teal: #0E8C86; --teal-soft: #E0F1EF;
|
||||
--rose: #B4463C; --rose-soft: #F7E7E4;
|
||||
--code-bg: #F1F4F9;
|
||||
--shadow: 0 1px 2px rgba(16,26,40,.05), 0 8px 24px -12px rgba(16,26,40,.16);
|
||||
--radius: 14px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--paper: #0B1017; --card: #131A24; --ink: #E8EDF4; --ink-soft: #A4B0C0;
|
||||
--ink-faint: #74808F; --line: #263140; --line-soft: #1C242F;
|
||||
--primary: #6AA0F0; --primary-soft: #172A44; --primary-deep: #AECBF8;
|
||||
--amber: #DBA25C; --amber-soft: #2C2318;
|
||||
--teal: #4FC7BE; --teal-soft: #14302E; --rose: #E08A80; --rose-soft: #331E1B;
|
||||
--code-bg: #161E28;
|
||||
--shadow: 0 1px 2px rgba(0,0,0,.35), 0 10px 30px -14px rgba(0,0,0,.65);
|
||||
}
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
|
||||
body { margin: 0; background: var(--paper); color: var(--ink);
|
||||
font-family: "Pretendard","Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",system-ui,sans-serif;
|
||||
line-height: 1.72; -webkit-font-smoothing: antialiased; letter-spacing: -0.01em; }
|
||||
.wrap { max-width: 820px; margin: 0 auto; padding: clamp(20px,4vw,48px); }
|
||||
.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--primary); display: flex; align-items: center; gap: 10px; }
|
||||
.eyebrow::before { content:""; width: 22px; height: 2px; background: currentColor; border-radius: 2px; }
|
||||
header.mast { border: 1px solid var(--line); border-radius: var(--radius);
|
||||
background: radial-gradient(120% 140% at 100% 0%, var(--primary-soft) 0%, transparent 55%), var(--card);
|
||||
box-shadow: var(--shadow); padding: clamp(26px,4vw,42px); }
|
||||
.wordmark { font-size: 13px; font-weight: 800; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-soft); }
|
||||
.wordmark b { color: var(--primary); }
|
||||
header.mast h1 { font-size: clamp(27px,5.4vw,40px); line-height: 1.15; margin: 16px 0 0; font-weight: 800; letter-spacing: -0.03em; text-wrap: balance; }
|
||||
header.mast .lede { margin: 16px 0 0; font-size: clamp(15px,2.4vw,17px); color: var(--ink-soft); max-width: 58ch; }
|
||||
.mast-meta { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px; }
|
||||
.chip { font-size: 13px; font-weight: 600; color: var(--ink-soft); background: var(--paper); border: 1px solid var(--line); padding: 6px 12px; border-radius: 999px; display: inline-flex; align-items: center; gap: 7px; }
|
||||
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); }
|
||||
section { margin-top: clamp(30px,5vw,46px); scroll-margin-top: 20px; }
|
||||
section > h2 { font-size: clamp(20px,3.4vw,25px); font-weight: 800; letter-spacing: -0.025em; margin: 12px 0 0; text-wrap: balance; }
|
||||
section > .sub { color: var(--ink-soft); margin: 9px 0 0; font-size: 15px; max-width: 64ch; }
|
||||
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: clamp(18px,3vw,26px); }
|
||||
.mt { margin-top: 16px; }
|
||||
p { margin: 0; } strong, b { font-weight: 700; }
|
||||
h3 { font-size: 16.5px; font-weight: 800; margin: 0 0 8px; letter-spacing: -0.01em; }
|
||||
|
||||
.why { display: grid; gap: 12px; margin-top: 16px; }
|
||||
.why li { list-style: none; display: grid; grid-template-columns: 26px 1fr; gap: 12px; font-size: 14.5px; color: var(--ink-soft); }
|
||||
.why .n { width: 22px; height: 22px; border-radius: 6px; background: var(--primary); color: #fff; font-weight: 800; font-size: 12px; display: grid; place-items: center; margin-top: 3px; }
|
||||
@media (prefers-color-scheme: dark){ .why .n { color:#0B1017; } }
|
||||
.why b { color: var(--ink); }
|
||||
|
||||
.flow { display: grid; gap: 10px; margin-top: 16px; counter-reset: fl; }
|
||||
.flow .step { display: grid; grid-template-columns: 34px 1fr; gap: 14px; align-items: start; padding: 14px 16px; border: 1px solid var(--line); border-radius: 12px; background: var(--card); }
|
||||
.flow .step::before { counter-increment: fl; content: counter(fl); width: 28px; height: 28px; border-radius: 8px; background: var(--primary-soft); color: var(--primary-deep); font-weight: 800; display: grid; place-items: center; font-variant-numeric: tabular-nums; }
|
||||
.flow .step .t { font-weight: 700; color: var(--ink); }
|
||||
.flow .step .d { color: var(--ink-soft); font-size: 14px; margin-top: 2px; }
|
||||
|
||||
.checklist { margin-top: 14px; display: grid; gap: 9px; }
|
||||
.checklist .item { display: grid; grid-template-columns: 22px 1fr; gap: 11px; font-size: 14.5px; color: var(--ink-soft); align-items: start; }
|
||||
.checklist .box { width: 17px; height: 17px; border: 1.5px solid var(--primary); border-radius: 5px; margin-top: 3px; }
|
||||
.checklist b { color: var(--ink); }
|
||||
|
||||
.ex { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 16px; }
|
||||
@media (max-width: 620px){ .ex { grid-template-columns: 1fr; } }
|
||||
.ex .col { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
|
||||
.ex .col .hd { padding: 9px 14px; font-size: 13px; font-weight: 800; }
|
||||
.ex .bad .hd { background: var(--rose-soft); color: var(--rose); }
|
||||
.ex .good .hd { background: var(--teal-soft); color: var(--teal); }
|
||||
.ex .bd { padding: 12px 14px; font-size: 13.5px; color: var(--ink-soft); line-height: 1.6; }
|
||||
.ex .bd .q { display: block; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--line-soft); }
|
||||
.ex .bd .q:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; }
|
||||
code, .icode { background: var(--code-bg); border-radius: 5px; padding: 1px 6px; font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 0.9em; }
|
||||
|
||||
.tip { margin-top: 16px; background: var(--teal-soft); border: 1px solid color-mix(in srgb, var(--teal) 28%, transparent); border-radius: 12px; padding: 14px 16px; font-size: 14px; color: var(--ink-soft); }
|
||||
.tip b { color: var(--teal); }
|
||||
.warn { margin-top: 16px; background: var(--amber-soft); border: 1px solid color-mix(in srgb, var(--amber) 32%, transparent); border-radius: 12px; padding: 14px 16px; font-size: 14px; color: var(--ink-soft); }
|
||||
.warn b { color: var(--amber); }
|
||||
|
||||
footer { margin-top: 48px; padding-top: 20px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; color: var(--ink-faint); font-size: 13px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<header class="mast">
|
||||
<div class="wordmark"><b>AWESOMEDEV</b> · 실습 가이드</div>
|
||||
<div class="eyebrow" style="margin-top:16px">Code Review</div>
|
||||
<h1>코드리뷰 실습 가이드</h1>
|
||||
<p class="lede">코드리뷰는 "누가 잘못했나"를 찾는 자리가 아니라, <b>팀의 코드를 함께 좋게 만드는</b> 자리예요.
|
||||
5~8주차 티켓 작업 내내, 여러분은 서로의 PR을 리뷰합니다. 리뷰를 <b>잘 주는 법</b>과 <b>잘 받는 법</b>을 여기서 익혀요.</p>
|
||||
<div class="mast-meta">
|
||||
<span class="chip"><span class="dot"></span>5~8주차 · 상시</span>
|
||||
<span class="chip"><span class="dot"></span>도구: Gitea Pull Request</span>
|
||||
<span class="chip"><span class="dot"></span>평가 ③ 협업·소통과 연결</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Why</div>
|
||||
<h2>왜 신입 때부터 리뷰를 배우나</h2>
|
||||
<p class="sub">실무 개발자는 코드를 짜는 시간만큼 남의 코드를 읽습니다. 리뷰는 선택이 아니라 일하는 방식이에요.</p>
|
||||
<ul class="why">
|
||||
<li><span class="n">1</span><div><b>버그는 혼자 못 잡는다.</b> 내 코드의 빈틈은 내 눈에 잘 안 보여요. 다른 눈 하나가 사고를 막습니다 — 우리 감사에서 나온 버그들도 대부분 "한 번 더 읽었으면" 잡혔을 것들이에요.</div></li>
|
||||
<li><span class="n">2</span><div><b>남의 코드를 읽으며 는다.</b> 동료가 문제를 어떻게 풀었는지 보는 게 가장 빠른 학습이에요. 리뷰는 공짜 과외입니다.</div></li>
|
||||
<li><span class="n">3</span><div><b>코드에 대한 대화가 팀을 만든다.</b> "왜 이렇게 했어?"를 감정 없이 주고받는 연습 — 이게 되는 사람이 함께 일하기 좋은 사람이에요.</div></li>
|
||||
<li><span class="n">4</span><div><b>기록이 남는다.</b> PR의 리뷰 코멘트는 여러분이 어떻게 성장했는지 보여주는 증거예요. 평가 ③ 협업·소통의 핵심 근거가 됩니다.</div></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Flow</div>
|
||||
<h2>우리 팀 리뷰 흐름</h2>
|
||||
<p class="sub">티켓 하나를 끝낼 때마다 이 순서를 돕니다. 4명이라 서로를 다 볼 수 있어요.</p>
|
||||
<div class="flow">
|
||||
<div class="step"><div><div class="t">브랜치에서 작업하고 PR을 연다</div><div class="d">티켓별 브랜치(예: <code>feat/empty-state</code>)에서 작업 → Gitea에서 Pull Request 생성. 제목·설명에 "무엇을 왜 했는지" + 스크린샷(화면 변경 시)을 적어요.</div></div></div>
|
||||
<div class="step"><div><div class="t">리뷰어를 지정한다 — 동료 1명 + 멘토</div><div class="d">동료 수습생 한 명과 멘토를 리뷰어로. 동료끼리 먼저 보는 게 핵심이에요 — 같은 눈높이의 질문이 가장 배움이 큽니다.</div></div></div>
|
||||
<div class="step"><div><div class="t">리뷰어는 24시간 안에 코멘트를 남긴다</div><div class="d">아래 체크리스트로 읽고, 줄 단위로 코멘트. 좋은 점도 꼭 하나 남겨요("여기 이렇게 나눈 거 좋네요").</div></div></div>
|
||||
<div class="step"><div><div class="t">작성자는 코멘트에 답하고 반영한다</div><div class="d">고치면 "반영했어요", 다르게 생각하면 "이래서 이렇게 했는데 어때요?"로 답. <b>침묵하고 그냥 merge하지 않기.</b></div></div></div>
|
||||
<div class="step"><div><div class="t">승인(Approve) 후 merge</div><div class="d">리뷰어가 Approve하면 작성자가 merge. 배포는 멘토와 함께(운영은 학생 사용 시간을 피해서).</div></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Reviewer</div>
|
||||
<h2>리뷰할 때 — 무엇을 볼까 (체크리스트)</h2>
|
||||
<p class="sub">위에서 아래로. 위쪽이 더 중요해요. 스타일 지적보다 "돌아가는가·안전한가"가 먼저입니다.</p>
|
||||
<div class="card mt">
|
||||
<div class="checklist">
|
||||
<div class="item"><span class="box"></span><div><b>정말 동작하나</b> — 직접 받아서 실행해 봤나? 엣지 케이스(0건, 빈 값, 아주 긴 입력)는?</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>안전한가</b> — 사용자 입력을 그대로 믿지 않나? 남의 데이터에 접근되지 않나? (우리가 배운 것들)</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>읽히나</b> — 3개월 뒤의 내가, 처음 보는 동료가 이 코드를 이해할까? 이름이 하는 일을 말해주나?</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>테스트가 있나</b> — 규칙이 있는 로직이면 테스트로 못 박았나?</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>범위가 맞나</b> — 티켓과 상관없는 "김에 수정"이 섞여 있지 않나? PR은 작아야 리뷰가 됩니다.</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>우리 패턴을 따르나</b> — 옆 코드와 같은 방식인가? (프론트 Field 부품, 백엔드 서비스 계층 등)</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tip"><b>리뷰의 황금률:</b> 사람이 아니라 코드를 짚는다. "너 왜 이렇게 했어?"(X) → "이 부분은 이러이러해서 이렇게 하면 어떨까요?"(O). 질문형으로, 근거와 함께.</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Examples</div>
|
||||
<h2>같은 지적, 다르게 쓰기</h2>
|
||||
<p class="sub">내용이 맞아도 말투가 팀을 만들거나 무너뜨려요. 왼쪽처럼 말고 오른쪽처럼.</p>
|
||||
<div class="ex">
|
||||
<div class="col bad">
|
||||
<div class="hd">🚫 이렇게 말고</div>
|
||||
<div class="bd">
|
||||
<span class="q">"이거 버그임"</span>
|
||||
<span class="q">"왜 여기서 컴포넌트를 또 만들었어요?"</span>
|
||||
<span class="q">"그냥 다 지우고 다시 하세요"</span>
|
||||
<span class="q">(코멘트 없이 Approve만 누름)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col good">
|
||||
<div class="hd">✅ 이렇게</div>
|
||||
<div class="bd">
|
||||
<span class="q">"0건일 때 <code>.map()</code>이 빈 배열이라 화면이 비어요. 빈 상태 안내를 넣으면 어떨까요?"</span>
|
||||
<span class="q">"컴포넌트를 함수 안에 정의하면 렌더마다 새로 만들어져 포커스가 풀려요(가입 폼에서 났던 그 버그!). 밖으로 빼면 어떨까요?"</span>
|
||||
<span class="q">"방향은 좋아요! 이 함수만 두 가지 일을 해서, 조회와 저장을 나누면 더 읽기 쉬울 것 같아요."</span>
|
||||
<span class="q">"여기 이름 나눈 거 깔끔하네요 👍 한 가지만: …"</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="eyebrow">Author</div>
|
||||
<h2>리뷰받을 때 — 잘 받는 것도 실력</h2>
|
||||
<p class="sub">리뷰는 내 코드에 대한 것이지 나에 대한 게 아니에요. 방어하지 말고 배우면 됩니다.</p>
|
||||
<div class="card mt">
|
||||
<div class="checklist">
|
||||
<div class="item"><span class="box"></span><div><b>고맙다고 시작한다.</b> 리뷰어는 내 시간을 아껴 준 거예요.</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>이해가 안 되면 되묻는다.</b> "이 부분 왜 그런지 조금만 더 알려줄 수 있어요?" — 몰라서 묻는 건 부끄러운 게 아니에요.</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>동의하지 않으면 근거로 답한다.</b> 감정이 아니라 "이래서 이렇게 했는데, 어떻게 생각해요?"로.</div></div>
|
||||
<div class="item"><span class="box"></span><div><b>고쳤으면 알린다.</b> 코멘트마다 "반영했어요" / "이건 이래서 그대로 뒀어요"로 마무리.</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warn"><b>하지 말 것:</b> 리뷰를 무시하고 조용히 merge / "그냥 되게만 하면 되잖아요" / 지적을 인신공격으로 받기. 리뷰를 잘 받는 사람이 가장 빨리 큽니다.</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<span>AWESOMEDEV · 코드리뷰 실습 가이드</span>
|
||||
<span>평가 ③ 협업·소통 · 5~8주차 상시</span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -103,6 +103,8 @@
|
||||
.scoreline .lbl { color: var(--ink-soft); font-weight: 600; }
|
||||
.scorebox { width: 70px; height: 34px; border: 1.5px solid var(--primary); border-radius: 8px; display: inline-block; }
|
||||
.scoreline .max { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
|
||||
.dim-note { margin-top: 12px; padding: 11px 14px; background: var(--primary-soft); border-radius: 10px; font-size: 13.5px; color: var(--ink-soft); line-height: 1.65; }
|
||||
.dim-note b { color: var(--ink); }
|
||||
@media (max-width: 680px){ .levels { grid-template-columns: 1fr 1fr; } }
|
||||
@media (max-width: 420px){ .levels { grid-template-columns: 1fr; } }
|
||||
|
||||
@ -236,12 +238,13 @@
|
||||
<div class="wt">배점 20점</div>
|
||||
</div>
|
||||
<div class="levels">
|
||||
<div class="lv lv1"><div class="lh"><span class="dot"></span>Lv1 미흡</div><p>보고·질문이 없고, 리뷰 반영이 안 된다.</p><div class="pts">~10점</div></div>
|
||||
<div class="lv lv2"><div class="lh"><span class="dot"></span>Lv2 보통</div><p>시키면 보고하나, 질문의 맥락이 부족하다.</p><div class="pts">11–14점</div></div>
|
||||
<div class="lv lv3"><div class="lh"><span class="dot"></span>Lv3 충족</div><p>중간보고를 하고, 30분 룰·3요소로 질문하며 리뷰를 반영한다.</p><div class="pts">15–17점</div></div>
|
||||
<div class="lv lv4"><div class="lh"><span class="dot"></span>Lv4 우수</div><p>먼저 공유하고 동료를 도우며, 소통이 팀에 도움이 된다.</p><div class="pts">18–20점</div></div>
|
||||
<div class="lv lv1"><div class="lh"><span class="dot"></span>Lv1 미흡</div><p>보고·질문이 없고, 받은 리뷰가 반영되지 않는다. 동료 PR도 안 본다.</p><div class="pts">~10점</div></div>
|
||||
<div class="lv lv2"><div class="lh"><span class="dot"></span>Lv2 보통</div><p>시키면 보고하나 맥락이 부족하고, 리뷰는 지적받은 것만 겨우 고친다.</p><div class="pts">11–14점</div></div>
|
||||
<div class="lv lv3"><div class="lh"><span class="dot"></span>Lv3 충족</div><p>중간보고·질문(30분 룰·3요소)을 하고, 받은 리뷰를 이해하고 반영한다. 동료 PR에 한 건이라도 성실히 리뷰한다.</p><div class="pts">15–17점</div></div>
|
||||
<div class="lv lv4"><div class="lh"><span class="dot"></span>Lv4 우수</div><p>먼저 공유하고, 동료 PR에 근거 있는 리뷰로 도움을 준다. 리뷰를 감정이 아닌 코드로 주고받는다.</p><div class="pts">18–20점</div></div>
|
||||
</div>
|
||||
<div class="scoreline"><span class="lbl">점수</span><span class="scorebox"></span><span class="max">/ 20</span></div>
|
||||
<p class="dim-note">💡 <b>코드리뷰</b>는 이 축의 핵심 근거예요 — Gitea PR의 리뷰 코멘트(주고받은 것 모두)가 5~8주차 내내 남습니다. <b>리뷰를 잘 받는 것</b>(방어하지 않고 배우기)과 <b>리뷰를 잘 주는 것</b>(사람이 아닌 코드를 짚기)을 함께 봅니다. 자세한 방법은 학습 문서 <b>「코드리뷰 실습 가이드」</b> 참고.</p>
|
||||
</div>
|
||||
|
||||
<!-- D4 -->
|
||||
|
||||
@ -15,6 +15,7 @@ const CATEGORY_ORDER = [
|
||||
['LESSON', '수업 자료'],
|
||||
['ASSIGNMENT', '과제 안내'],
|
||||
['TICKET', '실무 티켓'],
|
||||
['PRACTICE', '실습'],
|
||||
];
|
||||
|
||||
export default function DocsPage() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user