K-BOOKS / WEB DESIGN PRACTICAL

E01 빈 파일에서 완성까지
28단계 독학 교재

R42 여울 문화공간 문제 규격과 완성 코드를 기준으로 제작한 실습 교재입니다.

먼저 학생 지급 ZIP(빈 HTML·CSS·JS+이미지)을 압축 풀고 한 폴더를 28단계까지 직접 작성합니다. 학생 지급파일에는 FINISH 정답이 없습니다. 정상 화면은 강의에서 확인하세요. 3시간 모의시험에서는 이 교재를 보지 않습니다.

T01 / 28

학생 지급 ZIP 받아 VS Code로 열기

빈 파일과 지급 이미지가 있는 프로젝트 하나를 준비합니다.
학생 지급 ZIP: KBOOKS_E01_STUDENT_START_R44.zip (정답 파일 미포함)

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부터 직접 작성합니다. 중간 단계에서 프로젝트를 새로 다운로드하거나 기존 작업을 덮어쓰지 않습니다.

막혔을 때

직접 확인

T01 지급 프로젝트 확인

T02 / 28

HTML 전체 뼈대 만들기

빈 index.html을 HTML 문서 구조와 영역 표시용 주석으로 채웁니다.
정상 결과: 제목과 HTML 구조가 생성되지만 배치된 내용은 아직 없습니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 파일 전체 입력

<!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>

코드의 의미

막혔을 때

직접 확인

T02 단계 정상 화면

T03 / 28

CSS 기본·1200px 뼈대

여백 제거, 부모 너비, 링크·버튼 공통 규칙
정상 결과: PC의 기본 화면이 1200px 중앙에 놓입니다.

css/style.css 전체에 기본 CSS와 주석 표시를 입력하세요. 이 주석은 나중에 실제 디자인 코드로 바뀝니다. 이미지가 모바일 화면 밖으로 나가지 않게 하는 안전 규칙입니다.

1. css/style.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 */

코드의 의미

막혔을 때

직접 확인

T03 단계 정상 화면

T04 / 28

로고와 홈 링크

헤더의 브랜드 링크를 완성합니다.
정상 결과: 왼쪽 상단에 여울 문화공간 로고가 나타납니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- E01:LOGO -->

  <a class="brand" href="#about" aria-label="여울 문화공간 첫 화면">
    <img src="images/logo.svg" alt="여울 문화공간">
  </a>

코드의 의미

막혔을 때

직접 확인

T04 단계 정상 화면

T05 / 28

모바일 메뉴 버튼

작은 화면에서 클릭할 전체 메뉴 버튼을 만듭니다.
정상 결과: 로고 옆에 전체 메뉴 버튼이 생깁니다(아직 열기 동작은 나중에 연결).

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- E01:NAV_BUTTON -->

  <button class="nav-toggle" type="button" aria-expanded="false" aria-controls="mainNav">전체 메뉴</button>

코드의 의미

막혔을 때

직접 확인

T05 단계 정상 화면

T06 / 28

PC 주 메뉴·하위 메뉴

4개 메뉴와 하위 링크를 HTML로 만듭니다.
정상 결과: 로고 옆에 공간 소개·프로그램·전시·이용 안내 메뉴가 표시됩니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T06 단계 정상 화면

T07 / 28

첫 번째 슬라이드

첫 번째 이미지를 문서에 넣습니다.
정상 결과: 첫 번째 전시 이미지와 제목이 보입니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T07 단계 정상 화면

T08 / 28

두 번째 슬라이드

두 번째 이미지를 슬라이드 목록에 더합니다.
정상 결과: 두 번째 이미지가 문서에 추가됩니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T08 단계 정상 화면

T09 / 28

세 번째 슬라이드

세 번째 이미지를 추가합니다.
정상 결과: 이미지 3장의 HTML 구성이 완성됩니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T09 단계 정상 화면

T10 / 28

슬라이드 일시정지 버튼

슬라이드 순서 표시와 일시정지 버튼을 만듭니다.
정상 결과: 이미지 3장 아래에 01 / 03과 일시정지 버튼이 보입니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T10 단계 정상 화면

T11 / 28

