mirim-app/frontend/public/docs/week2-assignments.html
AWESOMEDEV 5c9959ce08
All checks were successful
CI / backend-test (push) Successful in 3m25s
CI / frontend-build (push) Successful in 48s
CI / backend-dep-scan (push) Successful in 31s
feat(design): 디자인 시스템 — Pretendard 실적용·토큰·Lucide 아이콘 132개·/design
이모지 아이콘을 SVG로 바꾸다가, 더 큰 원인을 발견해 함께 고쳤다.

■ 폰트: 이름만 부르고 파일은 안 받아오고 있었다
  global.css에 font-family: "Pretendard"가 적혀 있었지만 @font-face도 CDN
  링크도 없었다. 그래서 이 글꼴이 따로 깔린 사람 외에는 전원 맑은 고딕으로
  보고 있었다 — 앱과 학습 문서 19개 전부. 에러가 안 나서 조용히 넘어간 케이스.
  - PretendardStdVariable.woff2(285KB)를 public/fonts에 self-host
    · CDN 의존 없음 → 외부 서비스가 죽어도, 오프라인에서도 안 깨진다
    · variable font → 파일 하나가 굵기 45~920을 전부 담는다
    · font-display: swap → 받는 동안 흰 화면(FOIT) 방지
  - 전체판(2MB) 대신 Std판(상용 한글 2,350자)을 골랐다. 앱이 쓰는 한글
    1,276자가 Std에 전부 있는지 브라우저에서 실측 확인했다(폴백 0자).
  - 학습 문서 19개에도 같은 @font-face 적용

■ 아이콘: 이모지 95종 → Lucide 132개
  이모지는 OS마다 다른 그림으로 렌더링되고, 굵기·색을 통제할 수 없고,
  스크린리더가 "bust in silhouette"처럼 읽는다.
  - 카탈로그의 icon: '📜'(문자) → Icon: ScrollText(컴포넌트)로 전환
  - strokeWidth={1.5}로 굵기 통일, aria-hidden으로 장식임을 명시
  - 개별 import만 사용(트리셰이킹) → gzip +14KB
  - slug: null인 '개발 환경 설치 가이드' 항목이 일괄 변환에서 누락돼
    Icon이 undefined가 될 뻔한 것을 정합성 검사(항목 수 vs Icon 수)로 잡았다

■ 토큰: 여백·글자 크기·굵기를 :root로 승격
  --space-1~7(4의 배수), --text-xs~2xl, --weight-*, --radius-sm/lg/full.
  값을 직접 박으면 다크 모드에서 깨지고 화면마다 미묘하게 어긋난다.
  CSS 프레임워크는 도입하지 않았다 — 손으로 쓴 global.css와 충돌하고,
  교재로서 "왜 이렇게 썼는지"가 사라지기 때문이다.

■ /design: 재료를 고르는 화면
  색·글자·여백·모서리를 실제 CSS 변수로 렌더링(다크 모드 자동 반영) +
  Lucide 아이콘 1,993개 검색·복사. lucide-react는 아이콘 하나를 이름 3개로
  내보내(Rocket/RocketIcon/LucideRocket) 5,981개처럼 보이므로 별칭을 걸렀다.
  아이콘 전체를 훑어야 해 무거운 화면이라 lazy로 분리했다
  → 첫 화면 번들은 1.9KB만 증가(575.57 → 577.44KB).

검증: ESLint 통과, Vitest 30개 통과, 프로덕션 빌드 성공, 콘솔 에러 0.
브라우저에서 수습 7카테고리 + 초급~특급 4과정 = 11개 화면 전수 확인
(각 12카드/12아이콘/이모지 0).

문서: DESIGN.md(폰트·토큰·비용), ICONS.md(규약·대응표 132행)
2026-07-22 21:10:28 +09:00

