[HIGH] - 로그인 잠금 XFF 스푸핑 우회: X-Forwarded-For leftmost(위조 가능) → rightmost(Caddy가 붙인 실제 IP) 사용 - 멘토 기본 비밀번호 하드코딩 제거: env 미설정 시 SecureRandom 생성 후 로그 1회 출력. compose에 MENTOR_INITIAL_PASSWORD 추가 - 클릭 카드 키보드 접근 불가(학습문서 전체): Card에 role=button·tabIndex·Enter/Space·aria-label [MEDIUM] - CSV 수식 인젝션: =+-@ 로 시작하는 학생 자유입력 앞에 ' 붙여 엑셀 수식화 차단 (+회귀 테스트) - 학습문서 17개 <html lang=ko> 골격 주입 (스크린리더 한국어 발음) - 과제 제출 폼 label htmlFor/id 연결 - 상단 아이콘 링크(👤·🔑) aria-label + 이모지 aria-hidden - 멘토 제출물 표 행 키보드 접근(role/tabIndex/Enter·Space) - 배지 amber/teal 글자 대비 4.5:1 확보(테마별 --amber-text/--teal-text) [LOW] - 코드에디터 포커스 표시(:focus-within), 문제번호 색 대비(--muted), 카드 :focus-visible - 폼 오류 메시지 role=alert (스크린리더 즉시 안내) 검증: 테스트 26개 통과, 운영에서 XFF 우회 차단(6회째 429)·카드 role/aria·문서 lang=ko 확인. PMD 29건은 전부 저심각 스멜로 실질 결함 없음. 각 수정에 원리 학습 주석. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
860 lines
58 KiB
HTML
860 lines
58 KiB
HTML
<!doctype html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>어썸데브 수습 교육 · 2주차 일일 과제집</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: 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>코드 읽기 & 자료구조 적용</h1>
|
||
<p class="lede">이 과제집은 오후 자기주도 시간(10:40–12:00, 13:00–16: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:30–10:30</div><div class="v"><b>아침 강의(멘토)</b> — 이번 주는 자료구조(배열·리스트·스택·큐·해시·트리) + 요일별 주제 강의</div></div>
|
||
<div class="row"><div class="t">10:40–12:00</div><div class="v"><b>일일 과제 전반</b> — 과제집을 열고 그날의 본 과제를 시작해요</div></div>
|
||
<div class="row"><div class="t">13:00–16:30</div><div class="v"><b>일일 과제 후반</b> — 막히면 <b>30분 룰</b>: 30분 넘게 혼자 못 풀면 질문 메모를 적고 다음 단계로 넘어가요</div></div>
|
||
<div class="row"><div class="t">16:30–17:00</div><div class="v"><b>멘토 과제 리뷰</b> — 그날의 산출물을 제출하고 피드백을 받아요</div></div>
|
||
<div class="row"><div class="t">17:00–17: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:30–17: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>
|