소식 제목과 탭 버튼

공지사항·갤러리 전환 버튼을 만듭니다.
정상 결과: 여울 소식 제목과 두 탭 버튼이 보입니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- E01:NEWS_TITLE -->

    <h2 id="newsHeading">여울 소식</h2>

2. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- 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>

코드의 의미

막혔을 때

직접 확인

T11 단계 정상 화면

T12 / 28

공지사항 목록

제공 문구를 공지 목록으로 구성합니다.
정상 결과: 공지사항 세 줄이 나타납니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T12 단계 정상 화면

T13 / 28

갤러리 3장

공지/갤러리 콘텐츠 영역 안에 이미지 3장을 넣습니다.
정상 결과: 갤러리 영역이 생기고 이미지 파일이 연결됩니다(탭 전환은 나중).

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T13 단계 정상 화면

T14 / 28

배너·바로가기

콘텐츠 영역의 나머지 두 열을 채웁니다.
정상 결과: 공지 목록 옆에 전시 배너와 관람 안내 영역이 나타납니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- E01:BANNER_1 -->

  <a class="panel promo-panel" href="#gallery"><span>EXHIBITION</span><strong>여울의 새로운 전시</strong><span>전시 이야기 보기 →</span></a>

2. index.html — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: <!-- E01:BANNER_2 -->

  <a class="panel quick-panel" href="#contact"><span>VISIT YEOUL</span><strong>오시는 길과 관람 안내</strong><span>이용 안내 보기 →</span></a>

코드의 의미

막혔을 때

직접 확인

T14 단계 정상 화면

T15 / 28

푸터 정보

사이트 맨 아래 푸터를 만듭니다.
정상 결과: 여울 문화공간 이름과 안내 문구가 보입니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T15 단계 정상 화면

T16 / 28

공지 팝업 구조

공지 클릭 시 열릴 대화상자를 작성합니다.
정상 결과: 숨겨진 팝업 구조와 닫기·확인 버튼이 만들어집니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. index.html — 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>

코드의 의미

막혔을 때

직접 확인

T16 단계 정상 화면

T17 / 28

태블릿·모바일 반응형

900px·680px 이하에서 메뉴와 카드 1열 정리
정상 결과: PC 디자인을 작성하기 전부터 모바일 화면이 가로로 넘치지 않도록 안전한 반응형 규칙이 추가됩니다.

css/style.css에서 Ctrl+F로 E01:CSS_RESPONSIVE 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.

1. css/style.css — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: /* 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; }
}

코드의 의미

막혔을 때

직접 확인

T17 단계 정상 화면

T18 / 28

헤더·드롭다운 CSS

로고와 주 메뉴를 100px 헤더 안에 배치
정상 결과: 상단 메뉴가 가로로 정렬되고 PC 하위 메뉴가 hover로 열립니다.

css/style.css에서 Ctrl+F로 E01:CSS_HEADER 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.

1. css/style.css — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: /* 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; }

코드의 의미

막혔을 때

직접 확인

T18 단계 정상 화면

T19 / 28

슬라이드·페이드 CSS

300px 이미지 영역과 겹침·전환 규칙 추가
정상 결과: 슬라이드 높이가 300px이고 첫 이미지가 하나만 보입니다.

css/style.css에서 Ctrl+F로 E01:CSS_HERO 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.

1. css/style.css — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: /* 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; }

코드의 의미

막혔을 때

직접 확인

T19 단계 정상 화면

T20 / 28

콘텐츠 200px·갤러리 CSS

3열 카드, 공지 목록과 탭·갤러리 배치
정상 결과: 콘텐츠가 PC에서 200px로 정렬됩니다.

css/style.css에서 Ctrl+F로 E01:CSS_CONTENT 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.

1. css/style.css — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: /* 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; }

코드의 의미

막혔을 때

직접 확인

T20 단계 정상 화면

T21 / 28

배너·푸터·팝업 CSS

배너 배경, 100px 푸터, 팝업 디자인
정상 결과: PC 전체 높이가 헤더 100 + 슬라이드 300 + 콘텐츠 200 + 푸터 100으로 완성됩니다.

css/style.css에서 Ctrl+F로 E01:CSS_FOOTER 주석을 찾고, 표시 주석 전체를 이번 코드로 교체하세요. 이전 CSS는 절대 지우지 않습니다.

1. css/style.css — Ctrl+F로 표시 주석 찾아 교체

찾을 위치: /* 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; }

코드의 의미

막혔을 때

직접 확인

T21 단계 정상 화면

T22 / 28

JavaScript 실행 틀

js/script.js에 이벤트 실행 준비와 기능별 주석 표시를 만듭니다.
정상 결과: 페이지는 정상 표시되고 JS 기능은 아직 작동하지 않습니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. js/script.js — 파일 전체 입력

/* P07에서 이 파일에 동작 코드를 작성합니다. */
/* P07: 메뉴·슬라이드·탭·팝업을 연결합니다. 외부 인터넷 없이 작동합니다. */
document.addEventListener('DOMContentLoaded', () => {
  // E01:JS_MENU
  // E01:JS_SLIDE
  // E01:JS_TABS
  // E01:JS_POPUP
});

