R42 여울 문화공간 문제 규격과 완성 코드를 기준으로 제작한 실습 교재입니다.
먼저 학생 지급 ZIP(빈 HTML·CSS·JS+이미지)을 압축 풀고 한 폴더를 28단계까지 직접 작성합니다. 학생 지급파일에는 FINISH 정답이 없습니다. 정상 화면은 강의에서 확인하세요. 3시간 모의시험에서는 이 교재를 보지 않습니다.
1. 학생 지급 ZIP을 다운로드하고 Windows의 [모두 압축 풀기]를 선택합니다.
2. VS Code의 파일 → 폴더 열기에서 KBOOKS_E01_MOCK를 선택합니다.
3. index.html·css/style.css·js/script.js는 내용이 없는 게 정상입니다. images/에 이미지 7개, source/content.txt에 제공 문구가 있는지 확인합니다.
KBOOKS_E01_MOCK/ ├─ index.html (빈 파일 · 학생 작성) ├─ css/style.css (빈 파일 · 학생 작성) ├─ js/script.js (빈 파일 · 학생 작성) ├─ images/ (지급 이미지 7개) │ ├─ logo.svg │ ├─ slide1.jpg / slide2.jpg / slide3.jpg │ └─ gallery1.jpg / gallery2.jpg / gallery3.jpg └─ source/content.txt (지급 문구)
모든 학생이 동일한 파일·이미지에서 시작하고, HTML·CSS·JavaScript는 T02부터 직접 작성합니다. 중간 단계에서 프로젝트를 새로 다운로드하거나 기존 작업을 덮어쓰지 않습니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>여울 문화공간 | K-BOOKS 실기 입문</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/script.js" defer></script>
</head>
<body>
<a class="skip-link" href="#main">본문 바로가기</a>
<div class="site-shell">
<header class="site-header">
<div class="header-inner">
<!-- E01:LOGO --><!-- E01:NAV_BUTTON --><!-- E01:NAV_MENU --></div>
</header>
<main id="main">
<section id="about" aria-label="문화공간 소개">
<div class="hero" aria-label="이달의 문화 소식">
<div class="slides" id="heroSlides">
<!-- E01:SLIDE_1 --><!-- E01:SLIDE_2 --><!-- E01:SLIDE_3 --> </div>
<!-- E01:SLIDE_CONTROLS --></div>
</section>
<section id="program" aria-label="프로그램 안내">
<div class="panel-grid">
<section class="panel news-panel" aria-labelledby="newsHeading">
<!-- E01:NEWS_TITLE --><!-- E01:NEWS_TABS --><!-- E01:NEWS_LIST --><!-- E01:GALLERY --> </section>
<!-- E01:BANNER_1 --><!-- E01:BANNER_2 --></div>
</section>
</main>
<footer class="site-footer" id="contact">
<!-- E01:FOOTER --> </footer>
</div>
<!-- E01:POPUP --></body>
</html>

