고도화 점검(다중 에이전트)에서 생존했으나 배치로 즉시 구현하지 않은 항목을 학생 실습 티켓으로 정식 등록. 기존 20개는 그대로, 새 섹션으로 추가: - OP-01/02: 컨테이너 헬스체크, push 자동 테스트 게이트(Gitea Actions CI) - SEC-01~05: 보안·권한 통합테스트(@WebMvcTest), PII 감사로그, 공개 쓰기 레이트리밋, 개인정보 접근 로그, 의존성 취약점 수동 점검 - QA-05/06: 프론트 첫 테스트(Vitest+gradeSql), 죽은 퀴즈 사본(seed/quiz 71개) 정리 - FE-08~10: 학습센터 진도율 로딩 가드, 멘토 '리뷰 대기' 카운트·정렬, 설치 가이드 진입점 - EDU-01/02: '웹 보안·인증 기초' 코스 신설, 코딩문제·캡스톤 문서 주차별 연결 각 티켓에 무엇을/찾는법/완료조건/멘토준비를 실코드 근거로 기입. 현황판 34행, 헤더 개수 표기 갱신. 배치에서 이미 구현한 4건(다운알림·로그회전·개인정보파기·퀴즈버튼)은 제외. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
634 lines
72 KiB
HTML
634 lines
72 KiB
HTML
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>수습용 실전 티켓 풀 34 · 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; --amber-line: #E4C79A;
|
|
--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; --amber-line: #4A3B23;
|
|
--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);
|
|
}
|
|
}
|
|
:root[data-theme="light"] {
|
|
--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; --amber-line: #E4C79A;
|
|
--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);
|
|
}
|
|
:root[data-theme="dark"] {
|
|
--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; --amber-line: #4A3B23;
|
|
--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.7; -webkit-font-smoothing: antialiased; letter-spacing: -0.01em; }
|
|
.wrap { max-width: 880px; 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 { font-weight: 700; }
|
|
|
|
/* criteria */
|
|
.crit { display: grid; gap: 12px; }
|
|
.crit li { list-style: none; display: grid; grid-template-columns: 26px 1fr; gap: 12px; font-size: 14.5px; color: var(--ink-soft); }
|
|
.crit .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){ .crit .n { color:#0B1017; } }
|
|
:root[data-theme="dark"] .crit .n { color:#0B1017; }
|
|
.crit b { color: var(--ink); font-weight: 700; }
|
|
.nono { border: 1px solid color-mix(in srgb,var(--rose) 30%,transparent); background: var(--rose-soft); border-radius: 10px; padding: 13px 16px; margin-top: 14px; }
|
|
.nono p { font-size: 13.5px; color: var(--ink-soft); } .nono b { color: var(--rose); }
|
|
|
|
/* category divider */
|
|
.cat-div { margin-top: clamp(36px,6vw,52px); display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: center; }
|
|
.cat-badge { width: 58px; height: 58px; border-radius: 14px; background: var(--primary); color: #fff; display: grid; place-items: center; font-size: 24px; box-shadow: var(--shadow); }
|
|
@media (prefers-color-scheme: dark){ .cat-badge { color:#0B1017; } }
|
|
:root[data-theme="dark"] .cat-badge { color:#0B1017; }
|
|
.cat-badge.be { background: var(--teal); } .cat-badge.q { background: var(--amber); } .cat-badge.dz { background: var(--rose); }
|
|
.cat-div h2 { margin: 0; font-size: clamp(21px,3.6vw,27px); font-weight: 800; letter-spacing: -0.025em; }
|
|
.cat-div .dm { margin-top: 3px; color: var(--ink-soft); font-size: 14px; }
|
|
|
|
/* ticket card */
|
|
.ticket { margin-top: 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); overflow: hidden; }
|
|
.tk-head { padding: 14px 20px; border-bottom: 1px solid var(--line-soft); background: var(--primary-soft); display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
|
.tk-head .id { font-size: 11px; font-weight: 800; letter-spacing: 0.06em; color: var(--primary); font-variant-numeric: tabular-nums; }
|
|
.tk-head h3 { margin: 3px 0 0; font-size: 16.5px; font-weight: 800; letter-spacing: -0.015em; }
|
|
.tk-meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
|
|
.diff { font-size: 12px; font-weight: 800; padding: 4px 11px; border-radius: 999px; white-space: nowrap; }
|
|
.diff.d1 { color: var(--teal); background: var(--teal-soft); }
|
|
.diff.d2 { color: var(--amber); background: var(--amber-soft); }
|
|
.dur { font-size: 12px; font-weight: 700; color: var(--ink-faint); white-space: nowrap; }
|
|
.tk-body { padding: 16px 20px; display: grid; gap: 12px; }
|
|
.tk-body .row { display: grid; grid-template-columns: 86px 1fr; gap: 12px; align-items: start; font-size: 14px; }
|
|
.tk-body .row .lab { font-size: 11px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; padding-top: 3px; }
|
|
.row.what .lab { color: var(--primary); } .row.find .lab { color: var(--teal); }
|
|
.row.dod .lab { color: var(--amber); } .row.prep .lab { color: var(--ink-faint); }
|
|
.tk-body .row .txt { color: var(--ink-soft); } .tk-body .row .txt b { color: var(--ink); font-weight: 600; }
|
|
|
|
/* form template */
|
|
.tpl { border: 1px solid var(--amber-line); background: var(--code-bg); border-radius: 10px; overflow: hidden; }
|
|
.tpl .cap { font-size: 11.5px; font-weight: 800; letter-spacing: 0.04em; color: var(--amber); background: var(--amber-soft); padding: 7px 15px; border-bottom: 1px solid var(--amber-line); text-transform: uppercase; }
|
|
.tpl pre { margin: 0; padding: 14px 16px; font-family: "SF Mono","Cascadia Code","Consolas",ui-monospace,monospace; font-size: 13px; line-height: 1.7; color: var(--ink); white-space: pre-wrap; word-break: break-word; letter-spacing: 0; }
|
|
.tpl .hl { color: var(--primary); font-weight: 600; }
|
|
|
|
/* tracker */
|
|
.tblwrap { overflow-x: auto; margin-top: 16px; border: 1px solid var(--line); border-radius: 12px; }
|
|
table { width: 100%; border-collapse: collapse; font-size: 13.5px; min-width: 560px; }
|
|
th, td { text-align: left; padding: 11px 13px; border-bottom: 1px solid var(--line-soft); }
|
|
thead th { font-size: 11.5px; letter-spacing: 0.03em; text-transform: uppercase; color: var(--ink-faint); background: var(--paper); white-space: nowrap; }
|
|
tbody tr:last-child td { border-bottom: 0; }
|
|
td .fill { display: inline-block; min-width: 70px; border-bottom: 1.5px dashed var(--line); }
|
|
td.num { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--primary); white-space: nowrap; }
|
|
|
|
footer { margin-top: 46px; padding-top: 22px; border-top: 1px solid var(--line); color: var(--ink-faint); font-size: 13px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
|
|
@media (max-width:560px){ .tk-body .row { grid-template-columns: 70px 1fr; gap: 8px; } }
|
|
@media print { body{background:#fff;} .card,header.mast,.ticket,.cat-badge{box-shadow:none;} .ticket{break-inside:avoid;} td .fill{border-color:#999;} }
|
|
|
|
.row.target { background: var(--primary-soft); border-radius: 10px; padding: 9px 11px; margin-top: 8px; }
|
|
.row.target .lab { color: var(--primary-deep); font-weight: 800; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="wrap">
|
|
|
|
<header class="mast">
|
|
<div class="wordmark"><b>AWESOMEDEV</b> · 수습 실전 티켓 풀</div>
|
|
<h1>실전 티켓 풀 34<br>템플릿 & 아이디어 카탈로그</h1>
|
|
<p class="lede">5주차부터 수습생에게 줄 실전 티켓 34개예요(기본 20 + 고도화 점검 추가 14). 각 티켓에 <strong>🎯 우리 건</strong> — 여러분이 매일 쓰는 이 학습 플랫폼(mirim-app)의 실제 코드에서 찾아 둔 구체적인 일감이 붙어 있어요. 여러분이 고치면 이 플랫폼이 정말로 좋아집니다. 파일 경로까지 적어 뒀으니, 배정받으면 바로 코드를 열어 보세요.</p>
|
|
<div class="mast-meta">
|
|
<span class="chip"><span class="dot"></span>프론트 10 · 백엔드 6 · 품질 6 · 디자인 3 · 운영 2 · 보안 5 · 교육 2</span>
|
|
<span class="chip"><span class="dot"></span>난이도 ★(반나절~1일) · ★★(2~3일)</span>
|
|
<span class="chip"><span class="dot"></span>구체화 완료 · 배정 대기</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Selection criteria -->
|
|
<section id="criteria">
|
|
<div class="eyebrow">티켓 고르는 기준</div>
|
|
<h2>수습용 티켓의 4가지 조건</h2>
|
|
<div class="card mt">
|
|
<ul class="crit" style="margin:0;padding:0;">
|
|
<li><span class="n">1</span><div><b>망가뜨려도 복구 쉬운 것.</b> 롤백 가능하고, 스테이징에서 검증 가능하고, 결제·인증·개인정보 같은 민감 영역이 아닐 것.</div></li>
|
|
<li><span class="n">2</span><div><b>범위가 한 문장으로 설명되는 것.</b> "이 화면의 이 부분을 이렇게" — 파일 3~4개 이내로 끝나는 크기. 아키텍처를 이해해야만 하는 건 아직 아님.</div></li>
|
|
<li><span class="n">3</span><div><b>완료 조건(DoD)을 명확히 쓸 수 있는 것.</b> "되면 이렇게 보인다/동작한다"를 미리 적을 수 없으면 수습용이 아님.</div></li>
|
|
<li><span class="n">4</span><div><b>실제로 가치 있는 것.</b> 연습용 가짜 일이 아니라, 머지되면 정말 제품이 나아지는 일. (이게 동기부여의 핵심이에요)</div></li>
|
|
</ul>
|
|
<div class="nono">
|
|
<p><b>주지 말 것:</b> 운영 DB 마이그레이션 · 인증/권한 로직 · 결제 · 배포 설정 · 고객사 SLA가 걸린 긴급 건 · "리팩터링 알아서" 같은 범위 무한 티켓.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- FRONTEND -->
|
|
<div class="cat-div" id="fe">
|
|
<div class="cat-badge">🖥️</div>
|
|
<div><h2>프론트엔드 (React) — 7개</h2><div class="dm">화면에서 보이는 것부터. 결과가 눈에 보여서 첫 실전에 가장 좋아요.</div></div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-01</div><h3>빈 상태(Empty State) 화면 추가</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5~1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">데이터가 없을 때 휑하게 비는 목록/테이블에 "아직 항목이 없어요 + 안내 문구/버튼"을 보여주기.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">각 화면에서 데이터 0건으로 조회해 보기. <code>.map()</code> 돌리는 목록 중 <b>length가 0일 때 분기가 없는 곳</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">0건일 때 지정된 문구·아이콘 노출, 1건 이상이면 기존과 동일, 스크린샷 첨부된 PR.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대상 화면 지정, 문구 확정(디자이너 수습생과 협업시키면 좋음).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>코딩 문제 필터 결과 0건 빈 상태</b> — <b>FE-04(필터)와 짝 티켓.</b> 코딩 문제 목록 <code>frontend/src/pages/CodingPage.jsx</code>에 FE-04로 언어·난이도 필터가 생기면 "조건에 맞는 문제 0건"이라는 새 상태가 태어난다. 0건일 때 "조건에 맞는 문제가 없어요" 안내 + [필터 초기화] 버튼을 보여줄 것. FE-04 담당자와 둘이 짝으로 진행 — 남의 코드 위에 얹는 연습.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-02</div><h3>로딩 표시(스피너/스켈레톤) 추가</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5~1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">API 응답을 기다리는 동안 아무것도 안 보이는 화면에 로딩 상태 표시 넣기.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">네트워크를 느리게(개발자도구 throttling) 하고 화면 이동 — <b>흰 화면이나 깜빡임이 보이는 곳</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">로딩 중 표시 노출, 로드 완료 시 자연스럽게 교체, 에러 시 로딩이 무한히 남지 않음.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">기존 공용 로딩 컴포넌트가 있으면 알려주기(새로 만들지 재사용할지).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>공용 로딩 컴포넌트(스켈레톤) 도입</b> — "불러오는 중…" 텍스트가 <b>9곳에 하드코딩</b>되어 있다(<code>grep -rn "불러오는 중" frontend/src</code>로 직접 세어 보기). <code>components/Loading.jsx</code>를 만들어 카드 모양 스켈레톤으로 통일하고, 우선 대시보드(<code>DashboardPage.jsx</code>)와 학습 문서(<code>DocsPage.jsx</code>) 2곳만 교체. 나머지는 후속 티켓.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-03</div><h3>폼 입력 유효성 검사 보강</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">빈 값·형식 오류를 제출 후에야 알려주는 폼에, 입력 시점 안내 메시지 추가(예: 이메일 형식, 필수값).</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">각 폼에 일부러 빈 값/이상한 값 넣고 제출해 보기 — <b>서버 에러로만 알게 되는 폼</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">정의된 규칙별 에러 문구 노출, 통과 시 정상 제출, 에러 문구는 한국어로 친절하게.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">검사 규칙 목록 확정(무엇을 필수로, 어떤 형식으로).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>비밀번호 변경: 새 비번 = 현재 비번 차단</b> — <code>frontend/src/pages/ChangePasswordPage.jsx</code> — 지금은 새 비밀번호를 현재 것과 똑같이 넣어도 통과된다(바꾼 게 아닌데 바꿨다고 안내됨). 프론트 validate에 "현재와 다른 비밀번호를 써 주세요" 추가 + <b>백엔드 <code>AuthService.changePassword</code>에도 같은 검사</b>(프론트는 우회 가능 — 서버가 최종 방어선이라는 걸 몸으로 익히는 티켓).</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-04</div><h3>목록 정렬/필터 옵션 추가</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">있는 목록 화면에 정렬(최신순/이름순) 또는 필터(상태별) 드롭다운 1개 추가.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">운영/관리 화면 중 <b>항목이 20개 이상 쌓이는 목록</b>에서 사용자가 스크롤로만 찾는 곳.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">옵션 변경 시 목록 갱신, 새로고침해도 기본값 정상, 기존 기능 회귀 없음.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">정렬/필터 기준 확정, 서버 쿼리 지원 여부 확인(클라이언트 정렬로 충분한지).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>코딩 문제 목록에 언어·난이도 필터</b> — <code>frontend/src/pages/CodingPage.jsx</code> — 문제 25개(JS 12·SQL 5·Java 8)가 한 줄로 길게만 나온다. 언어(전체/JS/SQL/Java)와 난이도(쉬움/보통/어려움) 필터 칩을 추가. <code>useState</code> 2개와 <code>.filter()</code>면 충분 — 새 API 불필요. 퀴즈 페이지의 탭 UI(<code>tabs</code> 클래스)를 참고. 완료되면 FE-01(0건 빈 상태)로 이어진다.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-05</div><h3>에러 화면/토스트 메시지 개선</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5~1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">"Error" 또는 영문 원문이 그대로 뜨는 실패 메시지를, 사용자가 이해할 한국어 안내로 교체.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">서버 꺼두고/네트워크 끊고 주요 동작 실행 — <b>알 수 없는 문구가 뜨는 곳</b> 수집.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">실패 상황별 지정 문구 노출, 콘솔 에러는 유지(디버깅용), 문구 목록 문서화.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">상황별 문구 톤 가이드 한 줄(사과·원인·해결 순).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>세션 만료 안내 메시지</b> — <code>frontend/src/api/client.js</code>의 401 인터셉터가 <code>/login</code>으로 보낼 때 아무 설명이 없다 — 학생 입장에선 "쓰다가 갑자기 로그인 화면"이다. 인터셉터에서 <code>/login?expired=1</code>로 보내고, <code>LoginPage.jsx</code>가 쿼리스트링(<code>useSearchParams</code>)을 읽어 "세션이 만료됐어요. 다시 로그인해 주세요"를 폼 위에 표시.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-06</div><h3>모바일 화면 깨짐 수정</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">1~2일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">좁은 화면에서 넘치거나 겹치는 요소(테이블·버튼 줄바꿈 등) 1~2군데 반응형 수정.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">개발자도구 모바일 뷰(375px)로 주요 화면 전부 훑기 — <b>가로 스크롤 생기는 곳</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">375px·768px·데스크톱 3구간 스크린샷 첨부, 기존 데스크톱 레이아웃 변화 없음.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">깨지는 화면 목록 미리 수집(학생이 찾는 것부터 시키면 반나절 추가).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>상단 메뉴 가로 스크롤 힌트</b> — 모바일에서 상단 메뉴(<code>global.css</code>의 <code>.topnav-menu</code>)가 "코딩 문…"처럼 잘리는데, 스크롤바까지 숨겨져 있어(<code>::-webkit-scrollbar</code>) 더 있다는 표시가 전혀 없다. 오른쪽 가장자리 그라데이션 페이드(<code>mask-image</code> 또는 <code>::after</code>)로 "더 있음"을 암시할 것. CSS만으로 해결 — JS 불필요.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-07</div><h3>공통 컴포넌트로 중복 UI 정리</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">거의 똑같은 버튼/카드/모달이 여러 파일에 복붙돼 있는 것을 컴포넌트 1개로 추출해 교체.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">비슷한 JSX 덩어리를 검색(클래스명·문구로 grep) — <b>3곳 이상 반복되는 블록</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">추출한 컴포넌트 1개 + 기존 사용처 전부 교체, 화면 픽셀 변화 없음(전후 스크린샷).</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대상 지정 필수(범위 폭주 방지). props 설계는 착수 계획서에서 함께 리뷰.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>페이지 히어로 공통 컴포넌트 추출</b> — <code>hero compact</code> 블록(eyebrow + 제목 + 설명)이 <b>7개 페이지에 복붙</b>되어 있다. <code>components/PageHero.jsx</code>로 추출(<code>eyebrow·title·children</code> props 설계)하고 2~3개 페이지부터 적용. "언제 컴포넌트로 뽑는가(3번 반복되면)"와 props 설계를 연습하는 티켓.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- BACKEND -->
|
|
<div class="cat-div" id="be">
|
|
<div class="cat-badge be">⚙️</div>
|
|
<div><h2>백엔드 (Spring Boot) — 6개</h2><div class="dm">서버 쪽 안전 지대. 응답·검증·로그처럼 되돌리기 쉬운 것 위주.</div></div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-01</div><h3>API 응답에 필드 1개 추가</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">화면에서 필요해진 값(예: 등록일, 개수, 상태명)을 기존 조회 API 응답에 추가하고 프론트 반영까지.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">프론트에서 <b>두 번 요청해서 조합하거나, 하드코딩으로 때운 값</b>이 있는 화면의 API가 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">응답 DTO에 필드 추가, 기존 호출부 회귀 없음, 화면에 값 표시, API 문서/주석 갱신.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">추가할 필드와 출처(어느 테이블/계산) 지정.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>코딩 문제 목록에 시도 횟수(attempts) 내려주기</b> — <code>CodingController</code>의 <code>ProblemSummary</code> record에는 <code>attempts</code>가 없다(상세 <code>ProblemDetail</code>에는 있음). 목록 응답에 추가하고, 프론트 <code>CodingPage.jsx</code> 목록에 "3번째 도전 중" 배지 표시. record 필드 추가 → 서비스 → 화면까지 <b>세로로 관통</b>하는 첫 티켓으로 적당.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-02</div><h3>요청 검증(Validation) 보강</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">이상한 값(음수, 빈 문자열, 초과 길이)이 들어와도 500으로 터지는 API에 검증 어노테이션과 400 응답 추가.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">등록/수정 API에 일부러 이상한 값 보내 보기 — <b>500이 뜨는 곳</b>이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">정의된 규칙 위반 시 400 + 명확한 메시지, 정상 값은 기존대로, 테스트 1개 추가.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">검증 규칙 표(필드·규칙·에러 메시지) 초안.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>코딩 제출 코드 길이 제한</b> — <code>CodingController.SubmitRequest</code>의 <code>code</code> 필드에 길이 제한이 없다 — 수 MB를 보내도 DB가 그대로 받는다. <code>@Size(max = 20000)</code>(약 500줄) + 컨트롤러 <code>@Valid</code> 확인 + 프론트 초과 시 안내. "받을 수 있는 것"과 "받아도 되는 것"은 다르다는 검증 원칙 티켓.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-03</div><h3>에러 로그 메시지 개선</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5~1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">"error occurred" 수준의 로그에 무엇이/어떤 값으로/왜 실패했는지 문맥 추가 (단, 개인정보는 제외).</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">최근 장애/문의 때 <b>로그 보고도 원인을 몰랐던 지점</b>을 선임에게 물어 수집.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">지정 지점 로그에 식별자·원인 포함, 개인정보(전화번호 등) 마스킹 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">개인정보 로깅 금지 규칙 브리핑(이 티켓이 보안 교육 기회).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>검증 실패(400) 응답 메시지 한국어 통일</b> — <code>@Valid</code> 실패 시 응답 message가 "Validation failed for object='loginRequest'…" 영어 원문이다(기본기 감사에서 발견). <code>@RestControllerAdvice</code> + <code>MethodArgumentNotValidException</code> 핸들러를 만들어 <b>첫 번째 필드 에러의 한국어 메시지</b>(DTO에 이미 다 적어 둠)를 <code>{"message": "..."}</code>로 내려주기. 전역 예외 처리 입문 티켓.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-04</div><h3>조회 API 신규 작성 (단순 CRUD의 R)</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">기존 테이블에서 조건으로 조회하는 GET API 1개를 컨트롤러→서비스→리포지토리 계층대로 신규 작성.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">관리/통계 화면에서 <b>"이 목록도 보고 싶다"고 미뤄둔 요구</b>가 후보 (신규 화면과 세트로 주면 FE 수습생과 협업 티켓이 됨).</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">API 동작(페이징 포함 여부 명시), 계층 구조 준수, 테스트 1개, API 명세 공유.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대상 테이블·조건·응답 형태 스펙 한 장. 참고할 기존 유사 API 지정.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>학생 한 명의 과제 제출 모아보기 API</b> — 멘토 화면(<code>MentorController</code>)에는 전체 제출 최신순(<code>GET /api/mentor/submissions</code>)만 있고 <b>학생별로 모아보기가 없다</b>. <code>GET /api/mentor/students/{userId}/submissions</code> 신규 작성(단순 R) + <code>MentorPage.jsx</code>에서 학생 이름 클릭 시 필터. <code>hasRole("MENTOR")</code> 권한이 어떻게 걸리는지(SecurityConfig) 함께 확인.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-05</div><h3>매직 넘버/하드코딩 값 설정으로 추출</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">코드에 박힌 숫자·문자열(페이지 크기, 제한 횟수, 안내 문구)을 상수/설정 파일로 추출.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">서비스 코드에서 <b>의미를 알 수 없는 숫자 리터럴</b> grep — 선임이 "이건 바뀔 수 있는 값"이라고 확인해준 것만.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">동작 변화 없음(값 동일), 상수 이름이 의미를 설명, 어디서 왜 쓰는지 주석.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">추출 대상 목록 지정(전체 수색은 범위 폭주).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>로그인 잠금 정책을 설정값으로 추출</b> — <code>LoginAttemptService</code>의 5회/5분이 <code>static final</code> 상수로 박혀 있다. <code>security.login-lock.max-failures</code>·<code>lock-minutes</code>를 <code>application.yml</code>에 <code>${ENV:기본값}</code> 패턴으로 추출(<code>@Value</code> 주입) — 컨테이너 재빌드 없이 정책을 조정할 수 있게. 기존 테스트(<code>LoginAttemptServiceTest</code>)가 깨지지 않게 유지하는 것까지가 완료 조건.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">BE-06</div><h3>느린 조회 1건 개선 (N+1 / 불필요 조회 제거)</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">목록 조회 시 항목마다 추가 쿼리가 나가는 지점 1개를 한 번에 가져오도록 개선.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">쿼리 로그 켜고 목록 화면 열기 — <b>같은 모양 쿼리가 수십 번 반복</b>되는 곳이 후보.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">쿼리 수 전/후 비교 기록(예: 51→2), 결과 데이터 동일함 검증, 개선 설명 PR에 작성.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대상 지점 확정 필수. JPA 페치 전략을 함께 리뷰(혼자 두면 위험한 티켓).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>N+1 지점 측정하고 개선하기</b> — <code>MentorController</code>·<code>CodingService</code> 주석에 "학생 4~5명이라 괜찮다(N+1)"고 적어 둔 지점이 있다(<code>grep -rn "N+1" backend/src</code>). SQL 로그를 켜고(<code>logging.level...SQL: DEBUG</code>) 멘토 화면을 열어 <b>쿼리가 몇 번 나가는지 직접 센 뒤</b>, 학생 100명 가정으로 IN 절/JOIN FETCH로 줄이기. 측정→개선→재측정 수치를 PR에 첨부.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- QUALITY -->
|
|
<div class="cat-div" id="q">
|
|
<div class="cat-badge q">🧪</div>
|
|
<div><h2>품질 · 테스트 · 문서 — 4개</h2><div class="dm">티가 덜 나지만 팀이 정말 고마워하는 일. 코드 이해 훈련으로도 최고예요.</div></div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-01</div><h3>버그 재현 → 수정 → 회귀 테스트</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">알려진 경미한 버그 1개를 재현 절차 문서화 → 원인 찾아 수정 → 재발 방지 테스트 추가.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">이슈 트래커/메모에 쌓인 <b>"급하지 않은 버그"</b> 목록에서 데이터 파손 위험 없는 것.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">재현 절차 문서, 수정 PR, 실패했다가 통과하는 테스트 1개.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">버그 후보 2~3개 골라두기(1개는 막힐 경우의 교체용).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>잠재 버그: 문서 뷰어 notFound 상태 미리셋</b> — <code>frontend/src/pages/DocViewerPage.jsx</code> — <code>notFound</code>가 한 번 true가 되면 되돌리는 코드가 없다. 지금은 문서 간 직행 링크가 없어 안 터질 뿐(전수 감사에서 "결함 실재, 현재 도달 불가"로 판정된 건). <b>재현 경로를 직접 구성</b>(없는 slug 접속 → 정상 문서로 이동)해 버그를 드러내고, slug 변경 시 상태 리셋으로 수정. "도달 불가한 결함은 언제 고치는가"를 토론해 보는 티켓.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-02</div><h3>핵심 함수에 단위 테스트 추가</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1~2일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">테스트가 없는 계산/변환 함수(금액 포맷, 날짜 계산, 상태 판정 등)에 정상·경계·예외 케이스 테스트 작성.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt"><b>if 분기가 3개 이상인데 테스트 0개인 함수</b>를 서비스 코드에서 찾기.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">케이스 5개 이상(경계값 포함), 전부 통과, 테스트 이름만 봐도 뭘 검증하는지 알 것.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대상 함수 지정 + 기존 테스트 파일 1개를 본보기로 알려주기.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>SubmissionService·CourseProgressService 단위 테스트</b> — 이 두 서비스만 테스트가 0개다(<code>backend/src/test/.../service/</code> 목록과 대조). <code>ProgressServiceTest</code> 패턴(Mockito + 한국어 <code>@DisplayName</code>) 그대로 — 재제출 시 상태가 SUBMITTED로 돌아오는지, 없는 과제 404, 빈 내용 400 등 <b>분기마다 1개</b>. 전체 테스트가 25개+로 늘어나는 걸 확인.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-03</div><h3>README / 로컬 실행 가이드 현행화</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">본인이 1주차에 환경 세팅하며 막혔던 지점을 살려, 실행 가이드를 "처음 온 사람도 따라 하면 되는" 상태로 갱신.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">본인의 <b>1주차 세팅 메모</b>가 그대로 재료. (이 티켓은 사실상 준비돼 있음)</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">문서만 보고 다른 수습생이 클린 세팅 성공(상호 검증), 시크릿 값은 문서에 안 씀.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">문서 위치 지정, 시크릿 기재 금지 재강조.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>README 현행화 — 따라 하면 실행되게</b> — <code>README.md</code>에 텔레그램 알림·로그인 잠금·내 정보·주소찾기 등 최근 기능이 하나도 없다(grep 0건). <b>README만 보고 처음부터 로컬 실행을 따라 해 보고</b>, 막히는 지점(환경변수·포트·시드 계정)을 전부 고쳐 쓰기. "문서는 새 팀원이 검증한다" — 여러분이 그 새 팀원이다.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-04</div><h3>미사용 코드/주석 정리 (지정 범위)</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5~1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">멘토가 지정한 폴더 안에서 주석 처리된 죽은 코드, 안 쓰는 import, 미사용 함수 제거.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">에디터/린트의 <b>unused 경고</b> + 주석 블록 검색. 삭제 전 "정말 안 쓰나" 확인 방법을 배우는 게 포인트.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">빌드·테스트 통과, 삭제 목록을 PR 설명에 정리, 확신 없는 건 삭제 대신 질문.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">범위 폴더 지정(전체 수색 금지), 리뷰 꼼꼼히.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>미사용 import·변수 정리 (frontend/src/pages 한정)</b> — <code>npm run lint</code> 결과를 기준으로 <code>frontend/src/pages</code> 디렉터리의 미사용 import·변수만 정리. <b>범위를 벗어난 "김에 수정"은 금지</b> — 정리 PR은 작아야 리뷰가 된다는 걸 배우는 티켓. lint 경고 개수 before/after를 PR 설명에 기록.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DESIGN -->
|
|
<div class="cat-div" id="dz">
|
|
<div class="cat-badge dz">🎨</div>
|
|
<div><h2>디자이너 티켓 — 3개</h2><div class="dm">디자인 수습생용. 개발 수습생의 FE 티켓과 짝지으면 협업 훈련이 됩니다.</div></div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">DZ-01</div><h3>빈 상태·에러 상태 디자인 세트</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1~2일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">FE-01·FE-05와 짝: 빈 상태/에러 상태의 일러스트·문구·레이아웃을 디자인하고 개발자에게 핸드오프.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">시안 + 문구 + 간격/색 명세, 개발 구현 결과가 시안과 일치하는지 확인까지.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">기존 디자인 톤 참고 자료 전달.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>빈 상태·오류 화면 디자인 세트</b> — FE-01·FE-05와 짝. 플랫폼 공통 상태 3종(데이터 없음 / 검색 결과 없음 / 오류) 일러스트+문구 톤을 피그마로 설계. 현재 화면들은 회색 텍스트 한 줄뿐이다. 개발 수습생과 협업해 <b>1종은 실제 반영까지</b> — 디자인이 코드가 되는 전 과정을 경험.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">DZ-02</div><h3>화면 1개 UX 개선 제안 → 반영</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">2주차 UI 인벤토리에서 찾은 불편 지점 1개를 개선 시안으로 만들고, 팀 리뷰 통과분을 개발 티켓으로 넘기기.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">현행 문제 정의 → 개선 시안(2안) → 선택안 핸드오프 문서 → 구현 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">개선 후보 화면 2~3개 승인해 두기(고객사 승인 필요 화면 제외).</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>퀴즈 결과 화면 UX 관찰·개선 제안</b> — 퀴즈 결과 화면(<code>QuizPage.jsx</code> 채점 후) — 틀린 문제를 다시 보는 흐름을 <b>동료 2명이 쓰는 걸 관찰</b>하고(어디서 머뭇거리나), 개선안 1장(before/after 스케치)을 멘토에게 제안 → 승인되면 개발 티켓으로 전환. "관찰 → 제안 → 반영"의 UX 사이클 한 바퀴.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">DZ-03</div><h3>UI 컴포넌트 정리표 (미니 디자인 시스템)</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">서비스에서 쓰이는 버튼·입력·카드·색·글자 크기를 한 장으로 정리한 "컴포넌트 정리표" 제작 — FE-07(공통 컴포넌트화)의 근거 자료가 됨.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">컴포넌트별 변형 수 집계, 통일 제안 포함, 팀 공유 발표 10분.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">범위(화면 몇 개까지) 지정.</span></div>
|
|
<div class="row target"><span class="lab">🎯 우리 건</span><span class="txt"><b>미니 디자인 시스템 정리표</b> — <code>global.css</code>의 버튼(btn·btn-primary·btn-ghost)·배지(badge-*)·칩·카드 변형을 화면 캡처로 전수 수집해 피그마 정리표로. 색·간격·글자 크기 토큰까지 뽑으면 <b>이 플랫폼의 미니 디자인 시스템 v0.1</b>이 된다. DZ-01·FE-07과 자연스럽게 연결.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 고도화 점검 추가 티켓 (2026-07-17) -->
|
|
<div class="cat-div">
|
|
<div class="cat-badge be">🔬</div>
|
|
<div><h2>고도화 점검에서 나온 추가 티켓 (14건)</h2><div class="dm">2026-07-17 다중 에이전트 점검으로 발굴 · 운영·보안·테스트·UX·교육 전반. 위 20개와 같은 방식으로 배정하세요.</div></div>
|
|
</div>
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">OP-01</div><h3>컨테이너 헬스체크 + 의존성 조건</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">backend·caddy 컨테이너에 HEALTHCHECK를 넣어 "죽지는 않았지만 응답을 멈춘(hang)" 상태를 감지한다. caddy의 <code>depends_on</code>을 <code>condition: service_healthy</code>로 올린다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">docker-compose.prod.yml에 postgres만 healthcheck가 있음. backend엔 무인증 200 GET이 없으니 actuator 대신 wget/nc로 8080 응답 존재만 확인하는 가벼운 형태로.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">backend·caddy에 healthcheck 추가, <code>docker ps</code>에 (healthy) 표시, 일부러 hang 상황을 만들어 재기동되는지 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">이미 있는 헬스체크 알림(deploy/healthcheck.sh)과 역할이 다름을 설명 — 이건 컨테이너 자동복구, 그건 사람 알림.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">OP-02</div><h3>push 시 자동 테스트 게이트 (Gitea Actions)</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">Gitea Actions 러너를 EC2에 등록하고 <code>.gitea/workflows/test.yml</code>로 push마다 <code>mvn -q test</code>(+ 가능하면 vitest)를 돌려, 회귀를 배포 전에 잡는 초록/빨강 체크를 만든다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt"><code>.gitea/workflows</code>·<code>.github/workflows</code>가 전무 — 지금은 사람이 수동으로 mvn test. "CI가 뭘 하는지"를 직접 세워 보는 게 목표.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">러너 등록, 워크플로 작성, 일부러 실패하는 테스트를 push해 빨간 체크가 뜨는 것까지 확인(그다음 고쳐 초록).</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">CI는 교육용으로 일부러 안 만든 항목 — 러너 운영 부담을 설명하고 범위를 test만으로 한정.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">SEC-01</div><h3>보안·권한 계약 통합테스트 (@WebMvcTest)</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">SecurityConfig의 URL→권한 규칙을 검증하는 통합테스트를 작성한다: (1) 비로그인 <code>/api/mentor/students</code>→401, (2) STUDENT 세션→403, (3) MENTOR→통과, (4) <code>/api/auth/login·signup</code>→permitAll 도달.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">backend/src/test는 전부 서비스 단위테스트라 컨트롤러/보안 계약 테스트가 0건. 권한은 SecurityConfig 한 곳에만 있고 검증이 없다.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt"><code>@WebMvcTest</code> + <code>spring-security-test</code>로 위 5가지 단언, 전부 초록.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">spring-security-test 의존성 추가, @MockBean으로 서비스 대체하는 패턴 안내.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">SEC-02</div><h3>민감 PII 작업 감사로그</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">전체 인적사항 CSV 다운로드·멘토 비번 재설정·명단 조회에 "행위자(멘토)+대상 학생 id+시각"을 <code>log.info</code>로 남긴다. 세션 Authentication에서 행위자를 얻는다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">백엔드 log.* 호출은 LoginAttemptService·TelegramNotifier뿐. mentor/auth/profile 경로엔 감사로그가 전무 — 누가 전체 명단을 받았는지 추적 불가.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">위 세 경로에 감사로그 추가, 실제 호출 후 로그에 행위자·대상·시각이 남는지 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">미성년 PII라 추적이 왜 중요한지 설명. TelegramNotifier로 "CSV 전체 다운로드됨" 알림을 함께 보내도 좋음.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">SEC-03</div><h3>공개 쓰기 엔드포인트 레이트리밋</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">이미 있는 LoginAttemptService 패턴(IP+창시간)을 재사용해 <code>POST /api/auth/signup</code>과 <code>PUT /api/profile/me</code>에 분당 상한을 건다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">signup은 무인증으로 DB에 계정을 쓰고 성공마다 텔레그램 알림까지 쏜다 — 남용 시 가짜계정·알림 폭주 표면.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">짧은 시간에 반복 요청 시 429가 나는지 확인, 정상 사용은 영향 없음.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">로그인 잠금(LoginAttemptService) 코드를 먼저 읽게 하고, 재사용/일반화하는 방향으로 안내.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">SEC-04</div><h3>개인정보 열람·내보내기 접근 로그</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt"><code>/api/mentor/students</code>(전체 인적사항)와 <code>/export.csv</code>에 접근 한 줄(사용자·행위·시각) 로그를 남긴다. SEC-02와 짝.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">두 경로 모두 누가·언제 접근했는지 기록이 0. 유출 시 추적 근거가 없다.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">두 경로 접근 시 로그 한 줄이 남는지 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">"접근 감사" 개념 설명. 소규모라 법적 강제성은 낮지만 습관으로 배우는 주제.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">SEC-05</div><h3>의존성 취약점 손으로 점검해 보기</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">backend는 <code>mvn versions:display-dependency-updates</code>(또는 OWASP dependency-check) 1회, frontend는 <code>npm audit</code>를 돌려 결과를 읽고 STUDY.md에 정리한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">지금 버전은 대체로 최신이라 급한 건 없다 — "취약점이 어떻게 딸려 오는지"와 audit 읽는 법을 배우는 게 목적.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">audit 결과 요약 + "무엇을 언제 올려야 하나" 판단 한 단락을 STUDY.md에 추가.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">CI 자동화가 아니라 "손으로 한 번 돌려 읽기"로 범위 한정.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-05</div><h3>프론트 첫 테스트 — 채점 함수 gradeSql 검증</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">Vitest를 devDependency로 추가하고 test 스크립트 1줄 + 최소 설정 후, <code>frontend/src/lib/runSql.js</code>의 순수함수 <code>gradeSql</code> 단위테스트를 작성한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">frontend에 테스트 러너가 아예 없다. gradeSql은 SQL 통과/실패를 결정하는데, 서버는 그 판정을 그대로 믿고 저장한다 — 조용히 깨지면 진도가 잘못 기록된다.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">행 개수 불일치→false, orderMatters=false 순서무시 동등, true일 때 순서 다르면 오답, 값만 비교 — 4케이스 초록.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">runJs/runSql은 Worker·WASM 의존이라 이번 범위 밖(후속 티켓)임을 명시.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">QA-06</div><h3>죽은 퀴즈 사본 정리 (seed/quiz 71개)</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt"><code>seed/quiz/</code> 폴더(코스별 71개 파일)를 제거하거나, 최소한 "실사용은 quizzes.json"이라는 안내를 남겨 단일 출처로 정리한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">실제 적재는 <code>SeedDataLoader</code>가 <code>seed/quizzes.json</code>(단일 355문항)만 읽는다. quiz/ 폴더는 어디서도 참조 안 됨(grep 0건) — 고쳐도 앱에 반영 안 되는 함정.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">quiz/ 삭제(또는 명확한 주석/README), quizzes.json이 단일 출처임을 확인.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">"편집하기 좋아 보이는 죽은 파일"이 왜 위험한지 — 교재로서 가장 나쁜 함정임을 설명.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-08</div><h3>학습센터 진도율 로딩 가드 (0% 깜빡임)</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt"><code>LearnHubPage</code>에 loading 가드를 추가해, fetch 도착 전 순간적으로 "0% · 모든 코스 미완료"가 보였다 갱신되는 깜빡임을 없앤다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">completedSlugs를 빈 Set으로 두고 즉시 렌더. 대시보드 위젯·CourseShell은 로딩 처리가 있는데 여기만 빠짐(감사에서 지적된 일관성의 남은 사례).</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">느린 네트워크(throttling)에서 0% 깜빡임 없이 로딩→실제 진도로 자연스럽게 전환.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">대시보드 위젯의 로딩 처리를 참고 예시로 알려주기.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-09</div><h3>멘토 '리뷰 대기' 한눈에 보기</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">1일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">멘토 화면 상단에 '리뷰 대기 N건' 카운트와 '미검토(SUBMITTED) 우선' 정렬/필터 토글을 추가한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">/mentor/submissions·/mentor/coding-submissions가 최신순 평면 목록이라, 미검토 건을 상태 배지로 눈으로 스캔해야 함.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">미검토 건수 표시, 토글 시 미검토가 위로 정렬, 8주 누적돼도 놓치지 않음.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">개별 타임라인/일괄 피드백은 4명 규모엔 과잉 — 카운트·정렬만으로 충분함을 안내.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">FE-10</div><h3>'개발 환경 설치 가이드' 진입점 만들기</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">대시보드에 '처음이라면: 개발 환경 설치 가이드부터' 배너나 nav의 눈에 띄는 링크 하나를 추가한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">설치 가이드가 학습센터 마지막 카테고리 "부록·도구"의 첫 항목으로만 있고 nav·대시보드에 링크가 없다. 개발 3명이 첫날 스스로 못 찾으면 멘토가 병목.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">첫 로그인 학생이 대시보드/nav에서 설치 가이드로 한 번에 도달.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">온보딩 첫 관문임을 강조. 링크 하나로 해결되는 저비용 개선.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">EDU-01</div><h3>'웹 보안·인증 기초' 코스 신설</h3></div><div class="tk-meta"><span class="diff d2">★★</span><span class="dur">2~3일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">학습센터에 웹/애플리케이션 보안(비밀번호 해싱·세션/쿠키·무차별대입·인가) 코스 1개를 신설한다. 이 앱에 이미 있는 구현(BCrypt·changeSessionId·로그인 잠금·쿠키 플래그·서버측 정답 보관)을 읽고 개념화한다.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">71개 코스에 네트워크 보안(wifi-security·firewall)은 있으나 웹 보안이 없다. 마침 이 저장소가 그 주제의 살아있는 예제다.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">courseCatalog에 코스 등록 + 코스 페이지(courses/) 작성 + 확인 퀴즈, 실제 코드 참조 포함.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">새 이론이 아니라 "있는 코드를 개념으로 잇기"임을 강조. 후반부 실습 티켓감.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ticket">
|
|
<div class="tk-head"><div><div class="id">EDU-02</div><h3>코딩 문제·캡스톤 문서를 주차별 교육안에 연결</h3></div><div class="tk-meta"><span class="diff d1">★</span><span class="dur">0.5일</span></div></div>
|
|
<div class="tk-body">
|
|
<div class="row what"><span class="lab">무엇을</span><span class="txt">주차별 문서에 교차참조 추가: (1) 관련 주차(3~4주 자립코딩, 5~6주 티켓)에 '학습센터→코딩 문제로 손 풀기' 링크, (2) week7·8 강의안에서 capstone-demoday 문서 링크.</span></div>
|
|
<div class="row find"><span class="lab">찾는 법</span><span class="txt">코딩 문제 25개와 capstone-demoday 문서가 주차별 교육안·수습 가이드 어디에도 언급 없음 — 좋은 자료가 사장될 위험.</span></div>
|
|
<div class="row dod"><span class="lab">완료 조건</span><span class="txt">해당 주차 문서에 링크·안내 문장 추가, 1~4주와 5~8주를 잇는 동선이 문서상 연결됨.</span></div>
|
|
<div class="row prep"><span class="lab">멘토 준비</span><span class="txt">멘토가 "언제 무엇을 시킬지" 안내가 목적임을 설명.</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ticket form -->
|
|
<section id="form">
|
|
<div class="eyebrow">구체화 양식</div>
|
|
<h2>티켓 작성 양식 — 이 틀로 실제 티켓을 만드세요</h2>
|
|
<p class="sub">위 패턴에서 찾은 실제 건을 이 양식으로 옮기면 수습생에게 바로 줄 수 있는 티켓이 됩니다. 이슈 트래커에 그대로 복붙해도 돼요.</p>
|
|
<div class="card mt">
|
|
<div class="tpl">
|
|
<div class="cap">수습 티켓 양식 (복사해서 사용)</div>
|
|
<pre><span class="hl">[제목]</span> (패턴ID) 화면/기능 — 한 줄 요약
|
|
예: (FE-01) 알림 목록 — 빈 상태 화면 추가
|
|
|
|
<span class="hl">[배경]</span> 왜 필요한가 (1~2문장)
|
|
<span class="hl">[할 일]</span> 무엇을 어떻게 (구체적으로)
|
|
<span class="hl">[범위]</span> 건드릴 파일/영역 · 건드리지 말 것
|
|
<span class="hl">[완료 조건]</span>
|
|
- [ ] 조건 1 (눈으로 확인 가능하게)
|
|
- [ ] 조건 2
|
|
- [ ] PR에 전/후 스크린샷 또는 테스트
|
|
<span class="hl">[참고]</span> 비슷한 기존 코드 위치, 참고 문서
|
|
<span class="hl">[난이도/예상]</span> ★ or ★★ / 예상 소요
|
|
<span class="hl">[멘토]</span> 담당 멘토 이름</pre>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Tracker -->
|
|
<section id="tracker">
|
|
<div class="eyebrow">배정 트래커</div>
|
|
<h2>티켓 풀 현황판</h2>
|
|
<p class="sub">✅ 2026-07-17 구체화 완료 — 20건 모두 이 학습 플랫폼(mirim-app) 실코드 기준으로 검증했습니다. 이제 5~6주차에 배정만 하면 됩니다. 학생 1명당 5~6주 동안 2~4건이 적당해요.</p>
|
|
<div class="tblwrap">
|
|
<table>
|
|
<thead><tr><th>패턴</th><th>실제 티켓 제목</th><th>구체화</th><th>배정 대상</th><th>배정일</th><th>상태</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="num">FE-01</td><td>코딩 문제 필터 결과 0건 빈 상태</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-02</td><td>공용 로딩 컴포넌트(스켈레톤) 도입</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-03</td><td>비밀번호 변경: 새 비번 = 현재 비번 차단</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-04</td><td>코딩 문제 목록에 언어·난이도 필터</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-05</td><td>세션 만료 안내 메시지</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-06</td><td>상단 메뉴 가로 스크롤 힌트</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-07</td><td>페이지 히어로 공통 컴포넌트 추출</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-01</td><td>코딩 문제 목록에 시도 횟수(attempts) 내려주기</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-02</td><td>코딩 제출 코드 길이 제한</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-03</td><td>검증 실패(400) 응답 메시지 한국어 통일</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-04</td><td>학생 한 명의 과제 제출 모아보기 API</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-05</td><td>로그인 잠금 정책을 설정값으로 추출</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">BE-06</td><td>N+1 지점 측정하고 개선하기</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-01</td><td>잠재 버그: 문서 뷰어 notFound 상태 미리셋</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-02</td><td>SubmissionService·CourseProgressService 단위 테스트</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-03</td><td>README 현행화 — 따라 하면 실행되게</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-04</td><td>미사용 import·변수 정리 (frontend/src/pages 한정)</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">DZ-01</td><td>빈 상태·오류 화면 디자인 세트</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">DZ-02</td><td>퀴즈 결과 화면 UX 관찰·개선 제안</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">DZ-03</td><td>미니 디자인 시스템 정리표</td><td>☑</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">OP-01</td><td>컨테이너 헬스체크 + 의존성 조건</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">OP-02</td><td>push 시 자동 테스트 게이트 (Gitea Actions)</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">SEC-01</td><td>보안·권한 계약 통합테스트 (@WebMvcTest)</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">SEC-02</td><td>민감 PII 작업 감사로그</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">SEC-03</td><td>공개 쓰기 엔드포인트 레이트리밋</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">SEC-04</td><td>개인정보 열람·내보내기 접근 로그</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">SEC-05</td><td>의존성 취약점 손으로 점검해 보기</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-05</td><td>프론트 첫 테스트 — 채점 함수 gradeSql 검증</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">QA-06</td><td>죽은 퀴즈 사본 정리 (seed/quiz 71개)</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-08</td><td>학습센터 진도율 로딩 가드 (0% 깜빡임)</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-09</td><td>멘토 '리뷰 대기' 한눈에 보기</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">FE-10</td><td>'개발 환경 설치 가이드' 진입점 만들기</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">EDU-01</td><td>'웹 보안·인증 기초' 코스 신설</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
<tr><td class="num">EDU-02</td><td>코딩 문제·캡스톤 문서를 주차별 교육안에 연결</td><td>☐</td><td><span class="fill"></span></td><td><span class="fill"></span></td><td><span class="fill"></span></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="card mt" style="font-size:14px;color:var(--ink-soft);">
|
|
<b style="color:var(--ink);">배정 팁</b> — 첫 티켓(5주차)은 ★로 시작해 성공 경험을 만들고, 6주차에 ★★로 올리세요. FE-01+DZ-01, FE-07+DZ-03, BE-04+FE(신규 화면)처럼 <b style="color:var(--primary);">짝 티켓</b>으로 주면 협업 평가 재료가 생깁니다. 고객사 코드 반영이 어려운 경우, 사내 도구·내부 프로젝트에서 같은 패턴을 찾아도 좋아요.
|
|
</div>
|
|
</section>
|
|
|
|
<footer>
|
|
<span>AWESOMEDEV · 수습 실전 티켓 풀 템플릿 (멘토용)</span>
|
|
<span>2026 · 5~6주차 강의안·평가 루브릭과 함께 사용</span>
|
|
</footer>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|