코드의 의미

막혔을 때

직접 확인

T22 단계 정상 화면

T23 / 28

모바일 메뉴 열기·닫기

작은 화면에서 전체 메뉴 버튼을 열고 닫습니다.
정상 결과: 390px 메뉴 버튼을 누르면 메뉴가 나타나고 링크를 누르면 접힙니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. js/script.js — 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');

코드의 의미

막혔을 때

직접 확인

T23 단계 정상 화면

T24 / 28

3장 페이드 자동재생

2.8초마다 활성 이미지가 전환되고 일시정지 버튼이 동작하게 만듭니다.
정상 결과: 슬라이드 3장이 반복되고 일시정지·다시 재생이 동작합니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. js/script.js — 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();

코드의 의미

막혔을 때

직접 확인

T24 단계 정상 화면

T25 / 28

공지·갤러리 탭 전환

탭 버튼 클릭 시 noticePanel/galleryPanel을 바꿉니다.
정상 결과: 공지와 갤러리 이미지가 클릭할 때마다 전환됩니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. js/script.js — 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();
      });
    });
  }

코드의 의미

막혔을 때

직접 확인

T25 단계 정상 화면

T26 / 28

공지 팝업 열기·닫기

공지 제목 클릭 시 팝업을 보여주고 닫기·확인·Esc를 연결합니다.
정상 결과: 공지 클릭 후 팝업이 나타나고 세 가지 방법으로 닫힙니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. js/script.js — 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();
  });

코드의 의미

막혔을 때

직접 확인

T26 단계 정상 화면

T27 / 28

PC·모바일·오프라인 기능 검사

문제지의 영역·이미지·동작과 오류를 마지막으로 검증합니다.
정상 결과: PC는 1200px / 700px, 모바일은 세로형이며 모든 버튼이 정상 동작합니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. Chrome · 실기 제출 전 점검 — 검토·확인

1. F12 Console 오류가 없는지 확인
2. PC: 헤더 100px · 슬라이드 300px · 콘텐츠 200px · 푸터 100px
3. 모바일: 320/390px에서 가로 스크롤 없음
4. 메뉴·페이드 3장·일시정지·탭·팝업 직접 조작
5. 이미지 7개 정상, CSS/JS 상대경로 확인
6. 인터넷 연결 없이 index.html 직접 열기

코드의 의미

막혔을 때

직접 확인

T27 단계 정상 화면

T28 / 28

180분 모의시험·제출 점검

완성 코드의 백업과 시험 시간 내 독립 제작 여부를 확인합니다.
정상 결과: 제출 폴더를 옮겨도 사이트가 열리고, E01 자율연습에서 힌트를 끄고 재도전할 수 있습니다.

편집기 왼쪽 파일을 열고 안내된 위치를 Ctrl+F로 찾은 뒤 지시대로 직접 입력하세요. 입력 후 저장하고 미리보기를 확인합니다.

1. 제출 폴더 점검 — 검토·확인

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 실행
- 시험장 실제 제출 위치·방식은 현장 지시를 따름

코드의 의미

막혔을 때

직접 확인

T28 단계 정상 화면