css/style.css 전체에 기본 CSS와 주석 표시를 입력하세요. 이 주석은 나중에 실제 디자인 코드로 바뀝니다. 이미지가 모바일 화면 밖으로 나가지 않게 하는 안전 규칙입니다.
/* P01: 최소한의 안전한 화면 크기 설정. P05에서 실제 디자인을 완성합니다. */
* { box-sizing:border-box; }
body { margin:0; font:16px/1.65 system-ui, sans-serif; }
img { max-width:100%; height:auto; }
.site-shell { max-width:1200px; margin:0 auto; }
/* P05: 데스크톱 레이아웃부터 조립합니다. */
:root { --ink:#21314a; --navy:#1b3151; --coral:#eda76d; --paper:#f6f0e8; }
* { box-sizing:border-box; }
html { scroll-behavior:smooth; }
body { margin:0; background:#f1f5f8; color:var(--ink); font:15px/1.65 system-ui,"Malgun Gothic",sans-serif; }
[hidden] { display:none !important; }
a { color:inherit; text-decoration:none; }
a:focus-visible,button:focus-visible { outline:3px solid #ec8c49; outline-offset:3px; }
img { max-width:100%; display:block; }
button { font:inherit; cursor:pointer; }
.skip-link { position:absolute; top:-100px; left:12px; z-index:200; background:#fff; padding:10px; }
.skip-link:focus { top:12px; }
.site-shell { max-width:1200px; margin:0 auto; background:#fff; box-shadow:0 12px 48px rgba(11,29,55,.08); }
/* E01:CSS_HEADER *//* E01:CSS_HERO *//* E01:CSS_CONTENT *//* E01:CSS_FOOTER *//* E01:CSS_RESPONSIVE */
편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:LOGO -->
<a class="brand" href="#about" aria-label="여울 문화공간 첫 화면">
<img src="images/logo.svg" alt="여울 문화공간">
</a>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:NAV_BUTTON -->
<button class="nav-toggle" type="button" aria-expanded="false" aria-controls="mainNav">전체 메뉴</button>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:NAV_MENU -->
<nav class="main-nav" id="mainNav" aria-label="주 메뉴">
<ul>
<li><a href="#about">공간 소개</a><ul class="submenu"><li><a href="#about">인사말</a></li><li><a href="#contact">오시는 길</a></li></ul></li>
<li><a href="#program">프로그램</a><ul class="submenu"><li><a href="#program">교육 프로그램</a></li><li><a href="#program">신청 안내</a></li></ul></li>
<li><a href="#gallery">전시·공연</a><ul class="submenu"><li><a href="#gallery">전시 소식</a></li><li><a href="#gallery">공연 일정</a></li></ul></li>
<li><a href="#contact">이용 안내</a><ul class="submenu"><li><a href="#contact">찾아오시는 길</a></li><li><a href="#contact">문의</a></li></ul></li>
</ul>
</nav>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:SLIDE_1 -->
<article class="slide is-active" aria-hidden="false">
<img src="images/slide1.jpg" alt="여울 문화공간 첫 번째 전시 이미지">
<div class="slide-caption"><span>SPACE & ART</span><h1>예술이 머무는<br>우리 동네 문화공간</h1><p>전시와 교육, 공연을 한 자리에서 만나보세요.</p></div>
</article>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:SLIDE_2 -->
<article class="slide" aria-hidden="true">
<img src="images/slide2.jpg" alt="창작 프로그램 안내 이미지">
<div class="slide-caption"><span>LEARNING & CREATE</span><h2>함께 만들고 나누는 시간</h2><p>다양한 창작 워크숍이 열립니다.</p></div>
</article>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:SLIDE_3 -->
<article class="slide" aria-hidden="true">
<img src="images/slide3.jpg" alt="지역 공연 행사 이미지">
<div class="slide-caption"><span>COMMUNITY</span><h2>일상을 채우는 작은 공연</h2><p>지금 여울에서 만나는 새로운 이야기.</p></div>
</article>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:SLIDE_CONTROLS -->
<div class="hero-controls"><span id="slideStatus" aria-live="off">01 / 03</span><button id="slidePause" type="button" aria-pressed="false">일시정지</button></div>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:NEWS_TITLE -->
<h2 id="newsHeading">여울 소식</h2>
찾을 위치: <!-- E01:NEWS_TABS -->
<div class="tab-buttons" id="gallery" role="tablist" aria-label="소식 종류">
<button id="tabNotice" class="is-current" role="tab" aria-selected="true" aria-controls="noticePanel" type="button">공지사항</button>
<button id="tabGallery" role="tab" aria-selected="false" aria-controls="galleryPanel" type="button">갤러리</button>
</div>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:NEWS_LIST -->
<div id="noticePanel" role="tabpanel" aria-labelledby="tabNotice">
<ul class="notice-list">
<li><a class="notice-link" href="#noticePopup">11월 문화예술 프로그램 안내</a></li>
<li><a class="notice-link" href="#noticePopup">주말 어린이 창작 교실 모집</a></li>
<li><a class="notice-link" href="#noticePopup">커뮤니티 오픈 클래스 신청</a></li>
</ul>
</div>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:GALLERY -->
<div id="galleryPanel" role="tabpanel" aria-labelledby="tabGallery" hidden>
<div class="thumb-row"><img src="images/gallery1.jpg" alt="작가와의 만남"><img src="images/gallery2.jpg" alt="창작 교실"><img src="images/gallery3.jpg" alt="도시를 그리는 전시"></div>
</div>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:BANNER_1 -->
<a class="panel promo-panel" href="#gallery"><span>EXHIBITION</span><strong>여울의 새로운 전시</strong><span>전시 이야기 보기 →</span></a>
찾을 위치: <!-- E01:BANNER_2 -->
<a class="panel quick-panel" href="#contact"><span>VISIT YEOUL</span><strong>오시는 길과 관람 안내</strong><span>이용 안내 보기 →</span></a>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:FOOTER -->
<div class="footer-inner"><div><strong>YEOUL CULTURE</strong><p>여울 문화공간 · 전시·공연·교육</p></div><div><span>문의: 현장 연습용 가상 안내</span><small>© K-BOOKS ORIGINAL PRACTICE</small></div></div>

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: <!-- E01:POPUP -->
<div class="popup-backdrop" id="noticePopup" hidden>
<section class="popup-box" role="dialog" aria-modal="true" aria-labelledby="popupTitle" tabindex="-1">
<div class="popup-head"><h2 id="popupTitle">여울 소식</h2><button type="button" class="popup-close" id="popupClose">닫기 ×</button></div>
<p id="popupText">공지사항 내용을 확인하는 연습용 팝업입니다.</p>
<button class="popup-ok" id="popupOkay" type="button">확인</button>
</section>
</div>

css/style.css에서 Ctrl+F로 E01:CSS_RESPONSIVE 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.
찾을 위치: /* E01:CSS_RESPONSIVE */
/* P05에서도 작은 화면에서 카드가 겹치지 않게 하는 최소 안전장치를 넣습니다. */
@media (max-width:700px) { .header-inner { flex-wrap:wrap; padding:15px; } .main-nav { width:100%; } .main-nav>ul { flex-wrap:wrap; } .main-nav>ul>li { flex:1 1 45%; } .main-nav>ul>li>a { min-width:0; } .panel-grid,.gallery-cards { grid-template-columns:1fr; } .gallery-heading { flex-wrap:wrap; } .slide-caption { padding:38px 20px; width:96%; } .footer-inner { flex-wrap:wrap; } }
/* P06: 화면이 좁아져도 카드·메뉴가 한 글자씩 찌그러지지 않게 합니다. */
@media (max-width:900px) {
.header-inner { padding:14px 20px; }
.brand { width:145px; }
.main-nav>ul>li>a { min-width:100px; font-size:13px; }
.panel-grid { height:auto; min-height:200px; grid-template-columns:repeat(2,minmax(0,1fr)); }
.news-panel { grid-column:1 / -1; }
}
@media (max-width:680px) {
.header-inner { display:flex; align-items:center; gap:12px; }
.brand { width:138px; }
.nav-toggle { display:block; margin-left:auto; }
.main-nav { flex-basis:100%; width:100%; }
.main-nav>ul { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; }
.main-nav>ul>li { min-width:0; }
.main-nav>ul>li>a { border:1px solid #e1e8f1; border-radius:6px; padding:9px 6px; }
.submenu { display:none; position:static; box-shadow:none; border-radius:6px; }
.main-nav li:hover>.submenu,.main-nav li:focus-within>.submenu { display:block; }
/* 스크립트가 정상 연결된 경우에만 접기 메뉴를 적용합니다. */
.js-ready .main-nav:not(.is-open) { display:none; }
.js-ready .main-nav.is-open { display:block; }
.hero { height:300px; }
.slide-caption h1,.slide-caption h2 { font-size:clamp(23px,6.4vw,32px); }
.panel-grid { grid-template-columns:1fr; gap:10px; padding:14px; }
.news-panel { grid-column:auto; }
.footer-inner { padding:19px; }
}
@media (prefers-reduced-motion:reduce) {
html { scroll-behavior:auto; }
.slide { transition:none; }
}

css/style.css에서 Ctrl+F로 E01:CSS_HEADER 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.
찾을 위치: /* E01:CSS_HEADER */
.site-header { background:#fff; position:relative; z-index:20; }
.header-inner { display:flex; align-items:center; justify-content:space-between; gap:25px; min-height:100px; padding:0 32px; }
.brand { flex:none; display:block; width:162px; }
.brand img { width:100%; height:58px; object-fit:contain; }
.main-nav > ul { display:flex; list-style:none; margin:0; padding:0; gap:3px; }
.main-nav li { position:relative; list-style:none; }
.main-nav > ul > li > a { display:block; min-width:126px; padding:15px 8px; text-align:center; font-weight:750; }
.main-nav > ul > li > a:hover,.main-nav > ul > li:focus-within > a { color:#135b83; }
.submenu { position:absolute; left:0; right:0; top:100%; background:var(--navy); color:#fff; margin:0; padding:8px 0; list-style:none; display:none; box-shadow:0 6px 18px #091c3040; }
.main-nav li:hover > .submenu,.main-nav li:focus-within > .submenu { display:block; }
.submenu a { display:block; padding:8px 14px; font-size:13px; }
.submenu a:hover,.submenu a:focus-visible { background:#39516e; }
.nav-toggle { display:none; border:1px solid #d3dfec; padding:8px 12px; border-radius:7px; color:var(--navy); background:white; }

css/style.css에서 Ctrl+F로 E01:CSS_HERO 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.
찾을 위치: /* E01:CSS_HERO */
.hero { height:300px; position:relative; overflow:hidden; background:#1c3658; }
.slides,.slide { position:absolute; inset:0; }
.slide { opacity:0; pointer-events:none; transition:opacity .65s ease-in-out; }
.slide.is-active { opacity:1; pointer-events:auto; z-index:1; }
.slide img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.slide::after { content:""; position:absolute; inset:0; background:linear-gradient(90deg,rgba(18,30,49,.82),rgba(18,30,49,.1)); }
.slide-caption { position:relative; z-index:2; width:min(740px,88%); padding:45px 54px; color:#fff; }
.slide-caption > span { font-size:12px; letter-spacing:.22em; color:#ffce9c; font-weight:700; }
.slide-caption h1,.slide-caption h2 { font-size:clamp(24px,3vw,39px); letter-spacing:-.055em; line-height:1.3; margin:12px 0; }
.slide-caption p { margin:0; }
.hero-controls { position:absolute; bottom:13px; right:20px; z-index:5; color:#fff; display:flex; gap:14px; align-items:center; font-size:12px; }
.hero-controls button { border:1px solid #ffffff66; background:#152b4499; border-radius:50px; padding:6px 12px; color:#fff; }

css/style.css에서 Ctrl+F로 E01:CSS_CONTENT 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.
찾을 위치: /* E01:CSS_CONTENT */
.panel-grid { height:200px; min-height:0; display:grid; grid-template-columns:1.35fr 1fr 1fr; gap:14px; padding:12px 22px; }
.panel { border:1px solid #dae2ec; min-width:0; border-radius:9px; overflow:hidden; }
.news-panel { padding:8px 14px; }
.news-panel h2 { font-size:16px; margin:0 0 8px; }
.tab-buttons { display:flex; gap:8px; border-bottom:1px solid #dce5ef; }
.tab-buttons button { border:0; color:#61758d; padding:6px 8px; background:transparent; font-weight:700; }
.tab-buttons button.is-current { color:#164c7c; border-bottom:2px solid #2d6698; }
.notice-list { padding:10px 0 0 17px; margin:0; font-size:13px; }
.notice-list li { line-height:1.7; }
.notice-link:hover { text-decoration:underline; }
.thumb-row { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:6px; margin-top:14px; }
.thumb-row img { width:100%; aspect-ratio:1; object-fit:cover; border-radius:5px; }

css/style.css에서 Ctrl+F로 E01:CSS_FOOTER 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.
찾을 위치: /* E01:CSS_FOOTER */
.promo-panel,.quick-panel { display:flex; flex-direction:column; justify-content:end; gap:5px; color:white; padding:20px; background:linear-gradient(145deg,#304966,#19344e); }
.promo-panel { background:linear-gradient(145deg,#9b6d4a,#543a42); }
.promo-panel span,.quick-panel span { font-size:12px; letter-spacing:.035em; }
.promo-panel strong,.quick-panel strong { font-size:20px; line-height:1.4; }
.site-footer { background:#15263e; color:#fff; }
.footer-inner { min-height:100px; padding:20px 27px; display:flex; gap:18px; justify-content:space-between; align-items:center; }
.footer-inner strong { font-size:15px; letter-spacing:.08em; }
.footer-inner p,.footer-inner span,.footer-inner small { display:block; color:#bccce1; font-size:12px; margin:2px 0; }
.popup-backdrop { position:fixed; inset:0; background:#0b2136b8; display:grid; place-items:center; z-index:100; padding:18px; }
.popup-box { width:min(480px,100%); background:white; padding:25px; border-radius:12px; box-shadow:0 20px 55px #07192c4c; }
.popup-head { display:flex; justify-content:space-between; gap:14px; align-items:center; }
.popup-head h2 { margin:0; font-size:19px; }
.popup-close { background:#eef4fa; border:0; border-radius:6px; padding:8px 12px; }
.popup-ok { margin-top:20px; padding:9px 19px; border:0; background:var(--navy); color:white; border-radius:6px; }

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
/* P07에서 이 파일에 동작 코드를 작성합니다. */
/* P07: 메뉴·슬라이드·탭·팝업을 연결합니다. 외부 인터넷 없이 작동합니다. */
document.addEventListener('DOMContentLoaded', () => {
// E01:JS_MENU
// E01:JS_SLIDE
// E01:JS_TABS
// E01:JS_POPUP
});

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: // E01:JS_MENU
const navButton = document.querySelector('.nav-toggle');
const nav = document.getElementById('mainNav');
if (navButton && nav) {
navButton.addEventListener('click', () => {
const open = nav.classList.toggle('is-open');
navButton.setAttribute('aria-expanded', String(open));
});
nav.addEventListener('click', event => {
if (event.target.closest('a') && window.innerWidth <= 680) {
nav.classList.remove('is-open');
navButton.setAttribute('aria-expanded', 'false');
}
});
}
document.documentElement.classList.add('js-ready');

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: // E01:JS_SLIDE
const slides = [...document.querySelectorAll('.slide')];
const slideStatus = document.getElementById('slideStatus');
const pauseButton = document.getElementById('slidePause');
const hero = document.querySelector('.hero');
let current = 0;
let paused = false;
const updateSlides = () => {
slides.forEach((slide, index) => {
const active = index === current;
slide.classList.toggle('is-active', active);
slide.setAttribute('aria-hidden', String(!active));
});
if (slideStatus) slideStatus.textContent = `${String(current + 1).padStart(2,'0')} / ${String(slides.length).padStart(2,'0')}`;
};
if (pauseButton) pauseButton.addEventListener('click', () => {
paused = !paused;
pauseButton.textContent = paused ? '다시 재생' : '일시정지';
pauseButton.setAttribute('aria-pressed', String(paused));
});
if (slides.length > 1) {
window.setInterval(() => {
if (paused || document.hidden || (hero && (hero.matches(':hover') || hero.contains(document.activeElement)))) return;
current = (current + 1) % slides.length;
updateSlides();
}, 2800);
}
updateSlides();

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: // E01:JS_TABS
const tabNotice = document.getElementById('tabNotice');
const tabGallery = document.getElementById('tabGallery');
const noticePanel = document.getElementById('noticePanel');
const galleryPanel = document.getElementById('galleryPanel');
function selectTab(which) {
const onNotice = which === 'notice';
noticePanel.hidden = !onNotice;
galleryPanel.hidden = onNotice;
tabNotice.classList.toggle('is-current', onNotice);
tabGallery.classList.toggle('is-current', !onNotice);
tabNotice.setAttribute('aria-selected', String(onNotice));
tabGallery.setAttribute('aria-selected', String(!onNotice));
}
if (tabNotice && tabGallery && noticePanel && galleryPanel) {
tabNotice.addEventListener('click', () => selectTab('notice'));
tabGallery.addEventListener('click', () => selectTab('gallery'));
// 메뉴의 전시·공연 링크도 200px 콘텐츠 안의 갤러리 탭으로 이동합니다.
document.querySelectorAll('a[href="#gallery"]').forEach(link => {
link.addEventListener('click', () => selectTab('gallery'));
});
[tabNotice, tabGallery].forEach((tab, idx, arr) => {
tab.addEventListener('keydown', event => {
if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') return;
event.preventDefault();
const next = arr[1 - idx];
selectTab(next === tabNotice ? 'notice' : 'gallery');
next.focus();
});
});
}

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
찾을 위치: // E01:JS_POPUP
const popup = document.getElementById('noticePopup');
const popupBox = popup && popup.querySelector('.popup-box');
const popupText = document.getElementById('popupText');
const closePopup = () => { if (popup) popup.hidden = true; if (lastFocused) lastFocused.focus(); };
let lastFocused = null;
document.querySelectorAll('.notice-link').forEach(link => link.addEventListener('click', event => {
event.preventDefault();
lastFocused = link;
if (popupText) popupText.textContent = `${link.textContent.trim()}
자세한 내용은 문화공간 공지에서 확인하세요.`;
if (popup) popup.hidden = false;
if (popupBox) popupBox.focus();
}));
document.getElementById('popupClose')?.addEventListener('click', closePopup);
document.getElementById('popupOkay')?.addEventListener('click', closePopup);
popup?.addEventListener('click', event => { if (event.target === popup) closePopup(); });
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && popup && !popup.hidden) closePopup();
});

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
1. F12 Console 오류가 없는지 확인 2. PC: 헤더 100px · 슬라이드 300px · 콘텐츠 200px · 푸터 100px 3. 모바일: 320/390px에서 가로 스크롤 없음 4. 메뉴·페이드 3장·일시정지·탭·팝업 직접 조작 5. 이미지 7개 정상, CSS/JS 상대경로 확인 6. 인터넷 연결 없이 index.html 직접 열기

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.
KBOOKS_E01_MOCK/ ├─ index.html ├─ css/style.css ├─ js/script.js ├─ images/ (logo.svg, slide1~3.jpg, gallery1~3.jpg) └─ source/content.txt (학습 참고자료) - 최종 답안 폴더의 필수 파일이 정상 - 총 파일 크기 10MB 이내 - 파일 경로/이미지 정상, JS 기능 정상 - 인터넷 없이 직접 index.html 실행 - 시험장 실제 제출 위치·방식은 현장 지시를 따름