868 lines
58 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>어썸데브 수습 교육 · 2주차 일일 과제집</title>
<style>
/* 웹폰트: 앱과 같은 Pretendard를 문서에도 적용한다(선언만 있고 파일이 없던 문제 수정). */
@font-face {
font-family: "Pretendard";
src: url("/fonts/PretendardStdVariable.woff2") format("woff2-variations");
font-weight: 45 920;
font-display: swap;
}
: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: 860px; 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; }
.time { display: grid; gap: 0; }
.time .row { display: grid; grid-template-columns: 128px 1fr; gap: 16px; padding: 11px 0; border-top: 1px solid var(--line-soft); align-items: baseline; }
.time .row:first-child { border-top: 0; }
.time .t { font-weight: 700; font-size: 13.5px; font-variant-numeric: tabular-nums; color: var(--ink); white-space: nowrap; }
.time .v { font-size: 14.5px; color: var(--ink-soft); } .time .v b { color: var(--ink); font-weight: 600; }
.day-div { margin-top: clamp(38px,6vw,56px); display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: center; }
.day-badge { width: 58px; height: 58px; border-radius: 14px; background: var(--primary); color: #fff; display: grid; place-items: center; text-align: center; line-height: 1.05; box-shadow: var(--shadow); }
@media (prefers-color-scheme: dark){ .day-badge { color:#0B1017; } }
:root[data-theme="dark"] .day-badge { color:#0B1017; }
.day-badge .w { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; opacity: .85; }
.day-badge .n { font-size: 22px; font-weight: 800; }
.day-div h2 { margin: 0; font-size: clamp(21px,3.6vw,27px); font-weight: 800; letter-spacing: -0.025em; }
.day-div .dm { margin-top: 3px; color: var(--ink-soft); font-size: 14px; }
.task { margin-top: 16px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); box-shadow: var(--shadow); overflow: hidden; }
.t-head { padding: 16px 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; }
.t-head h3 { margin: 0; font-size: 17px; font-weight: 800; letter-spacing: -0.015em; }
.t-head .kind { font-size: 11px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--primary); }
.diff { font-size: 12px; font-weight: 800; letter-spacing: 0.03em; 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); }
.diff.d3 { color: var(--rose); background: var(--rose-soft); }
.t-body { padding: 18px 20px; display: grid; gap: 14px; }
.block > .bt { font-size: 12px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 8px; display: flex; align-items: center; gap: 7px; }
.block .bt .ic { width: 20px; height: 20px; border-radius: 6px; display: grid; place-items: center; font-size: 12px; }
.bt .ic.goal { background: var(--primary-soft); } .bt .ic.step { background: var(--teal-soft); }
.bt .ic.out { background: var(--amber-soft); } .bt .ic.extra { background: var(--line-soft); }
.bt .ic.dz { background: var(--rose-soft); }
.goal-line { font-size: 14.5px; color: var(--ink); }
.steps { margin: 0; padding: 0 0 0 2px; display: grid; gap: 8px; counter-reset: st; }
.steps li { list-style: none; padding-left: 30px; position: relative; font-size: 14px; color: var(--ink-soft); counter-increment: st; }
.steps li::before { content: counter(st); position: absolute; left: 0; top: 1px; width: 20px; height: 20px; border-radius: 6px; background: var(--teal-soft); color: var(--teal); font-weight: 800; font-size: 12px; display: grid; place-items: center; }
.steps li b { color: var(--ink); font-weight: 600; }
.out-box { border: 1px solid var(--amber-line); background: var(--amber-soft); border-radius: 10px; padding: 12px 15px; }
.out-box p { font-size: 13.5px; color: var(--ink-soft); } .out-box b { color: var(--ink); }
.out-box ul { margin: 6px 0 0; padding-left: 20px; display: grid; gap: 4px; font-size: 13.5px; color: var(--ink); }
.extra-box { border: 1px dashed var(--line); background: var(--code-bg); border-radius: 10px; padding: 12px 15px; }
.extra-box p { font-size: 13.5px; color: var(--ink-soft); } .extra-box b { color: var(--primary); }
.dz-box { border: 1px solid color-mix(in srgb,var(--rose) 25%,transparent); background: var(--rose-soft); border-radius: 10px; padding: 12px 15px; }
.dz-box p { font-size: 13.5px; color: var(--ink-soft); } .dz-box b { color: var(--rose); }
.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.65; color: var(--ink); white-space: pre-wrap; word-break: break-word; letter-spacing: 0; }
.tpl .hl { color: var(--primary); font-weight: 600; }
code { font-family: "SF Mono","Cascadia Code","Consolas",ui-monospace,monospace; font-size: 0.9em; background: var(--code-bg); padding: 1px 6px; border-radius: 5px; }
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){ .time .row { grid-template-columns: 96px 1fr; gap: 10px; } }
@media print { body{background:#fff;} .card,header.mast,.task,.day-badge{box-shadow:none;} .task{break-inside:avoid;} }
</style>
</head>
<body>
<div class="wrap">
<header class="mast">
<div class="wordmark"><b>AWESOMEDEV</b> · 일일 과제집 · 2 / 4</div>
<h1>2주차 일일 과제집<br>코드 읽기 &amp; 자료구조 적용</h1>
<p class="lede">이 과제집은 오후 자기주도 시간(10:4012:00, 13:0016:30)에 여러분이 혼자 읽고 그대로 수행할 수 있도록 만든 문서예요. 매일 정해진 산출물이 있고, 16:30 멘토 리뷰 시간에 반드시 제출해요. 오전 강의에서 배운 내용을 오후에 손으로 직접 확인하는 것이 이번 주의 핵심이에요.</p>
<div class="mast-meta">
<span class="chip"><span class="dot"></span>Week 2 · 월–금</span>
<span class="chip"><span class="dot"></span>오후 자기주도 시간용</span>
<span class="chip"><span class="dot"></span>매일 16:30 과제 리뷰 제출</span>
</div>
</header>
<section id="rhythm">
<div class="eyebrow">Daily Rhythm</div>
<h2>하루의 리듬</h2>
<p class="sub">2주차 내내 매일 같은 리듬으로 움직여요. 시간을 몸에 익히면 과제에만 집중할 수 있어요.</p>
<div class="card mt">
<div class="time">
<div class="row"><div class="t">09:3010:30</div><div class="v"><b>아침 강의(멘토)</b> — 이번 주는 자료구조(배열·리스트·스택·큐·해시·트리) + 요일별 주제 강의</div></div>
<div class="row"><div class="t">10:4012:00</div><div class="v"><b>일일 과제 전반</b> — 과제집을 열고 그날의 본 과제를 시작해요</div></div>
<div class="row"><div class="t">13:0016:30</div><div class="v"><b>일일 과제 후반</b> — 막히면 <b>30분 룰</b>: 30분 넘게 혼자 못 풀면 질문 메모를 적고 다음 단계로 넘어가요</div></div>
<div class="row"><div class="t">16:3017:00</div><div class="v"><b>멘토 과제 리뷰</b> — 그날의 산출물을 제출하고 피드백을 받아요</div></div>
<div class="row"><div class="t">17:0017:30</div><div class="v"><b>하루 정리 · 3줄 회고</b> — 오늘 배운 것 1줄 / 막힌 것 1줄 / 내일 할 것 1줄</div></div>
</div>
<p class="mt" style="font-size:14px; color:var(--ink-soft);"><strong>규칙 하나만 기억해요:</strong> 과제가 일찍 끝나면 놀지 말고 예비 과제로 — 예비 과제도 평가에 반영돼요.</p>
</div>
</section>
<!-- ===================== MON ===================== -->
<div class="day-div" id="d1">
<div class="day-badge"><div><div class="w">MON</div><div class="n"></div></div></div>
<div>
<h2>코드 지도 그리기</h2>
<p class="dm">오전 강의 "코드베이스 구조 훑기"에서 본 폴더들을, 오후에는 직접 열어 보고 내 손으로 지도를 그려요.</p>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">본 과제</div><h3>React · Spring Boot 프로젝트 구조도 그리기</h3></div>
<span class="diff d2">난이도 ★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">처음 보는 프로젝트를 받아도 "어디에 무엇이 있는지" 10분 안에 파악하는 눈을 만들어요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>멘토가 안내한 <b>연습용 React 프로젝트</b>를 에디터(VS Code)로 열어요. 왼쪽 파일 탐색기에서 최상위 폴더부터 하나씩 펼쳐 보세요.</li>
<li>주요 폴더 <b>10개</b>를 골라요. 예: <code>src/</code>, <code>src/components/</code>, <code>src/pages/</code>, <code>src/hooks/</code>, <code>src/api/</code>, <code>public/</code>, <code>node_modules/</code> 등. 각 폴더 안의 파일 2~3개를 실제로 열어 보고 "이 폴더의 역할"을 <b>한 줄</b>로 적어요. 추측이면 문장 끝에 (추측)이라고 표시해요.</li>
<li>아래 <b>구조도 워크시트</b> 양식에 맞춰 종이나 문서에 트리 형태로 그려요. 그림 도구는 자유(손그림 사진도 OK)지만 폴더 이름과 역할 한 줄은 반드시 들어가야 해요.</li>
<li>같은 방법으로 <b>연습용 Spring Boot 프로젝트</b>를 열어요. <code>src/main/java/</code> 아래 패키지(controller, service, repository, domain/entity, config 등)와 <code>src/main/resources/</code>를 중심으로 폴더 10개를 골라 역할 한 줄을 적어요.</li>
<li>두 구조도를 나란히 놓고 <b>공통점 2개, 차이점 2개</b>를 문장으로 적어요. (예: "둘 다 화면/요청을 받는 층과 데이터 층이 나뉘어 있다")</li>
<li>마지막으로 "내가 열어 봤지만 역할을 모르겠는 파일" <b>3개</b>를 골라 파일 경로와 함께 질문으로 적어요. 이건 16:30 리뷰 때 멘토에게 그대로 물어봐요.</li>
</ol>
</div>
<div class="tpl">
<div class="cap">워크시트 · 구조도 양식 (React / Spring Boot 각 1장)</div>
<pre>[프로젝트 이름] ______________ (React / Spring Boot 중 하나)
폴더 트리 (10개 이상)
├─ <span class="hl">폴더명</span> ..................... 역할 한 줄: ______________________
│ └─ 대표 파일 1개: ______________
├─ <span class="hl">폴더명</span> ..................... 역할 한 줄: ______________________
│ └─ 대표 파일 1개: ______________
... (10개까지 반복)
공통점·차이점 (두 장을 다 그린 뒤 작성)
- 공통점 1: ______________________
- 공통점 2: ______________________
- 차이점 1: ______________________
- 차이점 2: ______________________
모르겠는 파일 질문 3개
1) 경로: ______________ / 질문: ______________________
2) 경로: ______________ / 질문: ______________________
3) 경로: ______________ / 질문: ______________________</pre>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>JS 기초 연습 (13:00 이후 병행)</div>
<p class="goal-line" style="margin-bottom:8px;">아래 10문제를 새 파일 <code>day1.js</code>에 번호 주석과 함께 풀어요. 브라우저 콘솔이나 Node로 실행해 <code>console.log</code> 결과를 확인하고, 답은 외우지 말고 직접 실행해서 검증해요.</p>
<div class="tpl">
<div class="cap">연습문제 · JS 기초 10제 (변수·조건·반복)</div>
<pre>1. 변수 name에 자기 이름, age에 나이를 담고 "저는 OO이고 18살입니다" 형태로 출력하세요. (템플릿 문자열 ` ` 사용)
2. let과 const의 차이를 코드로 증명하세요. const로 선언한 변수에 재할당을 시도하고, 발생한 에러 메시지를 주석으로 남기세요.
3. 숫자 score가 90 이상이면 "A", 80 이상이면 "B", 그 외에는 "C"를 출력하는 if/else if/else를 작성하세요. score를 95, 82, 60으로 바꿔 3번 실행해 보세요.
4. 숫자 n이 짝수면 "짝수", 홀수면 "홀수"를 출력하세요. (% 연산자 사용)
5. for 반복문으로 1부터 10까지 출력하세요.
6. for 반복문으로 1부터 100까지의 합을 구해 출력하세요. (정답은 5050이 나와야 해요)
7. while 반복문으로 10부터 1까지 거꾸로 출력하세요.
8. 1부터 30까지 중 3의 배수만 출력하세요. (if와 % 조합)
9. 구구단 7단을 "7 x 1 = 7" 형태로 출력하세요.
10. 이중 for문으로 별 찍기: 1줄에 *, 2줄에 **, ... 5줄에 *****가 나오게 출력하세요.</pre>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지 제출할 것</b> — 팀 공유 폴더의 본인 이름 폴더에 올려요.</p>
<ul>
<li>구조도 2장 (React 1장 + Spring Boot 1장, 워크시트 양식대로)</li>
<li>공통점·차이점 4문장 + 질문 3개 (구조도에 포함)</li>
<li><code>day1.js</code> — 10문제 풀이, 문제 번호 주석 포함, 전부 실행돼야 해요</li>
</ul>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">예비 과제</div><h3>package.json 탐험</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">프로젝트의 "명함"인 설정 파일을 읽고 이 프로젝트가 무엇으로 만들어졌는지 설명할 수 있게 돼요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>React 프로젝트의 <code>package.json</code>을 열어요.</li>
<li><code>scripts</code> 항목의 명령어 각각이 무슨 일을 하는지 추측해서 한 줄씩 적어요. (예: <code>dev</code>, <code>build</code>, <code>lint</code>)</li>
<li><code>dependencies</code>에서 라이브러리 5개를 골라 이름을 검색해 보고, 각각의 용도를 한 줄로 적어요.</li>
<li>Spring Boot 쪽 <code>pom.xml</code>(또는 <code>build.gradle</code>)을 열어 같은 방식으로 의존성 5개의 용도를 적어요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic extra"></span>제출</div>
<div class="extra-box">
<p><b>메모 1장</b>(스크립트 설명 + 라이브러리 10개 용도)을 본 과제 산출물과 함께 제출해요. 예비 과제도 평가에 반영돼요.</p>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">디자이너 과제</div><h3>UI 인벤토리 ① — 색상 팔레트 추출</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic dz">🎨</span>개발자 과제와의 차이</div>
<div class="dz-box">
<p><b>디자이너는 코드 대신 화면을 지도화해요.</b> 이번 주 내내 우리 서비스 화면을 뒤져 "UI 인벤토리"를 만들고, 금요일에 개선 리포트로 완성해요. 오늘은 그 첫날, 색상이에요.</p>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>멘토가 안내한 우리 서비스 화면(웹) <b>5개 이상</b>을 돌아다니며 스크린샷을 찍어요.</li>
<li>스포이드 도구(Figma·브라우저 개발자도구 등)로 화면에 실제 쓰인 색을 <b>전부</b> 추출해요. HEX 값으로 기록해요.</li>
<li>Figma에 색상 칩을 나열하고 <b>주색·보조색·회색 계열·경고/성공색</b>으로 그룹핑해요.</li>
<li>"거의 같은데 미묘하게 다른 색" 쌍을 찾아 표시해요. (예: #333333 vs #343434) — 목요일 불일치 찾기의 재료가 돼요.</li>
<li>색이 몇 개인지 세어 보고, "이 서비스의 색은 관리되고 있는가?"에 대한 내 생각을 3문장으로 적어요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지:</b></p>
<ul>
<li>Figma 색상 팔레트 페이지 링크 (HEX 값 + 그룹핑 포함)</li>
<li>"미묘하게 다른 색" 쌍 목록 + 내 생각 3문장</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ===================== TUE ===================== -->
<div class="day-div" id="d2">
<div class="day-badge"><div><div class="w">TUE</div><div class="n"></div></div></div>
<div>
<h2>요청 추적 리포트</h2>
<p class="dm">오전 강의 "Network 탭 요청 흐름"에서 본 것을, 오후에는 우리 서비스에서 직접 잡아내요.</p>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">본 과제</div><h3>API 요청 5개 추적하기</h3></div>
<span class="diff d2">난이도 ★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">화면 뒤에서 오가는 요청과 응답을 스스로 관찰하고 "이 요청이 무슨 일을 하는지" 추론하는 힘을 길러요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>브라우저에서 우리 서비스를 열고 <b>F12 → Network 탭</b>을 켜요. 필터를 <b>Fetch/XHR</b>로 바꾸면 API 요청만 보여요.</li>
<li>서비스를 자유롭게 사용하면서(로그인, 목록 보기, 검색, 저장 등) Network 탭에 잡히는 요청 중 <b>서로 다른 동작 5개</b>를 골라요. GET만 5개 고르지 말고 <b>POST·PUT·DELETE 중 최소 1개</b>를 포함해요.</li>
<li>각 요청을 클릭해 <b>Headers·Payload·Response</b>를 확인하고 아래 추적표 양식에 기록해요. 응답 JSON이 길면 핵심 필드 5개만 적어도 돼요.</li>
<li>각 요청마다 "이 요청은 ~를 하는 것 같다"라는 <b>추측 문장</b>을 적어요. 근거(URL 단어, 응답 내용 등)도 한 줄 함께요.</li>
<li>5개 중 하나를 골라 <b>상태 코드</b>가 무엇인지 확인하고, 일부러 실패시켜 보세요(예: 로그아웃 상태로 호출, 잘못된 검색어). 성공/실패 시 상태 코드와 응답이 어떻게 다른지 2문장으로 기록해요.</li>
<li>다 적었으면 표를 다시 읽으며 <b>내가 자신 없는 칸에 형광 표시</b>를 해요. 리뷰 때 그 부분부터 질문해요.</li>
</ol>
</div>
<div class="tpl">
<div class="cap">워크시트 · 요청 추적표 (요청 5개 × 아래 양식)</div>
<pre>요청 #__ 이름(내가 붙인 별명): ______________
- 언제 발생? (무슨 버튼/화면): ______________________
- Method: <span class="hl">GET / POST / PUT / DELETE</span> 중 ______
- URL: ______________________
- 요청 데이터(Payload, 없으면 "없음"): ______________________
- 응답 상태 코드: ______
- 응답 데이터(핵심 필드 위주): ______________________
- 추측: 이 요청은 ______________________ 를 하는 것 같다.
- 근거: ______________________
(마지막에 한 번만) 실패 실험
- 실험 방법: ______________________
- 성공 시: 코드 ____ / 응답 ______________
- 실패 시: 코드 ____ / 응답 ______________</pre>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>JS 배열 연습 (병행)</div>
<p class="goal-line" style="margin-bottom:8px;">아침 자료구조 강의(배열·리스트)와 연결되는 문제예요. <code>day2.js</code>에 15문제를 모두 풀고 실행해서 확인해요.</p>
<div class="tpl">
<div class="cap">연습문제 · JS 배열 15제</div>
<pre>공통 준비: const fruits = ["사과", "바나나", "포도"]; const nums = [3, 1, 4, 1, 5, 9, 2, 6];
1. fruits 배열 끝에 "딸기"를 추가하고(push) 배열 전체를 출력하세요.
2. fruits 배열의 마지막 요소를 제거하고(pop) 제거된 값과 남은 배열을 각각 출력하세요.
3. fruits 배열 맨 앞에 "귤"을 추가하세요(unshift). 맨 앞 요소를 제거하는 것(shift)도 해 보세요.
4. nums 배열의 길이(length)와 3번째 요소(인덱스 주의!)를 출력하세요.
5. nums에서 5가 몇 번째 인덱스에 있는지 indexOf로 찾아 출력하세요. 없는 값 100을 찾으면 무엇이 나오는지도 확인하세요.
6. nums 배열에 9가 포함되어 있는지 includes로 확인해 true/false를 출력하세요.
7. for문으로 nums의 모든 요소를 한 줄씩 출력하세요. 그다음 같은 일을 forEach로 다시 작성하세요.
8. nums의 모든 요소를 2배로 만든 새 배열을 map으로 만들어 출력하세요. (원본 nums는 그대로여야 해요 — 확인 출력 포함)
9. nums에서 4 이상인 요소만 남긴 새 배열을 filter로 만들어 출력하세요.
10. nums의 합계를 구하세요. 먼저 for문으로, 그다음 reduce로 두 번 구현하세요.
11. nums에서 가장 큰 값을 찾으세요. (반복문으로 직접 — Math.max 금지)
12. fruits 배열을 join으로 "사과, 바나나, 포도" 형태의 문자열로 만들어 출력하세요.
13. nums를 오름차순 정렬하세요. sort()만 쓰면 [1,1,2,3,4,5,6,9]가 안 나올 수 있어요 — 왜 그런지 주석으로 설명하고 올바르게 고치세요.
14. 학생 배열 const students = [{name:"김", score:85}, {name:"이", score:92}, {name:"박", score:78}] 에서 score가 80 이상인 학생의 name만 담긴 배열을 만드세요. (filter + map 조합)
15. nums에서 중복을 제거한 새 배열을 만드세요. 방법은 자유(반복문, includes, Set 등) — 어떤 방법을 왜 골랐는지 주석 한 줄을 남기세요.</pre>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지 제출할 것</b></p>
<ul>
<li>요청 추적표 5건 + 실패 실험 기록 (문서 1개)</li>
<li><code>day2.js</code> — 배열 15문제 풀이 (문제 번호 주석 포함)</li>
</ul>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">예비 과제</div><h3>상태 코드 카드 만들기</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">자주 만나는 HTTP 상태 코드를 내 언어로 정리해 두면 앞으로 에러를 볼 때 당황하지 않아요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>다음 8개 코드에 대해 조사해요: <code>200</code>, <code>201</code>, <code>301</code>, <code>400</code>, <code>401</code>, <code>403</code>, <code>404</code>, <code>500</code>.</li>
<li>각 코드마다 "공식 의미 한 줄 + 내 언어로 다시 쓴 한 줄 + 언제 만날 것 같은지 예시 한 줄"을 적어요.</li>
<li>오늘 Network 탭에서 실제로 본 코드에는 ✔ 표시를 해요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic extra"></span>제출</div>
<div class="extra-box">
<p><b>상태 코드 카드 8장</b>(문서 1개로 정리)을 본 과제와 함께 제출해요.</p>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">디자이너 과제</div><h3>UI 인벤토리 ② — 타이포그래피</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic dz">🎨</span>개발자 과제와의 차이</div>
<div class="dz-box">
<p><b>개발자가 Network 탭을 보는 동안, 디자이너는 개발자도구의 Elements 탭으로 글자를 관찰해요.</b> 같은 도구(F12)를 다른 각도로 쓰는 날이에요.</p>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>어제 본 화면 5개에서 <b>제목·부제목·본문·버튼 라벨·캡션</b>에 해당하는 텍스트를 찾아요.</li>
<li>개발자도구로 각 텍스트를 클릭 검사해 <b>font-family / font-size / font-weight / line-height / 색상</b>을 기록해요.</li>
<li>Figma에 "타입 스케일 표"를 만들어요: 역할(제목1, 제목2, 본문…) × 속성 값.</li>
<li>같은 역할인데 값이 다른 경우(예: 본문이 어떤 화면은 14px, 어떤 화면은 15px)를 찾아 표시해요.</li>
<li>글자 크기 종류가 총 몇 개인지 세고, "적절한 개수인가?"에 대한 생각을 3문장으로 적어요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지:</b></p>
<ul>
<li>Figma 타입 스케일 표 링크</li>
<li>불일치 사례 목록 + 생각 3문장</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ===================== WED ===================== -->
<div class="day-div" id="d3">
<div class="day-badge"><div><div class="w">WED</div><div class="n"></div></div></div>
<div>
<h2>기능 해부 보고서</h2>
<p class="dm">오전 강의 "기능 읽기"에서 배운 추적법으로, 오후에는 기능 하나를 끝까지 해부해요. 월(구조)·화(요청)를 합치는 날이에요.</p>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">본 과제</div><h3>기능 하나를 끝까지 해부하기</h3></div>
<span class="diff d3">난이도 ★★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">"버튼 클릭부터 응답까지" 한 기능의 전체 여정을 코드와 화면을 오가며 설명할 수 있게 돼요 — 이게 되면 코드 읽기의 절반은 끝난 거예요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>해부할 기능을 <b>하나</b> 골라요. 추천: <b>로그인</b> 또는 <b>목록 조회</b>. 너무 복잡한 기능(결제 등)은 피해요. 고민되면 10:40에 멘토에게 30초만 물어보고 시작해요.</li>
<li><b>화면에서 관찰</b>: 그 기능을 직접 사용하면서 "무엇을 누르면 → 화면이 어떻게 변하고 → Network 탭에 어떤 요청이 뜨고 → 어떤 응답이 오는지"를 순서대로 메모해요. (어제 배운 추적표 방식 재활용)</li>
<li><b>프론트 코드에서 찾기</b>: React 프로젝트에서 그 버튼/화면을 담당하는 컴포넌트 파일을 찾아요. 에디터 전체 검색(Ctrl+Shift+F)에 버튼에 적힌 문구나 URL 일부를 넣으면 빨라요. 찾으면 <b>파일 경로와 줄 번호</b>를 기록해요.</li>
<li>그 컴포넌트에서 <b>API를 호출하는 코드</b>(fetch, axios 등)를 찾아 파일·줄을 기록하고, 화면에서 본 요청 URL과 일치하는지 확인해요.</li>
<li><b>백엔드 코드에서 찾기</b>: Spring Boot 프로젝트에서 그 URL을 받는 컨트롤러를 찾아요(<code>@GetMapping</code>·<code>@PostMapping</code>의 경로 검색). 컨트롤러 → 서비스 → 리포지토리로 이어지는 호출을 따라가며 각 단계의 파일·줄·역할 한 줄을 기록해요. <b>모든 줄을 이해할 필요는 없어요</b> — 흐름의 연결만 잡으면 성공이에요.</li>
<li>아래 보고서 양식대로 정리해요. 이해 안 되는 코드는 "❓ 여기서 ~하는 것 같은데 확실하지 않음"으로 솔직하게 적어요. ❓가 있는 보고서가 없는 보고서보다 좋은 보고서예요.</li>
<li>시간이 남으면 흐름을 그림 1장(손그림 OK)으로 그려 보고서에 붙여요.</li>
</ol>
</div>
<div class="tpl">
<div class="cap">워크시트 · 기능 해부 보고서 양식</div>
<pre>■ 기능 이름: ______________ (예: 로그인)
■ 한 줄 요약: 사용자가 ______ 하면 ______ 가 일어난다.
① 화면에서 관찰한 것
- 트리거(무엇을 눌렀나): ______________
- 화면 변화: ______________
- 발생한 요청: <span class="hl">METHOD</span> ______ URL ______________
- 응답(핵심만): ______________
② 프론트 코드
- 화면/버튼 담당 파일: ______________ (____ 줄 근처)
- 이 파일이 하는 일 한 줄: ______________
- API 호출 코드 위치: ______________ (____ 줄 근처)
③ 백엔드 코드
- 컨트롤러: ______________ (____ 줄) — 역할: ______________
- 서비스: ______________ (____ 줄) — 역할: ______________
- 리포지토리/DB 접근: ______________ (____ 줄) — 역할: ______________
④ 전체 흐름 한 문장
버튼 클릭 → __________ → __________ → __________ → 화면 갱신
⑤ ❓ 이해 못 한 지점 (최소 2개, 솔직하게)
1) ______________________
2) ______________________</pre>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>JS 객체 연습 (병행)</div>
<p class="goal-line" style="margin-bottom:8px;">아침 자료구조 강의(해시)와 연결돼요. 객체와 Map은 "이름표로 값을 찾는 구조"예요. <code>day3.js</code>에 10문제를 풀어요.</p>
<div class="tpl">
<div class="cap">연습문제 · JS 객체·Map 10제</div>
<pre>1. 자신을 표현하는 객체 me를 만드세요. name, age, school, hobby 4개 속성을 넣고 전체를 출력하세요.
2. me.hobby를 점 표기법으로, me["school"]을 대괄호 표기법으로 각각 출력하세요. 두 방식의 차이를 주석 한 줄로 적으세요.
3. me에 새 속성 goal(올해 목표)을 추가하고, age 속성을 delete로 삭제한 뒤 결과를 출력하세요.
4. Object.keys(me)와 Object.values(me)를 출력하고, 각각 무엇이 나오는지 주석으로 설명하세요.
5. for...in 반복문으로 me의 모든 "속성이름: 값"을 한 줄씩 출력하세요.
6. 메뉴판 객체 const menu = { 아메리카노: 4500, 라떼: 5000, 주스: 6000 } 에서 사용자가 고른 메뉴 이름(변수 pick)의 가격을 출력하세요. menu에 없는 메뉴면 "없는 메뉴입니다"를 출력하세요.
7. 문자열 "hello world"에서 각 글자가 몇 번 나오는지 세는 객체를 만드세요. (예: {h:1, e:1, l:3, ...}) 공백은 제외하세요.
8. new Map()으로 학번→이름 Map을 만들고 set으로 3명을 넣은 뒤, get으로 1명을 꺼내고 has로 존재 여부를 확인하세요. size도 출력하세요.
9. 7번을 객체 대신 Map으로 다시 구현하세요. 객체 버전과 Map 버전 중 어느 쪽이 편했는지 주석 한 줄로 적으세요.
10. 학생 배열 [{name:"김", team:"A"}, {name:"이", team:"B"}, {name:"박", team:"A"}] 를 팀별로 묶은 객체 {A:["김","박"], B:["이"]} 로 변환하세요.</pre>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지 제출할 것</b></p>
<ul>
<li>기능 해부 보고서 1건 (양식의 ①~⑤ 모두 채울 것, ❓ 최소 2개 포함)</li>
<li><code>day3.js</code> — 객체·Map 10문제 풀이</li>
</ul>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">예비 과제</div><h3>두 번째 기능 미니 해부</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">한 번 해 본 해부를 다른 기능에 반복하면 속도가 2배로 붙는 걸 스스로 느낄 수 있어요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>본 과제에서 안 고른 기능 하나를 골라요.</li>
<li>보고서 양식 중 <b>①(화면 관찰)과 ④(전체 흐름 한 문장)만</b> 채워요. 코드 추적은 여유 있을 때만.</li>
<li>본 과제 때보다 얼마나 빨라졌는지 소요 시간을 기록해요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic extra"></span>제출</div>
<div class="extra-box">
<p><b>미니 보고서 1건</b>(①+④만)과 소요 시간 비교 한 줄을 함께 제출해요.</p>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">디자이너 과제</div><h3>UI 인벤토리 ③ — 컴포넌트 변형 수집</h3></div>
<span class="diff d2">난이도 ★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic dz">🎨</span>개발자 과제와의 차이</div>
<div class="dz-box">
<p><b>개발자가 기능 하나를 세로로 깊게 파는 동안, 디자이너는 컴포넌트를 가로로 넓게 모아요.</b> 오늘이 인벤토리 중 가장 손이 많이 가는 날이에요.</p>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>서비스 전체에서 <b>버튼</b>을 보이는 대로 전부 스크린샷으로 수집해요. (주 버튼, 보조 버튼, 텍스트 버튼, 비활성 버튼, 아이콘 버튼…)</li>
<li>같은 방법으로 <b>입력 필드</b>(기본, 포커스, 에러, 비활성)와 <b>카드</b>(목록 카드, 상세 카드 등)를 수집해요.</li>
<li>Figma에 세 그룹(버튼/입력/카드)으로 나눠 나열하고, 각 변형에 이름을 붙여요. (예: "버튼-주-대", "입력-에러")</li>
<li>변형별로 크기·모서리 반경·색을 메모해요. 정확하지 않아도 눈대중 + 개발자도구 확인이면 충분해요.</li>
<li>"이 둘은 사실 하나여야 하지 않나?" 싶은 변형 쌍에 표시를 남겨요 — 내일 불일치 찾기의 핵심 재료예요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지:</b></p>
<ul>
<li>Figma 컴포넌트 인벤토리 페이지 링크 (버튼·입력·카드 3그룹, 변형 이름 포함)</li>
<li>"하나여야 할 것 같은 쌍" 후보 목록</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ===================== THU ===================== -->
<div class="day-div" id="d4">
<div class="day-badge"><div><div class="w">THU</div><div class="n"></div></div></div>
<div>
<h2>PR 실전 연습</h2>
<p class="dm">오전 강의 "Git 실전(브랜치→PR)"의 흐름을, 오후에 연습 저장소에서 3번 반복해 몸에 새겨요.</p>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">본 과제</div><h3>브랜치 → 커밋 → PR 3회전 + 동료 리뷰</h3></div>
<span class="diff d2">난이도 ★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">실무 협업의 기본 사이클(브랜치→커밋→PR→리뷰)을 혼자서 처음부터 끝까지 3번 돌릴 수 있게 돼요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>멘토가 만든 <b>연습 저장소</b>를 clone 해요. 오전에 받은 저장소 주소를 사용하고, <code>git clone 주소</code><code>cd 폴더명</code> 순서예요.</li>
<li><b>1회전 — README 오타 수정</b>: <code>git switch -c fix/readme-오타-본인이름</code> 으로 브랜치를 만들고, README에 심어 둔 오타를 찾아 고쳐요. <code>git add</code><code>git commit -m "docs: README 오타 수정"</code><code>git push -u origin 브랜치명</code> → 저장소 웹 화면에서 <b>PR 생성</b>. PR 설명은 아래 양식대로 써요.</li>
<li><b>2회전 — 주석 추가</b>: main으로 돌아가(<code>git switch main</code><code>git pull</code>) 새 브랜치 <code>docs/주석-본인이름</code>을 만들어요. 저장소의 <code>utils.js</code>에서 주석 없는 함수 2개를 골라 "무엇을 받아 무엇을 돌려주는지" 주석을 달고, 같은 사이클로 PR까지 만들어요.</li>
<li><b>3회전 — 간단 함수 작성</b>: 브랜치 <code>feat/함수-본인이름</code>에서 <code>utils.js</code>에 아래 세 함수 중 <b>2개</b>를 골라 구현해요: ⓐ <code>sum(arr)</code> 배열의 합 반환 ⓑ <code>maxNum(arr)</code> 배열의 최댓값 반환 ⓒ <code>countChar(str, ch)</code> 문자열에서 특정 글자 개수 반환. <code>console.log</code>로 각각 2가지 입력을 실행해 결과 확인 후 PR까지 만들어요.</li>
<li><b>동료 리뷰</b>: 옆 사람의 PR 목록에서 아무거나 1개를 열어 코드를 읽고, <b>리뷰 코멘트 1개 이상</b>을 남겨요. "좋아요"만 쓰지 말고 ⓐ 좋은 점 1가지 + ⓑ 질문 또는 제안 1가지를 꼭 포함해요. (예: "변수 이름이 알아보기 쉬워요. 그런데 빈 배열이 들어오면 어떻게 되나요?")</li>
<li>내 PR에 달린 코멘트가 있으면 <b>답글</b>을 달아요. 수정이 필요하면 같은 브랜치에서 고치고 다시 push하면 PR에 자동 반영되는 것도 확인해요.</li>
<li>3회전 중 헷갈렸던 명령어를 <b>나만의 Git 치트시트</b>로 정리해요(명령어 + 내 언어 설명, 최소 8줄).</li>
</ol>
</div>
<div class="tpl">
<div class="cap">양식 · PR 설명 템플릿 (PR 3개 모두 이 양식으로)</div>
<pre><span class="hl">## 무엇을 했나요?</span>
- (한 줄로: 예. README의 오타 3곳을 수정했습니다)
<span class="hl">## 왜 했나요?</span>
- (예: 새로 온 사람이 문서를 읽을 때 헷갈리지 않도록)
<span class="hl">## 확인한 방법</span>
- (예: 수정한 함수를 console.log로 2가지 입력에 대해 실행해 확인)
<span class="hl">## 리뷰어에게</span>
- 특히 봐 주셨으면 하는 부분: ______________</pre>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지 제출할 것</b></p>
<ul>
<li>PR 3개 링크 (오타 수정 / 주석 추가 / 함수 작성 — 모두 템플릿 양식 사용)</li>
<li>동료 PR에 남긴 리뷰 코멘트 링크 1개 이상</li>
<li>나만의 Git 치트시트 (8줄 이상)</li>
</ul>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">예비 과제</div><h3>커밋 메시지 고쳐 쓰기</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">좋은 커밋 메시지와 나쁜 커밋 메시지를 구별하는 눈을 만들어요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>아래 나쁜 커밋 메시지 6개를 각각 좋은 메시지로 고쳐 쓰세요. 상황은 자유롭게 상상하되, "무엇을 왜 했는지"가 드러나야 해요.</li>
<li>고친 메시지마다 "원래 메시지의 문제점"을 한 줄씩 적어요.</li>
</ol>
</div>
<div class="tpl">
<div class="cap">연습문제 · 고쳐 쓸 커밋 메시지 6개</div>
<pre>1. "수정"
2. "asdf"
3. "버그 고침 + 새 기능 + 오타 수정 + 리팩토링"
4. "금요일 작업분"
5. "이제 진짜 됨"
6. "코드 추가"</pre>
</div>
<div class="block">
<div class="bt"><span class="ic extra"></span>제출</div>
<div class="extra-box">
<p><b>고쳐 쓴 메시지 6개 + 문제점 6줄</b>을 문서로 제출해요.</p>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">디자이너 과제</div><h3>UI 인벤토리 ④ — 불일치 지점 10개 찾기</h3></div>
<span class="diff d2">난이도 ★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic dz">🎨</span>개발자 과제와의 차이</div>
<div class="dz-box">
<p><b>디자이너도 오전 Git 강의는 함께 들어요.</b> 오후에는 개발자의 PR 대신, 월~수 인벤토리(색·글자·컴포넌트)를 증거 삼아 불일치를 잡아내요. 여유가 되면 개발자 연습 저장소에서 README 오타 수정 PR 1개에 도전해 보세요 — 디자이너가 PR을 만들 줄 알면 협업이 훨씬 부드러워져요.</p>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>월~수에 모은 인벤토리를 펼쳐 놓고 <b>불일치 지점 10개</b>를 골라요. (색 2~3개, 타이포 2~3개, 컴포넌트 4~5개 권장)</li>
<li>각 불일치마다 <b>비교 스크린샷 2장</b>(A화면 vs B화면)을 나란히 붙여요.</li>
<li>각 항목에 "무엇이 다른가 / 사용자에게 어떤 혼란을 주는가 / 어느 쪽으로 통일하면 좋을까(내 의견)"를 각 한 줄씩 적어요.</li>
<li>10개에 <b>심각도</b>(상·중·하)를 매기고, 상인 이유를 한 줄로 적어요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지:</b></p>
<ul>
<li>불일치 10건 목록 (비교 스크린샷 + 3줄 분석 + 심각도) — Figma 또는 문서</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ===================== FRI ===================== -->
<div class="day-div" id="d5">
<div class="day-badge"><div><div class="w">FRI</div><div class="n"></div></div></div>
<div>
<h2>주간 통합 미니 챌린지</h2>
<p class="dm">오전 회고·퀴즈에 이어, 오후에는 이번 주 전부(자료구조 + 코드 읽기)를 하나로 묶는 챌린지예요.</p>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">본 과제</div><h3>스택·큐 직접 구현 + 우리 서비스 속 배열 찾기</h3></div>
<span class="diff d3">난이도 ★★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">자료구조를 "외운 것"에서 "만들어 본 것"으로 바꾸고, 그것이 실제 서비스 어디에 살아 있는지 연결해요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>새 파일 <code>stack.js</code><b>Stack 클래스</b>를 만들어요. 내부 저장은 배열을 쓰되, 밖에서는 다음 4개 메서드만 쓰게 해요: <code>push(값)</code> 넣기 / <code>pop()</code> 맨 위 꺼내기(비었으면 <code>undefined</code>) / <code>peek()</code> 맨 위 확인만 / <code>isEmpty()</code> 비었는지 true·false.</li>
<li>동작 확인 코드를 작성해요: 1, 2, 3을 push한 뒤 pop을 두 번 하면 3, 2가 나오고 peek이 1인지 <code>console.log</code>로 검증해요. <b>빈 스택에서 pop</b>하는 경우도 꼭 확인해요.</li>
<li><code>queue.js</code><b>Queue 클래스</b>를 만들어요: <code>enqueue(값)</code> 뒤로 넣기 / <code>dequeue()</code> 앞에서 꺼내기 / <code>front()</code> 맨 앞 확인 / <code>isEmpty()</code>. "가", "나", "다"를 넣고 꺼내면 넣은 순서 그대로 나오는지 검증해요.</li>
<li>아래 <b>판단문제 5개</b>를 풀어요. 각 상황이 스택·큐 중 무엇에 어울리는지 고르고 이유를 한 줄씩 적어요.</li>
<li><b>서비스 탐험</b>: 우리 서비스에서 "목록(배열)이 화면에 그려지는 화면" <b>3개</b>를 찾아 스크린샷을 찍어요. 각 화면마다 ⓐ 무엇의 목록인지 ⓑ Network 탭 응답에서 배열(<code>[ ... ]</code>)이 실제로 보이는지 ⓒ 프론트 코드에서 그 목록을 그리는 <code>map</code> 호출을 찾을 수 있으면 파일·줄까지 기록해요. (ⓒ는 3개 중 1개만 성공해도 충분해요)</li>
<li>모든 내용을 <b>주간 챌린지 리포트</b> 1개 문서로 묶어요: 구현 코드 캡처(또는 링크) + 검증 결과 + 판단문제 답 + 화면 3건 기록.</li>
<li>마지막으로 이번 주 3줄 회고의 확장판, <b>주간 회고 5줄</b>을 리포트 끝에 적어요: 가장 배운 것 / 가장 어려웠던 것 / 30분 룰을 쓴 순간 / 다음 주에 시도할 것 / 멘토에게 바라는 것.</li>
</ol>
</div>
<div class="tpl">
<div class="cap">연습문제 · 스택/큐 판단문제 5개 (답과 이유를 적으세요)</div>
<pre>1. 브라우저의 "뒤로 가기" 버튼 — 스택일까 큐일까? 이유는?
2. 프린터에 먼저 보낸 문서가 먼저 인쇄되는 대기열 — 스택일까 큐일까? 이유는?
3. 에디터의 Ctrl+Z(실행 취소) — 스택일까 큐일까? 이유는?
4. 고객센터 상담 대기 순번 — 스택일까 큐일까? 이유는?
5. 함수가 함수를 호출하고, 안쪽 함수가 끝나야 바깥 함수로 돌아오는 구조 — 스택일까 큐일까? 이유는?</pre>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지 제출할 것</b></p>
<ul>
<li><code>stack.js</code> + <code>queue.js</code> (검증용 console.log 포함, 전부 실행돼야 해요)</li>
<li>주간 챌린지 리포트 1건 (판단문제 5개 답 + 배열 화면 3건 + 주간 회고 5줄 포함)</li>
</ul>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">예비 과제</div><h3>스택으로 괄호 검사기 만들기</h3></div>
<span class="diff d1">난이도 ★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic goal">🎯</span>목표</div>
<p class="goal-line">내가 만든 자료구조를 "문제 풀이 도구"로 써 보는 첫 경험이에요.</p>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>본 과제의 Stack 클래스를 재사용해 함수 <code>isBalanced(str)</code>를 만들어요. 문자열의 소괄호 <code>(</code> <code>)</code>가 짝이 맞으면 true, 아니면 false를 반환해요.</li>
<li>힌트: <code>(</code>를 만나면 push, <code>)</code>를 만나면 pop. pop할 게 없거나, 끝났는데 스택에 남아 있으면 false예요.</li>
<li>다음 5개 입력으로 검증해요: <code>"(())"</code> → true / <code>"(()"</code> → false / <code>"())("</code> → false / <code>"안녕(하세요)"</code> → true / <code>""</code> → true.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic extra"></span>제출</div>
<div class="extra-box">
<p><b><code>balance.js</code></b>(검증 5건 출력 포함)를 본 과제와 함께 제출해요.</p>
</div>
</div>
</div>
</div>
<div class="task">
<div class="t-head">
<div><div class="kind">디자이너 과제</div><h3>UI 개선점 5개 리포트 완성</h3></div>
<span class="diff d3">난이도 ★★★</span>
</div>
<div class="t-body">
<div class="block">
<div class="bt"><span class="ic dz">🎨</span>개발자 과제와의 차이</div>
<div class="dz-box">
<p><b>디자이너의 주간 통합은 "리포트 완성"이에요.</b> 월~목 인벤토리와 불일치 목록이 재료이고, 오늘은 그것을 남에게 보여줄 수 있는 한 편의 문서로 만들어요.</p>
</div>
</div>
<div class="block">
<div class="bt"><span class="ic step">📋</span>진행 순서</div>
<ol class="steps">
<li>어제 찾은 불일치 10개 중 <b>심각도가 높고 고치기 쉬운 것</b> 위주로 개선점 5개를 골라요.</li>
<li>각 개선점을 <b>현재(As-Is) 스크린샷 → 개선안(To-Be) 시안</b> 구성으로 만들어요. To-Be는 Figma로 간단히 그리면 돼요 — 완성도보다 "무엇이 달라지는지"가 보이면 충분해요.</li>
<li>각 개선점에 3줄을 붙여요: 문제 / 개선안 / 기대 효과(사용자 입장에서).</li>
<li>표지(제목·이름·날짜)와 마지막 장(이번 주 인벤토리 요약: 색 몇 개, 글자 크기 몇 종, 버튼 변형 몇 개)을 붙여 리포트를 완성해요.</li>
<li>16:30 리뷰 때 <b>5분 발표</b>로 공유할 수 있게 말할 순서를 머릿속으로 한 번 연습해요.</li>
</ol>
</div>
<div class="block">
<div class="bt"><span class="ic out">📤</span>산출물 · 제출</div>
<div class="out-box">
<p><b>16:30까지:</b></p>
<ul>
<li>UI 개선 리포트 1건 (개선점 5개 × As-Is/To-Be + 3줄 분석, 표지·요약 포함)</li>
<li>리뷰 시간 5분 발표</li>
</ul>
</div>
</div>
</div>
</div>
<!-- ===================== REVIEW GUIDE ===================== -->
<section id="review">
<div class="eyebrow">Mentor Guide</div>
<h2>멘토 리뷰 가이드 (16:3017:00)</h2>
<p class="sub">멘토용 참고 표예요. 학생도 읽어 두면 "리뷰 때 무엇을 물어볼지" 미리 준비할 수 있어요.</p>
<div class="card mt">
<div class="time">
<div class="row"><div class="t"></div><div class="v"><b>확인:</b> 구조도 10개 폴더에 역할이 다 적혔는가, (추측) 표시를 솔직하게 했는가. <b>질문:</b> "새 화면을 하나 추가한다면 어느 폴더에 파일을 만들 것 같아요?"</div></div>
<div class="row"><div class="t"></div><div class="v"><b>확인:</b> 5개 요청에 GET 외 메서드가 포함됐는가, 추측에 근거가 붙었는가. <b>질문:</b> "이 요청이 실패하면 사용자는 화면에서 뭘 보게 될까요?"</div></div>
<div class="row"><div class="t"></div><div class="v"><b>확인:</b> 프론트→백엔드 흐름이 파일·줄로 연결됐는가, ❓ 항목이 최소 2개 있는가. <b>질문:</b> "❓ 중 하나를 골라 지금 같이 열어 볼까요?" (즉석 해소 1건)</div></div>
<div class="row"><div class="t"></div><div class="v"><b>확인:</b> PR 3개가 템플릿을 지켰는가, 리뷰 코멘트에 질문/제안이 들어갔는가. <b>질문:</b> "리뷰 코멘트를 받았을 때 기분이 어땠어요? 어떤 코멘트가 좋은 코멘트일까요?"</div></div>
<div class="row"><div class="t"></div><div class="v"><b>확인:</b> 스택·큐가 빈 경우까지 검증됐는가, 배열 화면 3건이 응답 JSON과 연결됐는가. <b>질문:</b> "이번 주 배운 것 중 다음 주 코드 수정 때 바로 쓸 수 있는 건 뭐예요?" (디자이너는 5분 발표로 대체)</div></div>
</div>
</div>
</section>
<footer>
<div>AWESOMEDEV · 2주차 일일 과제집 (학생 배포용) · 4부작 중 2</div>
<div>2026 · 2주차 상세 강의안과 함께 사용</div>
</footer>
</div>
</body>
</html>