Lecture Slides

바이브코딩 클래스 슬라이드 마스터

Marp 마크다운 원본. PDF는 현재 30MB Storage 제한을 초과해 HTML 피칭덱을 기준 산출물로 사용합니다.


marp: true theme: default paginate: true size: 16:9 backgroundColor: '#1D242B' header: '' footer: '세미클래스 · 바이브코딩으로 내 서비스 만들기' style: | @import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css'); :root { --blue: #068FFF; --blue2: #3BA8FF; --dark: #1D242B; --card: #252E37; --card2: #2E3842; --line: #3A4651; --muted: #8D9194; --text: #E8ECF0; --white: #FFFFFF; --yellow: #FFD166; --green: #69DB7C; --red: #FF6B6B; } section { font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, sans-serif; background: var(--dark); color: var(--text); padding: 48px 64px; font-size: 18px; letter-spacing: 0; } section::after, footer { color: rgba(255,255,255,.34); font-size: 13px; } h1 { margin: 0 0 14px; color: var(--white); font-size: 46px; line-height: 1.12; } h2 { margin: 0 0 18px; color: var(--blue2); font-size: 26px; line-height: 1.25; } h3 { margin: 0 0 10px; color: var(--white); font-size: 22px; line-height: 1.3; } p, li { color: var(--text); font-size: 18px; line-height: 1.45; } strong { color: var(--blue2); font-weight: 850; } em, .muted { color: var(--muted); font-style: normal; } small, .small { color: var(--muted); font-size: 13px; } code { color: var(--blue2); background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 2px 7px; } a { color: var(--blue2); text-decoration: none; }

section.title { display: flex; flex-direction: column; justify-content: center; text-align: center; background: radial-gradient(circle at 80% 18%, rgba(255,255,255,.14), transparent 26%), radial-gradient(circle at 12% 86%, rgba(255,255,255,.10), transparent 30%), var(--blue); } section.title h1 { font-size: 64px; } section.title h2, section.title p { color: rgba(255,255,255,.92); } section.title .sub { font-size: 22px; color: rgba(255,255,255,.85); margin-top: 8px; }

section.section { display: flex; flex-direction: column; justify-content: center; background: radial-gradient(circle at 50% 48%, rgba(6,143,255,.18), transparent 58%), var(--dark); } section.section h1 { font-size: 60px; } section.section h2 { text-transform: uppercase; font-size: 18px; letter-spacing: 0; color: var(--yellow); } section.section .sub { font-size: 22px; color: #C9D4DE; margin-top: 12px; max-width: 880px; }

section.chapter { background: linear-gradient(135deg, rgba(255,209,102,.18), transparent 62%), var(--dark); } section.chapter .ch-tag { display: inline-block; font-weight: 950; font-size: 14px; letter-spacing: 1px; padding: 6px 12px; border-radius: 999px; background: rgba(255,209,102,.16); color: #FFE7A0; border: 1px solid rgba(255,209,102,.45); margin-bottom: 18px; } section.chapter h1 { font-size: 56px; } section.chapter .ch-sub { font-size: 22px; color: #C9D4DE; margin-top: 10px; max-width: 920px; }

/* Chapter cover variant: text + element image side-by-side */ section.chapter.with-art { display: block; padding: 60px 80px; } .ch-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 44px; align-items: center; min-height: 540px; } .ch-grid .ch-text h1 { font-size: 50px; margin-top: 8px; } .ch-grid .ch-text .ch-sub { font-size: 20px; } .ch-grid .ch-art { display: flex; justify-content: center; align-items: center; } .ch-grid .ch-art img { width: 100%; max-width: 560px; border-radius: 16px; box-shadow: 0 22px 70px rgba(0,0,0,.45); border: 1px solid rgba(6,143,255,.3); image-rendering: pixelated; }

/* Intro character portrait (S01, S02 character area) */ .intro-character-img { width: 100%; max-width: 280px; border-radius: 16px; box-shadow: 0 18px 60px rgba(0,0,0,.45); border: 1px solid rgba(6,143,255,.32); image-rendering: pixelated; }

section.cta { display: flex; flex-direction: column; justify-content: center; text-align: center; background: radial-gradient(circle at 50% 50%, rgba(6,143,255,.22), transparent 60%), var(--dark); }

/* Full-image slide — image fills slide, optional small chapter tag overlay */ section.fullimg { padding: 0 !important; background-color: #000; position: relative; } section.fullimg .corner-tag { position: absolute; top: 22px; left: 28px; z-index: 5; font-size: 13px; font-weight: 900; letter-spacing: 1px; color: #FFE7A0; background: rgba(0,0,0,.5); border: 1px solid rgba(255,209,102,.4); padding: 6px 12px; border-radius: 999px; } section.fullimg .corner-tag.q { color: #BDE1FF; border-color: rgba(6,143,255,.5); background: rgba(6,143,255,.18); } section.fullimg .corner-tag.r { color: #B6F0C2; border-color: rgba(105,219,124,.5); background: rgba(105,219,124,.18); } section.fullimg .corner-tag.s { color: rgba(255,255,255,.86); border-color: rgba(255,255,255,.3); background: rgba(255,255,255,.08); }

/* Finale / epilogue overlay caption on fullimg */ section.fullimg .finale-overlay { position: absolute; left: 0; right: 0; bottom: 0; padding: 64px 80px 56px; background: linear-gradient(to top, rgba(10,16,24,.95) 0%, rgba(10,16,24,.72) 55%, transparent 100%); text-align: center; z-index: 4; } section.fullimg .finale-overlay h1 { font-size: 42px; line-height: 1.18; margin-bottom: 14px; color: var(--white); text-shadow: 0 2px 14px rgba(0,0,0,.7); } section.fullimg .finale-overlay h1 strong { color: #FFE7A0; } section.fullimg .finale-overlay p { font-size: 22px; color: rgba(255,255,255,.94); font-weight: 700; text-shadow: 0 2px 10px rgba(0,0,0,.6); margin: 0; }

/* Common pieces */ .lead { font-size: 24px; color: #C9D4DE; font-weight: 650; max-width: 940px; } .grid2 { display: grid; grid-template-columns: 1.2fr 1fr; gap: 28px; align-items: center; } .grid2-eq { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; } .grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } .grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; } .grid6 { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; } .card { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 20px; min-height: 120px; } .card.blue { border-color: rgba(6,143,255,.7); background: rgba(6,143,255,.12); } .card.yellow { border-color: rgba(255,209,102,.55); background: rgba(255,209,102,.10); } .card.red { border-color: rgba(255,107,107,.55); background: rgba(255,107,107,.10); } .tag { display: inline-block; padding: 5px 9px; border-radius: 999px; background: rgba(6,143,255,.18); color: #BDE1FF; font-size: 13px; font-weight: 900; margin-bottom: 10px; } table { width: 100%; border-collapse: collapse; margin-top: 18px; font-size: 17px; } th, td { text-align: left; padding: 14px 14px; border-bottom: 1px solid var(--line); } th { color: var(--white); background: var(--card); } td { color: var(--text); }

.frame-note { margin-top: 16px; padding: 12px 14px; border-left: 4px solid var(--blue); background: rgba(6,143,255,.10); border-radius: 10px; font-size: 15px; color: #C9D4DE; } .tool-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; } .tool-link { display: inline-flex; align-items: center; gap: 8px; border: 1px solid rgba(6,143,255,.55); border-radius: 999px; padding: 9px 13px; background: rgba(6,143,255,.10); color: #BDE1FF; font-size: 14px; font-weight: 800; } .tool-link.secondary { border-color: rgba(255,255,255,.24); background: rgba(255,255,255,.06); color: rgba(255,255,255,.82); } section.loop-brief { display: flex; flex-direction: column; justify-content: center; } section.loop-brief .brief-kicker { display: inline-flex; align-items: center; gap: 8px; width: fit-content; margin-bottom: 18px; padding: 7px 12px; border-radius: 999px; color: #BDE1FF; background: rgba(6,143,255,.14); border: 1px solid rgba(6,143,255,.38); font-size: 13px; font-weight: 900; } section.loop-brief.state .brief-kicker { color: rgba(255,255,255,.86); background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.24); } section.loop-brief.problem .brief-kicker { color: #FFD6D6; background: rgba(255,107,107,.12); border-color: rgba(255,107,107,.34); } section.loop-brief.result .brief-kicker { color: #D4F8DA; background: rgba(105,219,124,.12); border-color: rgba(105,219,124,.34); } section.loop-brief .brief-card { width: 100%; max-width: 1180px; box-sizing: border-box; padding: 18px 10px; display: grid; grid-template-columns: minmax(0, 1fr) 390px; gap: 54px; align-items: center; } section.loop-brief .brief-card .brief-text { min-width: 0; } section.loop-brief .brief-card h1 { font-size: 46px; margin-bottom: 22px; } section.loop-brief .brief-card p { font-size: 23px; color: #D5DEE7; line-height: 1.45; margin: 0; font-weight: 650; } section.loop-brief .brief-card p + p { margin-top: 18px; } section.loop-brief .brief-card strong { color: var(--blue2); font-weight: 850; } section.loop-brief.problem .brief-card strong { color: #FFB4B4; } section.loop-brief.result .brief-card strong { color: #9FECAA; }

section.loop-brief .brief-visual { position: relative; padding: 12px; border-radius: 20px; background: linear-gradient(145deg, rgba(6,143,255,.16), rgba(255,255,255,.035)); border: 1px solid rgba(6,143,255,.38); box-shadow: 0 24px 70px rgba(0,0,0,.32); text-align: center; min-height: 360px; overflow: hidden; } section.loop-brief.state .brief-visual { background: linear-gradient(145deg, rgba(255,255,255,.10), rgba(255,255,255,.035)); border-color: rgba(255,255,255,.22); } section.loop-brief.problem .brief-visual { background: linear-gradient(145deg, rgba(255,107,107,.16), rgba(255,255,255,.035)); border-color: rgba(255,107,107,.34); } section.loop-brief.result .brief-visual { background: linear-gradient(145deg, rgba(105,219,124,.16), rgba(255,255,255,.035)); border-color: rgba(105,219,124,.34); } section.loop-brief .brief-visual::after { content: ""; position: absolute; inset: auto 18px 18px 18px; height: 28%; background: linear-gradient(to top, rgba(29,36,43,.82), transparent); pointer-events: none; } section.loop-brief .brief-visual .brief-infographic { width: 100%; height: 336px; object-fit: cover; border-radius: 14px; display: block; } section.loop-brief .brief-visual .visual-caption { position: absolute; left: 26px; right: 26px; bottom: 28px; z-index: 2; } section.loop-brief .brief-visual .visual-icon { position: relative; width: 92px; height: 92px; display: flex; align-items: center; justify-content: center; } section.loop-brief .brief-visual .visual-icon img { width: 92px; height: 92px; } section.loop-brief .brief-visual .visual-label { font-size: 18px; font-weight: 900; color: var(--white); line-height: 1.3; } section.loop-brief.state .brief-visual .visual-label { color: rgba(255,255,255,.95); } section.loop-brief.problem .brief-visual .visual-label { color: #FFD0D0; } section.loop-brief.result .brief-visual .visual-label { color: #C9F2D0; } section.loop-brief .brief-visual .visual-sub { font-size: 13px; color: var(--muted); line-height: 1.45; }

/* Quest/trial map (for overview slide) */ .questmap { display: grid; grid-template-columns: 110px repeat(7, 1fr); gap: 10px; margin-top: 10px; } .questmap .qbox { border-radius: 12px; padding: 12px 10px; background: rgba(255,255,255,.04); border: 1px solid var(--line); min-height: 100px; display: flex; flex-direction: column; gap: 6px; } .questmap .qbox.ritual { background: rgba(255,209,102,.12); border-color: rgba(255,209,102,.45); } .questmap .qbox.trial { background: rgba(6,143,255,.10); border-color: rgba(6,143,255,.42); } .questmap .qbox.fast { background: rgba(255,255,255,.04); border-style: dashed; } .questmap .qhead { font-size: 11px; font-weight: 950; letter-spacing: .8px; color: var(--muted); } .questmap .qtitle { font-size: 14px; font-weight: 900; color: var(--white); line-height: 1.25; } .questmap .qartifact { margin-top: auto; font-size: 12px; color: var(--yellow); font-weight: 800; } .questmap .qbox-thumb { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; border-radius: 6px; border: 1px solid rgba(255,255,255,.12); margin-bottom: 6px; image-rendering: pixelated; } .questmap .qbox-character { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 8px; border: 1px solid rgba(6,143,255,.32); image-rendering: pixelated; }

/* Roadmap layout (S03 redesign) */ section.has-roadmap { padding: 36px 48px; } section.has-roadmap h1 { font-size: 38px; margin-bottom: 10px; } .roadmap-grid { display: grid; grid-template-columns: 116px repeat(3, 1fr); grid-template-rows: 1fr 1fr; gap: 28px 38px; margin-top: 8px; padding: 14px 8px 6px; } .roadmap-char { grid-row: 1 / 3; grid-column: 1; align-self: center; text-align: center; } .roadmap-char img { width: 100%; max-width: 116px; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 12px; border: 1px solid rgba(6,143,255,.4); box-shadow: 0 10px 26px rgba(0,0,0,.4); image-rendering: pixelated; } .roadmap-char .rchar-label { margin-top: 6px; font-size: 12px; color: var(--muted); font-weight: 800; } .rstop { position: relative; background: rgba(255,255,255,.04); border: 1px solid var(--line); border-radius: 12px; padding: 8px 10px 10px; display: flex; flex-direction: column; gap: 3px; min-width: 0; } .rstop.ritual { background: rgba(255,209,102,.10); border-color: rgba(255,209,102,.45); } .rstop.trial { background: rgba(6,143,255,.10); border-color: rgba(6,143,255,.42); } .rstop.fast { background: rgba(255,255,255,.04); border-style: dashed; } .rstop .rnum { position: absolute; top: -11px; left: 10px; width: 26px; height: 26px; border-radius: 50%; background: var(--blue); color: var(--white); display: flex; align-items: center; justify-content: center; font-weight: 950; font-size: 12px; box-shadow: 0 3px 10px rgba(6,143,255,.5); z-index: 2; } .rstop.ritual .rnum { background: #FFD166; color: #1D242B; box-shadow: 0 3px 10px rgba(255,209,102,.5); } .rstop.fast .rnum { background: #8D9194; } .rstop-thumb { width: 100%; height: 108px; object-fit: cover; border-radius: 6px; border: 1px solid rgba(255,255,255,.1); image-rendering: pixelated; } .rstop .rhead { font-size: 10px; font-weight: 900; letter-spacing: .6px; color: var(--muted); margin-top: 4px; } .rstop .rtitle { font-size: 14px; font-weight: 900; color: var(--white); line-height: 1.18; } .rstop .rartifact { font-size: 11px; color: var(--yellow); font-weight: 800; margin-top: auto; }

/* Arrows between roadmap stops */ .rstop::after { position: absolute; font-size: 22px; font-weight: 900; color: var(--blue2); text-shadow: 0 2px 6px rgba(0,0,0,.5); z-index: 1; line-height: 1; } .rstop[data-arrow="right"]::after { content: "→"; right: -28px; top: 50%; transform: translateY(-50%); } .rstop[data-arrow="down"]::after { content: "↓"; bottom: -26px; left: 50%; transform: translateX(-50%); } .rstop[data-arrow="left"]::after { content: "←"; left: -28px; top: 50%; transform: translateY(-50%); } .rstop[data-arrow="none"]::after { content: ""; }

/* Pixel character + artifact badges (S01 + S46) */ .pixel { position: relative; width: 78px; height: 108px; image-rendering: pixelated; } .pixel span { position: absolute; display: block; border-radius: 2px; } .pixel .head { left: 24px; top: 0; width: 30px; height: 30px; background: #D9E8F4; box-shadow: 8px 8px 0 #091018, -8px 8px 0 #091018; } .pixel .body { left: 18px; top: 36px; width: 42px; height: 38px; background: linear-gradient(180deg, var(--blue), #125C9B); box-shadow: -15px 8px 0 #1A74C8, 15px 8px 0 #1A74C8; } .pixel .legs { left: 24px; top: 76px; width: 14px; height: 28px; background: #9AA8B8; box-shadow: 18px 0 0 #9AA8B8; } .artifact { display: inline-flex; align-items: center; justify-content: center; min-width: 50px; height: 38px; border-radius: 9px; padding: 0 12px; color: var(--yellow); background: rgba(255,209,102,.14); border: 1px solid rgba(255,209,102,.4); font-weight: 950; font-size: 14px; letter-spacing: .5px; } .artifact.full { color: #FFE7A0; background: rgba(255,209,102,.22); border-color: rgba(255,209,102,.65); box-shadow: 0 0 18px rgba(255,209,102,.32); }

@keyframes pop { 0% { opacity: 0; transform: scale(.6); } 60% { opacity: 1; transform: scale(1.16); } 100% { opacity: 1; transform: scale(1); } } @keyframes glow { 0%, 100% { box-shadow: 0 0 14px rgba(255,209,102,.20); } 50% { box-shadow: 0 0 22px rgba(255,209,102,.55); } } .title-stage { display: flex; flex-direction: column; align-items: center; gap: 22px; margin: 28px 0 20px; } .title-stage .pixel { animation: pop .6s ease-out both; } .title-badges { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; } .title-badges .artifact { opacity: 0; animation: pop .55s ease-out forwards, glow 2.4s ease-in-out infinite; } .title-badges .artifact:nth-child(1) { animation-delay: .6s, 3.0s; } .title-badges .artifact:nth-child(2) { animation-delay: .9s, 3.0s; } .title-badges .artifact:nth-child(3) { animation-delay: 1.2s, 3.0s; } .title-badges .artifact:nth-child(4) { animation-delay: 1.5s, 3.0s; } .title-badges .artifact:nth-child(5) { animation-delay: 1.8s, 3.0s; } .title-badges .artifact:nth-child(6) { animation-delay: 2.1s, 3.0s; }

.cta-button { display: inline-flex; align-items: center; justify-content: center; padding: 18px 36px; border-radius: 14px; background: var(--blue); color: var(--white); font-size: 22px; font-weight: 900; border: 1px solid rgba(255,255,255,.18); box-shadow: 0 12px 30px rgba(6,143,255,.35); margin-top: 18px; } .cta-meta { color: var(--muted); font-size: 14px; margin-top: 14px; letter-spacing: .5px; }

<!-- _class: title --> <div style="font-weight:900;color:rgba(255,255,255,.78);margin-bottom:22px;letter-spacing:1px">SEMICLASS · 비정기 클래스</div>

토요일 2시간,<br /><strong style="color:#FFE7A0">내 일에 쓰는 한 조각</strong>

<div class="sub">단발로 와서 손에 쥐고 가는 바이브코딩 한 회</div> <div class="title-stage"> <img class="intro-character-img" src="assets/intro/intro-beginner-character.png" alt="초보 바이브코딩 모험가" /> <div class="title-badges"> <span class="artifact">DATA</span> <span class="artifact">DB</span> <span class="artifact">URL</span> <span class="artifact">SIGNAL</span> <span class="artifact">SEAL</span> <span class="artifact">CAPE</span> </div> </div>

여러분은 오늘 <strong>초보 바이브코딩 모험가</strong>입니다

<div class="grid2"> <div> <p class="lead">오늘 강사는 바이브코딩으로 서비스를 만들며 겪는 <strong>여러 시련</strong>을 여러분에게 던집니다. 그 시련을 하나씩 넘으며, 끝나는 시점에 어엿한 <strong>바이브코딩 용사</strong>로 거듭납니다.</p> <ul> <li>강사는 시련을 던지는 사람</li> <li>여러분은 한 조각씩, <strong>아티팩트라는 이름의 모험을 도와줄 보물들</strong>을 얻는 사람</li> <li>2시간 후 손에 남는 건 — 내 일에 끼워넣을 한 조각</li> </ul> </div> <div style="display:flex;flex-direction:column;align-items:center;gap:12px"> <img class="intro-character-img" src="assets/intro/intro-beginner-character.png" alt="초보 바이브코딩 모험가" /> </div> </div>
<!-- _class: has-roadmap -->

오늘의 <strong>여정 한눈에</strong>

<div class="roadmap-grid"> <div class="roadmap-char"> <img src="assets/intro/intro-beginner-character.png" alt="초보 모험가" /> <div class="rchar-label">초보 모험가</div> </div> <div class="rstop ritual" data-arrow="right"> <span class="rnum">1</span> <img class="rstop-thumb" src="assets/intro/ch1-ritual-runes.png" alt="" /> <div class="rhead">CH 1 · 의식</div> <div class="rtitle">준비의 시간</div> <div class="rartifact">나침반 · 룬</div> </div> <div class="rstop trial" data-arrow="right"> <span class="rnum">2</span> <img class="rstop-thumb" src="assets/intro/ch2-empty-data-crystal.png" alt="" /> <div class="rhead">CH 2 · 시련</div> <div class="rtitle">공허의 화면</div> <div class="rartifact">데이터 크리스탈</div> </div> <div class="rstop trial" data-arrow="down"> <span class="rnum">3</span> <img class="rstop-thumb" src="assets/intro/ch3-forgetting-db-core.png" alt="" /> <div class="rhead">CH 3 · 시련</div> <div class="rtitle">망각의 몬스터</div> <div class="rartifact">DB 코어</div> </div> <div class="rstop fast" data-arrow="none"> <span class="rnum">6</span> <img class="rstop-thumb" src="assets/intro/ch6-nameless-identity-seal.png" alt="" /> <div class="rhead">CH 6 · 시연</div> <div class="rtitle">무명의 안개</div> <div class="rartifact">신원 인장</div> </div> <div class="rstop fast" data-arrow="left"> <span class="rnum">5</span> <img class="rstop-thumb" src="assets/intro/ch5-api-signal-sword.png" alt="" /> <div class="rhead">CH 5 · 시연</div> <div class="rtitle">외계의 신호</div> <div class="rartifact">신호의 검</div> </div> <div class="rstop trial" data-arrow="left"> <span class="rnum">4</span> <img class="rstop-thumb" src="assets/intro/ch4-isolation-deployment-boots.png" alt="" /> <div class="rhead">CH 4 · 시련</div> <div class="rtitle">고립의 장벽</div> <div class="rartifact">배포 부츠</div> </div> </div> <p class="small" style="margin-top:14px">앞 50분은 강사가 6개 아티팩트를 모두 압축 시연합니다. 후반 50분은 사전 제출한 아이디어를 각자 원하는 서비스로 바꾸는 실습입니다.</p>

오늘 만들 결과물

<div class="grid2"> <div> <p class="lead">앞 50분에는 강사가 <strong>우리동네 공공데이터 보드</strong>를 끝까지 시연하고, 후반에는 여러분 아이디어에 같은 제작 루프를 적용합니다.</p> <ul> <li>강사 데모: 카드 보드 · DB 저장 · 배포 · 모바일 확인</li> <li>개인 실습: 내 서비스 한 장 명세</li> <li>개인 실습: 첫 화면 프롬프트</li> <li>개인 실습: 필요한 아티팩트 목록</li> </ul> <p class="small" style="margin-top:14px">만들고 싶은 서비스는 수업 전 세미클래스 봇이나 소개사이트로 미리 받습니다.</p> </div> <div> <h3>후반 실습에서 바꿔볼 수 있는 방향</h3> <ul> <li>고객 문의를 자동으로 분류하는 <strong>상담 보드</strong></li> <li>행사 신청자를 상태별로 관리하는 <strong>운영 보드</strong></li> <li>경쟁사·매장 정보를 모으는 <strong>리서치 보드</strong></li> <li>사내 양식·업무 템플릿 <strong>한 페이지 도구</strong></li> </ul> </div> </div>

AI는 가끔 틀려요 — <strong>모험가의 회복 카드</strong>

<div class="grid3"> <div class="card blue"><span class="tag">1</span><h3>에러 그대로 복사</h3><p>화면 빨간 줄을 그대로 붙여 넣고 <em>"이 에러 났어, 고쳐줘"</em>로 다시 부탁합니다.</p></div> <div class="card blue"><span class="tag">2</span><h3>강사 화면 따라가기</h3><p>10분 이상 막히면 강사 화면을 보고 흐름만 따라옵니다. 결과물은 다음 단계에서 합칩니다.</p></div> <div class="card yellow"><span class="tag">3</span><h3>스냅샷 점프</h3><p>각 챕터 끝마다 <code>snapshots/</code>로 단계 점프 가능. 못 따라간 시련은 다음 시련에서 복구됩니다.</p></div> </div> <p class="small">막히는 건 정상입니다. 오늘 목표는 “완벽한 코드”가 아니라 “공개 URL이 열리는 것”입니다.</p>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 1 · 의식</div>

준비의 시간

<p class="ch-sub">본격적으로 바이브코딩에 들어가기 앞서, <strong>두 가지 의식</strong>을 거칩니다. 시련의 무게를 미리 줄여주는 사전 아티팩트입니다.</p> </div> <div class="ch-art"> <img src="assets/intro/ch1-ritual-runes.png" alt="기획의 나침반·디자인 룬" /> </div> </div>
<!-- _class: fullimg -->

bg


의식 1 · <strong>기획의 나침반</strong>

<div class="grid2"> <div> <h2>막막함을 한 장으로</h2> <p class="lead">만들 것을 3줄로 적습니다. 이 한 장이 이후 모든 프롬프트의 뼈대가 됩니다.</p> </div> <div> <h3>오늘 손에 쥐는 산출물</h3> <ul> <li><strong>목적 한 줄</strong> — 누구의 어떤 문제를 푸는가</li> <li><strong>사용자 한 줄</strong> — 누가 쓰는가</li> <li><strong>핵심 기능 한 줄</strong> — 그 사람이 뭘 할 수 있는가</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://manyfast.io/" target="_blank" rel="noopener noreferrer">매니페스트</a> </div> <p class="small" style="margin-top:10px">강사가 먼저 데모로 보여주고, 자기 케이스는 후반 실습에서 바꿔 씁니다.</p> </div> </div>
<!-- _class: fullimg -->

bg


의식 2 · <strong>디자인의 룬</strong>

<div class="grid2"> <div> <h2>민낯을 토큰으로</h2> <p class="lead">색 1개 · 폰트 1개 · 둥글기 1개. 그게 다입니다. 이 세 개가 모든 화면의 기준이 됩니다.</p> </div> <div> <h3>이 작은 결정이 만드는 차이</h3> <ul> <li>“못생기지 않은” 화면이 1분 안에</li> <li>이후 모든 추가 화면에 자동 적용</li> <li>AI 프롬프트에 색·폰트를 다시 지시할 필요 없음</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://claude.ai/design" target="_blank" rel="noopener noreferrer">Claude Design</a> <a class="tool-link secondary" href="https://claude.ai/" target="_blank" rel="noopener noreferrer">Claude</a> </div> </div> </div>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 2 · 시련</div>

공허의 화면

<p class="ch-sub">화면은 떴는데, 채울 것이 없는 그 첫 순간. 첫 시련을 넘으면 <strong>데이터 크리스탈</strong>이 손에 들어옵니다.</p> </div> <div class="ch-art"> <img src="assets/intro/ch2-empty-data-crystal.png" alt="공허의 화면 → 데이터 크리스탈" /> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 2 · 관찰</div> <div class="brief-card"> <div class="brief-text">

AI가 화면을 만들어줬어요

<p>뭔가 정보도 넣어준 것 같은데, 이건 <strong>정말 우리가 바꿔 쓸 수 있는 데이터</strong>일까요?</p> <p>한번 정보를 추가하거나 바꿔보면 어떨까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch2-state.png" alt="화면 안에 데이터가 갇힌 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">화면은 있는데</div> <div class="visual-sub">데이터가 <strong>코드 안에 박혀</strong> 있어요</div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 2 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

데이터가 분리되지 않으면

<p>나중에 <strong>실제 데이터로 전환하기 어렵습니다</strong>.</p> <p><strong>코드와 데이터를 분리</strong>해야 샘플이든 공공데이터든 갈아끼울 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch2-problem.png" alt="코드와 데이터가 엉켜 교체하기 어려운 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">코드 ≡ 데이터</div> <div class="visual-sub">샘플도, 진짜도 <strong>못 갈아끼움</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 2 · 해결</div> <div class="brief-card"> <div class="brief-text">

코드와 데이터를 분리했습니다

<p>이제 같은 화면에 <strong>다른 데이터를 넣어보는 테스트</strong>가 쉬워졌습니다.</p> <p>추후 <strong>실제 데이터 연동</strong>으로 넘어갈 준비도 된 상태입니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch2-result.png" alt="데이터 크리스탈로 코드와 데이터가 분리된 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">code ⇄ data</div> <div class="visual-sub"><strong>어떤 데이터든</strong> 갈아끼우기 OK</div> </div> </div> </div>

아티팩트 · <strong>데이터 크리스탈</strong>

<div class="grid2"> <div> <h2>코드와 데이터를 분리합니다</h2> <p class="lead">코드 안에 박힌 샘플 배열을 <code>public/sample-public-data.json</code>으로 빼냅니다. 공공데이터와 같은 구조로.</p> </div> <div> <h3>오늘 손에 쥐는 것</h3> <ul> <li>공공데이터 구조 JSON 1 파일</li> <li><code>fetchPublicItems()</code> 함수 — 나중에 진짜 API로 갈아끼우기 쉬운 구조</li> <li>같은 보드, 다른 데이터 소스로 즉시 갈아낄 수 있는 패턴</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://github.com/" target="_blank" rel="noopener noreferrer">GitHub</a> </div> </div> </div>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 3 · 시련</div>

망각의 몬스터

<p class="ch-sub">저장 버튼은 동작하는데, 새로고침하면 사라지는 그 순간. 두 번째 시련을 넘으면 <strong>DB 코어</strong>가 손에 들어옵니다.</p> </div> <div class="ch-art"> <img src="assets/intro/ch3-forgetting-db-core.png" alt="망각의 몬스터 → DB 코어" /> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 3 · 관찰</div> <div class="brief-card"> <div class="brief-text">

저장 버튼도 생겼어요

<p>관심 항목을 눌러두면 <strong>화면에서는 잘 남아 있는 것처럼</strong> 보입니다.</p> <p>그런데 <strong>새로고침하거나 다시 들어와도</strong> 그대로 남아 있을까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch3-state.png" alt="저장 버튼이 동작하는 것처럼 보이는 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">잘 저장돼요</div> <div class="visual-sub">…정말 그럴까요?</div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 3 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

임시 상태만 쓰면

<p><strong>새로고침 순간</strong> 저장한 내용이 사라질 수 있습니다.</p> <p>사용자의 선택을 <strong>데이터베이스에 저장</strong>해야 다음 접속에도 이어집니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch3-problem.png" alt="새로고침으로 임시 저장 내용이 사라지는 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">새로고침 = 사라짐</div> <div class="visual-sub"><strong>임시 메모리</strong>에만 남아 있음</div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 3 · 해결</div> <div class="brief-card"> <div class="brief-text">

저장한 내용이 남습니다

<p>관심 항목이 <strong>데이터베이스에 기록</strong>되도록 바뀌었습니다.</p> <p>이제 사용자가 <strong>다시 들어와도 자기 작업을 이어갈 수</strong> 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch3-result.png" alt="DB 코어에 저장 내용이 남는 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">DB에 영구 저장</div> <div class="visual-sub">다시 와도 <strong>그대로</strong></div> </div> </div> </div>

아티팩트 · <strong>DB 코어</strong>

<div class="grid2"> <div> <h2>다시 들어와도 그대로</h2> <p class="lead">Supabase 테이블에 관심 항목을 저장합니다. 새로고침해도, 다음 접속에도 그대로 살아 있습니다.</p> </div> <div> <h3>오늘 손에 쥐는 것</h3> <ul> <li>Supabase 프로젝트 + 테이블 1 개 (<code>saved_public_items</code>)</li> <li>저장 / 불러오기 흐름</li> <li>환경변수로 키 분리 — 키가 클라이언트에 노출되지 않게</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://supabase.com/" target="_blank" rel="noopener noreferrer">Supabase</a> </div> </div> </div>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 4 · 시련</div>

고립의 장벽

<p class="ch-sub">내 컴퓨터에서는 잘 되는데, 친구 휴대폰에선 안 열리는 그 순간. 세 번째 시련을 넘으면 <strong>배포 부츠</strong>가 손에 들어옵니다.</p> </div> <div class="ch-art"> <img src="assets/intro/ch4-isolation-deployment-boots.png" alt="고립의 장벽 → 배포 부츠" /> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 4 · 관찰</div> <div class="brief-card"> <div class="brief-text">

내 컴퓨터에서는 잘 돌아가요

<p>데이터도 남고 <strong>화면도 동작</strong>합니다.</p> <p>이제 <strong>친구나 팀원에게 보여주려면</strong> 어떻게 하면 될까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch4-state.png" alt="내 컴퓨터 안에서만 열리는 서비스 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">내 PC만 OK</div> <div class="visual-sub">…남에게 보여주려면?</div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 4 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

내 컴퓨터에서만 열리면

<p>아직 <strong>다른 사람이 쓸 수 있는 서비스가 아닙니다</strong>.</p> <p><strong>서버에 배포</strong>해야 누구나 같은 주소로 접속할 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch4-problem.png" alt="localhost 장벽으로 다른 기기에서 접속하지 못하는 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">나만 접속 가능</div> <div class="visual-sub">PC 끄면 <strong>끝</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 4 · 해결</div> <div class="brief-card"> <div class="brief-text">

공개 주소가 생겼습니다

<p>이제 <strong>내 컴퓨터를 켜두지 않아도</strong> 웹에서 바로 열립니다.</p> <p>친구, 팀원, 고객에게 <strong>링크 한 줄</strong>로 보여줄 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch4-result.png" alt="배포 후 공개 URL로 여러 기기가 접속하는 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">공개 URL</div> <div class="visual-sub"><strong>링크 한 줄</strong>로 누구나 접속</div> </div> </div> </div>

아티팩트 · <strong>배포 부츠</strong>

<div class="grid2"> <div> <h2>링크 한 줄로 누구든 들어옵니다</h2> <p class="lead">GitHub → Vercel을 잇고, 공개 URL이 발급됩니다. 휴대폰에서, 친구 노트북에서 그대로 열립니다.</p> </div> <div> <h3>오늘 손에 쥐는 것</h3> <ul> <li><code>https://*.vercel.app</code> 공개 URL</li> <li>코드 푸시 → 자동 빌드 → 자동 배포 흐름</li> <li>환경변수를 Vercel 쪽에도 옮기는 패턴</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://vercel.com/" target="_blank" rel="noopener noreferrer">Vercel</a> <a class="tool-link secondary" href="https://github.com/" target="_blank" rel="noopener noreferrer">GitHub</a> </div> </div> </div>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 5 · FAST-FORWARD</div>

외계의 신호

<p class="ch-sub">샘플 데이터를 <strong>진짜 공공데이터</strong>로 바꾸는 순간. 시연으로 함께 보고, 풀 코드는 사후 자료로 갑니다.</p> </div> <div class="ch-art"> <img src="assets/intro/ch5-api-signal-sword.png" alt="외계의 신호 → 신호의 검" /> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 5 · 관찰</div> <div class="brief-card"> <div class="brief-text">

샘플 데이터로 흐름은 확인했어요

<p>목록도 뜨고 <strong>상세도 볼 수 있습니다</strong>.</p> <p>그런데 매번 직접 고치지 않고, <strong>바깥의 최신 정보</strong>를 가져올 수 있을까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch5-state.png" alt="샘플 데이터로 흐름만 확인한 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">샘플 데이터</div> <div class="visual-sub">흐름은 OK, …<strong>실제 데이터는?</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 5 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

외부 데이터는 그냥 붙지 않습니다

<p><strong>요청 주소, 인증키, 응답 형태</strong>가 맞아야 우리 서비스 안으로 들어옵니다.</p> <p><strong>API 연결 구조</strong>를 잡아야 실제 데이터 흐름을 받아올 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch5-problem.png" alt="API 키 주소 포맷 장벽 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">키·주소·포맷</div> <div class="visual-sub">맞춰야 <strong>들어옵니다</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 5 · 해결</div> <div class="brief-card"> <div class="brief-text">

외부 데이터 흐름을 연결했습니다

<p>정적인 샘플을 넘어 <strong>실제로 바뀌는 정보</strong>를 서비스에 반영할 수 있습니다.</p> <p>이제 <strong>날씨, 장소, 상품, 예약</strong> 같은 외부 정보도 같은 방식으로 붙일 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch5-result.png" alt="신호의 검으로 외부 데이터를 서비스 카드로 바꾸는 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">실시간 외부 데이터</div> <div class="visual-sub">날씨·장소·상품·예약 <strong>전부 OK</strong></div> </div> </div> </div>

아티팩트 · <strong>신호의 검</strong>

<div class="grid2"> <div> <h2>외부 API에 직접 닿습니다</h2> <p class="lead">정적 JSON을 외부 공공데이터 API 호출로 갈아끼웁니다. 키·CORS·응답 포맷의 변수까지.</p> </div> <div> <h3>이 챕터에서 보는 것</h3> <ul> <li>한 줄을 갈아끼우면 진짜 데이터가 흐릅니다</li> <li>API 키 발급·관리·환경변수 패턴</li> <li>응답 포맷 차이를 흡수하는 어댑터 패턴</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://www.data.go.kr/" target="_blank" rel="noopener noreferrer">공공데이터포털</a> <a class="tool-link secondary" href="https://vercel.com/" target="_blank" rel="noopener noreferrer">Vercel Route</a> </div> <p class="small" style="margin-top:10px">실제 손 작업은 사후 자료에서.</p> </div> </div>
<!-- _class: chapter with-art --> <div class="ch-grid"> <div class="ch-text"> <div class="ch-tag">CHAPTER 6 · FAST-FORWARD</div>

무명의 안개

<p class="ch-sub">여럿이 쓰면서 누가 누구 건지 흐려지는 순간. 시연으로 같이 보는 <strong>신원 인장</strong>의 작동.</p> </div> <div class="ch-art"> <img src="assets/intro/ch6-nameless-identity-seal.png" alt="무명의 안개 → 신원 인장" /> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 6 · 관찰</div> <div class="brief-card"> <div class="brief-text">

여러 사람이 같이 쓰기 시작했어요

<p><strong>각자 저장하고 싶은 항목</strong>도 생깁니다.</p> <p>그런데 이 서비스는 <strong>누가 누구인지 구분</strong>할 수 있을까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch6-state.png" alt="여러 사용자가 같은 서비스를 쓰기 시작한 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">여러 사용자</div> <div class="visual-sub">…<strong>누가 누구?</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 6 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

사용자를 구분하지 않으면

<p>저장한 데이터가 <strong>섞이거나 다른 사람에게 보일 수</strong> 있습니다.</p> <p><strong>로그인과 권한 규칙</strong>을 붙여야 내 것만 안전하게 다룰 수 있습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch6-problem.png" alt="사용자 데이터가 섞이는 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">데이터 섞임</div> <div class="visual-sub">남에게 <strong>보일 수도</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 6 · 해결</div> <div class="brief-card"> <div class="brief-text">

사용자별 공간이 생겼습니다

<p>같은 서비스를 여러 사람이 써도 <strong>각자의 데이터가 분리</strong>됩니다.</p> <p>이제 <strong>팀용 도구나 고객용 서비스로 확장</strong>할 수 있는 기본 안전장치가 생겼습니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch6-result.png" alt="신원 인장으로 사용자별 공간이 분리된 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">내 것만 또렷이</div> <div class="visual-sub"><strong>사용자별</strong> 데이터 분리</div> </div> </div> </div>

아티팩트 · <strong>신원 인장</strong>

<div class="grid2"> <div> <h2>내 것만 또렷이 보입니다</h2> <p class="lead">로그인 + 권한 정책(RLS)이 카드를 사용자별로 갈라줍니다. 같이 써도 섞이지 않습니다.</p> </div> <div> <h3>이 챕터에서 보는 것</h3> <ul> <li>이메일·OAuth 로그인 1줄로 붙이기</li> <li>저장 항목에 <code>owner_id</code> 추가</li> <li>“내 것만 보이게” RLS 정책 한 장</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://supabase.com/" target="_blank" rel="noopener noreferrer">Supabase Auth</a> </div> </div> </div>
<!-- _class: chapter --> <div class="ch-tag">CHAPTER 7 · FAST-FORWARD</div>

손바닥 미로

<p class="ch-sub">휴대폰에서 손가락이 길을 잃고, 다시 찾아오게 할 신호도 없는 순간. 마지막 아티팩트 <strong>모바일 망토</strong>가 모바일 경험과 푸시 알림을 마감합니다.</p>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief state --> <div class="brief-kicker">CH 7 · 관찰</div> <div class="brief-card"> <div class="brief-text">

휴대폰에서도 열리긴 해요

<p>하지만 <strong>작은 화면에서 버튼과 목록</strong>을 편하게 다룰 수 있을까요?</p> <p><strong>새 알림이 생겼을 때</strong> 다시 사용자를 부를 수 있을까요?</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch7-state.png" alt="데스크톱 화면이 휴대폰에 맞지 않게 열린 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">모바일에서 열림</div> <div class="visual-sub">…<strong>편하긴 한가?</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief problem --> <div class="brief-kicker">CH 7 · 원인과 처방</div> <div class="brief-card"> <div class="brief-text">

데스크탑 기준 화면은

<p>휴대폰에서 <strong>금방 불편해집니다</strong>.</p> <p><strong>반응형 화면과 앱처럼 여는 흐름</strong>을 갖춰야 모바일에서도 계속 쓰게 됩니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch7-problem.png" alt="휴대폰 터치와 알림 흐름이 불편한 상태 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">PC 기준 = 모바일 불편</div> <div class="visual-sub">한 번 쓰고 <strong>다시 안 옴</strong></div> </div> </div> </div>
<!-- _class: fullimg -->

bg


<!-- _class: loop-brief result --> <div class="brief-kicker">CH 7 · 해결</div> <div class="brief-card"> <div class="brief-text">

모바일에서도 편하게 씁니다

<p><strong>Tailwind로 모바일 레이아웃</strong>을 정리하고, <strong>FCM으로 푸시 알림</strong> 흐름을 붙입니다.</p> <p>이제 직접 만든 웹서비스가 <strong>일상에서 다시 열리는 도구</strong>에 가까워집니다.</p> </div> <div class="brief-visual"> <img class="brief-infographic" src="assets/intro/brief-infographics/ch7-result.png" alt="모바일 망토로 반응형 화면과 푸시 알림이 연결된 인포그래픽" /> <div class="visual-caption"> <div class="visual-label">반응형 + 푸시</div> <div class="visual-sub"><strong>일상에서 다시 열리는</strong> 도구</div> </div> </div> </div>

아티팩트 · <strong>모바일 망토</strong>

<div class="grid2"> <div> <h2>손가락과 알림까지 맞춥니다</h2> <p class="lead">기존 CSS를 Tailwind로 정리하고, Firebase Cloud Messaging으로 푸시 알림 연결 과정을 보여줍니다.</p> </div> <div> <h3>이 챕터에서 보는 것</h3> <ul> <li>Tailwind 유틸리티 클래스로 모바일 정렬</li> <li>FCM API key와 service worker 연결</li> <li>데모용 프롬프트로 푸시 알림 구현 흐름 시연</li> </ul> <div class="tool-links"> <a class="tool-link" href="https://tailwindcss.com/" target="_blank" rel="noopener noreferrer">Tailwind CSS</a> <a class="tool-link secondary" href="https://firebase.google.com/docs/cloud-messaging" target="_blank" rel="noopener noreferrer">Firebase Cloud Messaging</a> </div> </div> </div>
<!-- _class: fullimg --> <div class="finale-overlay"> <h1>이제 여러분은 어엿한 <strong>바이브코더 용사</strong>입니다</h1> <p>이 능력들을 가지고, 직접 여러분의 서비스를 만들어볼까요?</p> </div>

bg


내 아이디어로 만드는 <strong>후반 50분</strong>

<div class="grid2"> <div> <h2>사전 제출 아이디어 · 실습 전환</h2> <p class="lead">세미클래스 봇이나 소개사이트로 미리 남긴 “만들고 싶은 것”을 실제 서비스 초안으로 좁힙니다.</p> <ul> <li>같은 제작 루프 → 각자 다른 서비스 주제</li> <li>강사가 필요한 아티팩트를 매핑하고 첫 프롬프트까지 정리</li> <li>준비된 아이디어는 화면 초안이나 실행 URL까지 시도</li> </ul> </div> <div> <h3>실습 산출물</h3> <ul> <li>내 서비스 한 장 명세</li> <li>첫 화면 생성 프롬프트</li> <li>필요 아티팩트 목록</li> <li>가능하면 초안 URL</li> </ul> </div> </div>

오늘 한 사람이 손에 쥐는 것

<div class="grid4"> <div class="card blue"><span class="tag">①</span><h3>제작 루프</h3><p>서비스가 막힐 때 어떤 요청을 해야 하는지</p></div> <div class="card blue"><span class="tag">②</span><h3>서비스 명세</h3><p>내 아이디어를 구현 가능한 첫 범위로 줄인 문서</p></div> <div class="card blue"><span class="tag">③</span><h3>첫 프롬프트</h3><p>AI에게 바로 맡길 수 있는 화면 생성 요청</p></div> <div class="card blue"><span class="tag">④</span><h3>다음 단계</h3><p>DB·배포·로그인·모바일 중 무엇부터 붙일지</p></div> </div> <div class="frame-note">강사 데모 결과물은 사후 자료로 받고, 내 서비스는 후속 구현으로 이어갈 수 있습니다.</div>
<!-- _class: cta -->

오늘 여정 끝 — <strong>다음은 어디로?</strong>

<div class="title-stage" style="margin-top:8px"> <div class="pixel"><span class="head"></span><span class="body"></span><span class="legs"></span></div> <div class="title-badges"> <span class="artifact full">DATA</span> <span class="artifact full">DB</span> <span class="artifact full">URL</span> <span class="artifact full">SIGNAL</span> <span class="artifact full">SEAL</span> <span class="artifact full">CAPE</span> </div> </div> <p class="lead" style="margin-top:8px;text-align:center;max-width:none"> 손에 쥔 6 아티팩트로 <strong>자기 서비스 1개</strong>까지 가고 싶다면 —<br /> <strong>세미콜론 인큐베이터</strong>가 다음 챕터입니다. </p> <p class="cta-meta" style="margin-top:18px">한 달 낙인 · 4주 1:1 케어 · 서비스 출시까지</p> <p class="small" style="margin-top:8px">관심 있으시면 강사에게 직접 말씀해 주세요. 자연스럽게 일정·인원 잡습니다.</p>