K-BOOKS ORIGINAL / E02

초록온 식물원 28단계
빈 파일에서 완성까지

지급 ZIP 한 개를 풀고, 같은 프로젝트 폴더를 끝까지 수정합니다. 시작 파일에 정답은 없습니다.

학생용 시작파일 ZIP
T01 / 28

학생 지급 ZIP 다운로드·폴더 열기

빈 코드 파일 3개와 제공 이미지 7개를 확인합니다.

정상 결과: KBOOKS_E02_MOCK 폴더 안의 HTML·CSS·JS 세 파일은 비어 있습니다.

한 번만 다운로드하세요. 이 파일은 완성 정답을 포함하지 않습니다.

1. 학생 지급파일 확인

프로젝트 준비

① [E02 학생 지급 ZIP]을 누르고 Windows에서 [모두 압축 풀기] 선택
② VS Code → 파일 → 폴더 열기 → KBOOKS_E02_MOCK 선택
③ index.html, css/style.css, js/script.js가 빈 파일인지 확인
④ images/ 7장과 source/content.txt, 문제지.txt 확인
⑤ T02부터 이 하나의 폴더를 마지막까지 계속 수정
※ 이미 작성한 프로젝트를 새 ZIP으로 덮어쓰지 마세요.

왜 필요한가

오류 복구

파일이 안 보여요 / ZIP을 압축 해제하지 않았거나 상위 폴더를 열었습니다. / KBOOKS_E02_MOCK 폴더 자체를 VS Code로 엽니다.

직접 확인

T01 모바일 화면

T02 / 28

index.html 문서 구조 만들기

문서 선언·head·CSS 연결·기본 HTML 영역을 구성합니다.

정상 결과: 텍스트가 아직 많지 않고, 브라우저가 빈 화면에 가까운 것은 정상입니다.

index.html은 빈 파일이므로 이 단계에서만 전체 입력합니다. 다음 단계부터는 주석 하나씩 교체합니다.

1. index.html

빈 파일 전체에 입력

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>초록온 식물원 | K-BOOKS 자체 제작 실기</title>
  <meta name="description" content="K-BOOKS 자체 제작 웹디자인개발기능사 E02 연습 완성 참고본">
  <link rel="stylesheet" href="css/style.css">
</head>
<body class="botanic">
<div class="site-shell" id="home">
<aside class="side-brand" id="about"><!-- E02:BRAND --><!-- E02:MOBILE --><!-- E02:NAV --><!-- E02:SIDEFOOT --></aside>
<div class="main-column">
<!-- E02:MASTHEAD -->
<main>
  <section class="visual" aria-label="메인 이미지 슬라이드">
    <div class="slide-window"><div class="slide-track" id="slideTrack">
          <!-- E02:SLIDE1 -->
          <!-- E02:SLIDE2 -->
          <!-- E02:SLIDE3 -->
    </div></div>
    <!-- E02:CONTROLS -->
  </section>
  <section class="content-grid" id="program" aria-label="공지와 안내">
    <div class="news-panel" id="notice"><!-- E02:TABS -->
      <!-- E02:NOTICE --><!-- E02:GALLERY -->
    </div>
    <!-- E02:STORY -->
    <!-- E02:QUICK -->
  </section>
</main>
<!-- E02:FOOTER -->
</div>
</div>
<!-- E02:POPUP -->
<script src="js/script.js"></script>
</body>
</html>

왜 필요한가

오류 복구

태그를 닫지 못해 화면이 깨짐 / index.html 전체를 빠뜨리거나 닫는 태그를 삭제했습니다. / 강의의 전체 HTML 구조를 처음부터 다시 확인하세요.

직접 확인

T02 모바일 화면

T03 / 28

CSS 기본값과 배치 표시 준비

글꼴·박스 크기·기본 링크 모양을 먼저 작성합니다.

정상 결과: CSS가 불러와지고 기본 여백이 사라집니다.

style.css는 이 단계에서만 전체 입력합니다. 이후에는 E02:CSS_ 표시 주석을 찾아 교체합니다.

1. css/style.css

빈 파일 전체에 입력

/* E02 - K-BOOKS 실기 180분 완성 참고 CSS */
:root{--primary:#1f5248;--accent:#cadc95;--soft:#f3f8ed;--ink:#284237;--hero-height:300px}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:#e9edf0;color:var(--ink);font:14px/1.6 system-ui,"Malgun Gothic",Arial,sans-serif}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer}
button:focus-visible,a:focus-visible{outline:3px solid #fdaf45;outline-offset:3px}
[hidden]{display:none!important}
/* E02:CSS_SHELL *//* E02:CSS_SIDE *//* E02:CSS_HEADER *//* E02:CSS_HERO *//* E02:CSS_CONTENT *//* E02:CSS_FOOTPOP *//* E02:CSS_RESPONSIVE */

왜 필요한가

오류 복구

CSS가 반영되지 않음 / index.html의 연결 경로가 잘못됐습니다. / <link href="css/style.css"> 경로와 파일 이름을 확인하세요.

직접 확인

T03 모바일 화면

T04 / 28

모바일에서 사이드바가 세로로 전환되는 규칙

PC 세로형을 모바일에서 1열로 전환할 준비를 합니다.

정상 결과: 작은 화면에서는 부모 영역이 세로로 배치되며 가로 스크롤이 발생하지 않습니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_RESPONSIVE */

@media(max-width:950px){.site-shell{max-width:100%}.content-grid{grid-template-columns:minmax(0,1.35fr) repeat(2,minmax(0,.85fr))}.story-card,.quick-card{padding-left:16px;padding-right:16px}}
@media(max-width:760px){
.site-shell{display:block;min-height:0}
.side-brand{min-height:0;padding:12px 17px;display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:10px}
.side-brand .brand{margin:0;flex:1;min-width:0}
.side-brand .brand small{font-size:8px}
.side-brand .sidebar-foot{display:none}
.top-header{height:auto;min-height:76px;padding:14px 16px;flex-wrap:wrap}
.top-header .brand{flex:1}
.mobile-menu{display:inline-block;white-space:nowrap}
.site-nav{display:none;width:100%;flex:0 0 100%;order:10;max-width:none!important}
.site-nav.open{display:block}
.top-header .site-nav>ul{display:block}
.top-header .nav-row{justify-content:space-between}
.top-header .nav-link{text-align:left}
.top-header .submenu{position:static;box-shadow:none}
.site-nav .nav-item{border-bottom:1px solid #ffffff33}
.site-nav .nav-link{padding:8px}
.masthead{height:auto;min-height:55px;padding:12px 18px}
.masthead strong{font-size:12px}
.content-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.news-panel{grid-column:1/-1}
.story-card,.quick-card{min-height:150px;padding:25px 17px}
.site-footer{flex-wrap:wrap;padding:22px 19px}
}
@media(max-width:420px){
.slide-copy{left:18px;top:57px;right:15px}.slide-copy h2{font-size:24px}.slide-copy p{font-size:11px}
.slide-no{left:18px;bottom:29px;font-size:18px}
.slider-controls{right:12px;bottom:14px}
.slider-controls>button{min-width:23px;height:26px}
.news-panel{padding:19px 16px}
.story-card,.quick-card{padding:20px 13px}
.story-card strong,.quick-card strong{font-size:14px}
.site-footer{gap:10px}.footer-links{justify-content:flex-start}.footer-links small{text-align:left}
}
@media(prefers-reduced-motion:reduce){.slide-track{transition:none}html{scroll-behavior:auto}}

왜 필요한가

오류 복구

모바일 글자가 좁게 찌그러짐 / PC의 200px 사이드바와 1000px 본문을 모바일에도 강제했습니다. / 760px 이하 .site-shell{display:block}을 확인하세요.

직접 확인

T04 모바일 화면

T05 / 28

왼쪽 로고·모바일 메뉴 버튼 추가

왼쪽 메뉴에 로고와 햄버거 버튼을 배치합니다.

정상 결과: 로고와 ☰ 메뉴가 HTML에 추가됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:BRAND -->

<a class="brand" href="#home"><img src="images/logo.svg" alt="초록온 식물원 심볼"><strong>초록온 식물원</strong><small>OFFICIAL WEBSITE</small></a>
2. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:MOBILE -->

<button type="button" class="mobile-menu" aria-expanded="false" aria-controls="siteNav">☰ 메뉴</button>

왜 필요한가

오류 복구

이미지가 깨져요 / logo.svg 상대경로 또는 images 폴더 위치가 틀렸습니다. / index.html 바로 옆에 images/logo.svg를 둡니다.

직접 확인

T05 모바일 화면

T06 / 28

세로 주 메뉴·하위 메뉴·사이드 문구

메뉴 4개와 각 하위 메뉴를 직접 입력합니다.

정상 결과: 식물원 소개·정원 탐방·체험 예약·소식안내 4개 메뉴가 준비됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:NAV -->

<nav class="site-nav" id="siteNav" aria-label="주 메뉴"><ul>      <li class="nav-item"><div class="nav-row"><a class="nav-link" href="#about">식물원 소개</a><button class="submenu-toggle" type="button" aria-expanded="false" aria-controls="sub0" aria-label="식물원 소개 하위 메뉴 열기">+</button></div><ul id="sub0" class="submenu" hidden><li><a href="#about">식물원 소개 안내</a></li><li><a href="#notice">소식 확인</a></li></ul></li>
      <li class="nav-item"><div class="nav-row"><a class="nav-link" href="#program">정원 탐방</a><button class="submenu-toggle" type="button" aria-expanded="false" aria-controls="sub1" aria-label="정원 탐방 하위 메뉴 열기">+</button></div><ul id="sub1" class="submenu" hidden><li><a href="#program">정원 탐방 안내</a></li><li><a href="#notice">소식 확인</a></li></ul></li>
      <li class="nav-item"><div class="nav-row"><a class="nav-link" href="#reserve">체험 예약</a><button class="submenu-toggle" type="button" aria-expanded="false" aria-controls="sub2" aria-label="체험 예약 하위 메뉴 열기">+</button></div><ul id="sub2" class="submenu" hidden><li><a href="#reserve">체험 예약 안내</a></li><li><a href="#notice">소식 확인</a></li></ul></li>
      <li class="nav-item"><div class="nav-row"><a class="nav-link" href="#community">소식·안내</a><button class="submenu-toggle" type="button" aria-expanded="false" aria-controls="sub3" aria-label="소식·안내 하위 메뉴 열기">+</button></div><ul id="sub3" class="submenu" hidden><li><a href="#community">소식·안내 안내</a></li><li><a href="#notice">소식 확인</a></li></ul></li></ul></nav>
2. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:SIDEFOOT -->

<p class="sidebar-foot">K-BOOKS · WEB PRACTICE<br>ORIGINAL PROJECT</p>

왜 필요한가

오류 복구

하위 메뉴 버튼이 반응하지 않음 / 아직 JS 작성 전이므로 정상입니다. / 이 단계에서는 메뉴 구조·하위 목록만 확인하세요. 클릭 기능은 T25에서 작성합니다.

직접 확인

T06 모바일 화면

T07 / 28

본문 상단 제목 100px

오른쪽 본문 상단에 메인 안내 영역을 만듭니다.

정상 결과: 오른쪽 제목과 안내 문구가 HTML에 표시됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:MASTHEAD -->

<div class="masthead"><span>WEB DESIGN PRACTICE</span><strong>자연의 결을 따라 걷는 하루</strong></div>

왜 필요한가

오류 복구

슬라이드가 아직 안 나와요 / 슬라이드 이미지 태그가 아직 없습니다. / T08에서 첫 이미지를 추가합니다.

직접 확인

T07 모바일 화면

T08 / 28

좌우 슬라이드 300px CSS

세 장을 가로로 배치하고 화면 밖의 그림을 숨깁니다.

정상 결과: PC 슬라이드가 300px 높이로 나타나고 이미지가 한 장씩 보입니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_HERO */

.visual{position:relative;height:var(--hero-height);overflow:hidden;background:var(--primary)}
.slide-window{height:var(--hero-height);overflow:hidden}
.slide-track{height:100%;display:flex;flex-direction:row;transition:transform .62s cubic-bezier(.22,.66,.19,1);will-change:transform}
.slide{height:var(--hero-height);min-height:var(--hero-height);flex:0 0 100%;width:100%;position:relative;overflow:hidden;background:var(--primary);color:white}
.slide::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,#08180d91,transparent 70%);pointer-events:none}
.slide img{display:block;width:100%;height:100%;object-fit:cover}
.slide-copy{position:absolute;z-index:1;top:48px;left:34px;max-width:420px}
.slide-copy .eyebrow{font-size:10px;letter-spacing:.18em;font-weight:800;color:#e2e5e6}
.slide-copy h2{font-size:clamp(25px,4vw,35px);line-height:1.25;margin:19px 0 12px;letter-spacing:-.05em}
.slide-copy p{max-width:410px;font-size:12px;margin:0;color:#f4f4f4}
.slide-no{position:absolute;z-index:1;left:34px;bottom:28px;color:#fff8;font-size:27px;letter-spacing:.14em;font-weight:350}
.slider-controls{position:absolute;z-index:2;bottom:22px;right:27px;display:flex;align-items:center;gap:6px}
.slider-controls>button{border:1px solid #ffffff7d;background:#10271e9a;color:white;border-radius:5px;min-width:27px;height:29px;font-size:15px}
#pauseSlide{font-size:10px;padding:0 7px}
.slider-dots{display:flex;gap:5px;margin-left:8px}
.slider-dots button{width:10px;height:10px;border-radius:99px;border:1px solid white;background:#ffffff65;padding:0}
.slider-dots button[aria-current=true]{width:23px;background:var(--accent)}

왜 필요한가

오류 복구

이미지 3장이 겹침 / slide-window overflow:hidden 또는 track display:flex가 없습니다. / .slide-track/.slide-window를 다시 확인합니다.

직접 확인

T08 모바일 화면

T09 / 28

첫 번째 가로 슬라이드 이미지

slide1.jpg와 문구를 추가합니다.

정상 결과: 첫 번째 이미지·제목·페이지번호가 준비됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:SLIDE1 -->

<article class="slide"><img src="images/slide1.jpg" alt="초록온 식물원 슬라이드 1"><div class="slide-copy"><span class="eyebrow">BOTANIC GARDEN · 01</span><h2>숲을 닮은 시간</h2><p>정원과 자연을 배우는 체험형 식물원 홈페이지</p></div><span class="slide-no">01 / 03</span></article>

왜 필요한가

오류 복구

이미지 대신 빈 영역 / images/slide1.jpg 경로가 틀렸거나 CSS 작업 전입니다. / 그림 경로를 확인하고 T08 CSS 이후 다시 확인합니다.

직접 확인

T09 모바일 화면

T10 / 28

두 번째·세 번째 슬라이드 이미지

slide2·slide3을 같은 slideTrack 안에 넣습니다.

정상 결과: 한 슬라이드 부모 안에 이미지가 총 3개 들어갑니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:SLIDE2 -->

<article class="slide"><img src="images/slide2.jpg" alt="초록온 식물원 슬라이드 2"><div class="slide-copy"><span class="eyebrow">BOTANIC GARDEN · 02</span><h2>초록빛 하루를 만나보세요</h2><p>정원과 자연을 배우는 체험형 식물원 홈페이지</p></div><span class="slide-no">02 / 03</span></article>
2. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:SLIDE3 -->

<article class="slide"><img src="images/slide3.jpg" alt="초록온 식물원 슬라이드 3"><div class="slide-copy"><span class="eyebrow">BOTANIC GARDEN · 03</span><h2>초록빛 하루를 만나보세요</h2><p>정원과 자연을 배우는 체험형 식물원 홈페이지</p></div><span class="slide-no">03 / 03</span></article>

왜 필요한가

오류 복구

이미지가 화면 아래로 세로 나열 / 아직 slide-track CSS를 작성하지 않았습니다. / T20에서 display:flex를 적용해 가로 줄로 만듭니다.

직접 확인

T10 모바일 화면

T11 / 28

슬라이드 이전·다음·일시정지·점 버튼

슬라이드 버튼을 배치합니다.

정상 결과: 이전/다음/일시정지 버튼과 3개 점 버튼이 보입니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:CONTROLS -->

<div class="slider-controls" aria-label="슬라이드 컨트롤"><button type="button" id="prevSlide" aria-label="이전 슬라이드">‹</button><button type="button" id="pauseSlide" aria-pressed="false">일시정지</button><button type="button" id="nextSlide" aria-label="다음 슬라이드">›</button><div class="slider-dots" aria-label="슬라이드 번호"><button type="button" data-index="0" aria-label="첫 번째 슬라이드" aria-current="true"></button><button type="button" data-index="1" aria-label="두 번째 슬라이드"></button><button type="button" data-index="2" aria-label="세 번째 슬라이드"></button></div></div>

왜 필요한가

오류 복구

버튼을 눌러도 이동하지 않음 / JS 기능을 아직 연결하지 않았습니다. / T24에서 좌우 전환 스크립트를 추가합니다.

직접 확인

T11 모바일 화면

T12 / 28

공지사항·갤러리 탭 버튼

탭 두 개와 선택 상태를 작성합니다.

정상 결과: 공지사항·갤러리 탭이 준비됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:TABS -->

<div class="tab-buttons" role="tablist" aria-label="공지와 갤러리"><button type="button" id="noticeTab" role="tab" aria-selected="true" aria-controls="noticePanel">공지사항</button><button type="button" id="galleryTab" role="tab" aria-selected="false" aria-controls="galleryPanel">갤러리</button></div>

왜 필요한가

오류 복구

내용이 아직 비어 있음 / 공지 패널과 갤러리 패널을 작성하지 않았습니다. / T12~T13에서 화면을 채웁니다.

직접 확인

T12 모바일 화면

T13 / 28

공지사항 3개와 팝업 열기 버튼

공지 제목과 날짜, data-title을 입력합니다.

정상 결과: 공지 3건이 HTML에 표시됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:NOTICE -->

<section id="noticePanel" role="tabpanel" aria-labelledby="noticeTab"><ul class="notice-list">
              <li><button type="button" class="notice-open" data-title="겨울 정원 관람시간 안내"><span>겨울 정원 관람시간 안내</span><time datetime="2026-11-02">2026.11</time></button></li>
              <li><button type="button" class="notice-open" data-title="식물 표본 수업 접수"><span>식물 표본 수업 접수</span><time datetime="2026-11-02">2026.11</time></button></li>
              <li><button type="button" class="notice-open" data-title="가든 페어 주말 행사"><span>가든 페어 주말 행사</span><time datetime="2026-11-02">2026.11</time></button></li>
      </ul></section>

왜 필요한가

오류 복구

공지 클릭해도 팝업 안 뜸 / 팝업 HTML과 JS를 아직 작성하지 않았습니다. / T16과 T27 이후 다시 검사합니다.

직접 확인

T13 모바일 화면

T14 / 28

갤러리 사진 3장

갤러리 이미지를 상대경로로 작성합니다.

정상 결과: 갤러리 패널 내부에 갤러리 사진 3장이 들어갑니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:GALLERY -->

<section id="galleryPanel" role="tabpanel" aria-labelledby="galleryTab" hidden><div class="gallery-grid">
              <a href="#program" class="gallery-item"><img src="images/gallery1.jpg" alt="온실 산책 사진"><span>온실 산책</span></a>
              <a href="#program" class="gallery-item"><img src="images/gallery2.jpg" alt="허브 클래스 사진"><span>허브 클래스</span></a>
              <a href="#program" class="gallery-item"><img src="images/gallery3.jpg" alt="정원 전시 사진"><span>정원 전시</span></a>
      </div></section>

왜 필요한가

오류 복구

갤러리가 화면에 나타나지 않음 / 처음 hidden인 것은 정상입니다. / T26에서 탭 전환 JS를 넣은 뒤 갤러리 버튼을 클릭합니다.

직접 확인

T14 모바일 화면

T15 / 28

콘텐츠 배너·바로가기 카드

식물 관리 배너와 예약 바로가기 카드를 추가합니다.

정상 결과: 공지·배너·바로가기 콘텐츠 3개가 완성됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:STORY -->

<a href="#community" class="story-card" id="reserve"><span>FEATURED STORY</span><strong>식물 관리 팁</strong><small>정원과 자연을 배우는 체험형 식물원 홈페이지</small><span class="card-arrow">자세히 보기 ↗</span></a>
2. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:QUICK -->

<a href="#community" class="quick-card"><span>QUICK ACCESS</span><strong>예약 안내 바로가기</strong><small>관련 정보를 확인해 보세요</small><span class="card-arrow">바로가기 ↗</span></a>

왜 필요한가

오류 복구

카드가 세로로 길게 보임 / 3열 CSS를 아직 적용하지 않았습니다. / T21에서 grid-template-columns를 작성합니다.

직접 확인

T15 모바일 화면

T16 / 28

사이트 푸터

사이트명·이용 안내·저작권을 작성합니다.

정상 결과: 푸터 안내가 화면 아래에 나타납니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:FOOTER -->

<footer id="community" class="site-footer"><div><strong>초록온 식물원</strong><small>K-BOOKS ORIGINAL PRACTICE · 2026</small></div><div class="footer-links"><a href="#about">이용약관</a><a href="#program">프로그램</a><a href="#notice">문의하기</a><small>© K-BOOKS educational mock project.</small></div></footer>

왜 필요한가

오류 복구

푸터가 너무 위로 올라옴 / 앞선 영역의 CSS 높이 설정 전입니다. / T22에서 푸터 100px 디자인을 적용합니다.

직접 확인

T16 모바일 화면

T17 / 28

공지 레이어 팝업 HTML

팝업을 처음에는 숨긴 상태로 만듭니다.

정상 결과: noticePopup이 존재하며 아직 hidden으로 숨겨져 있습니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. index.html

Ctrl+F로 주석을 찾아 교체

찾을 주석: <!-- E02:POPUP -->

<div id="noticePopup" class="popup-backdrop" role="dialog" aria-modal="true" aria-labelledby="popupTitle" hidden><div class="popup-card"><button type="button" class="popup-close" aria-label="팝업 닫기">×</button><span class="popup-label">NOTICE</span><h2 id="popupTitle"></h2><p id="popupBody">초록온 식물원에서 안내하는 연습용 공지사항입니다.</p><button type="button" class="popup-ok">확인</button></div></div>

왜 필요한가

오류 복구

팝업이 처음부터 떠 있음 / hidden이 빠졌거나 CSS hidden 규칙이 적용되지 않았습니다. / noticePopup의 hidden과 [hidden]{display:none!important}를 확인합니다.

직접 확인

T17 모바일 화면

T18 / 28

PC 200px 사이드바·1000px 본문 Grid

바깥 부모를 PC 2열 Grid로 나눕니다.

정상 결과: 1200px에서 왼쪽 약 200px, 오른쪽 약 1000px로 나눠집니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_SHELL */

.site-shell{max-width:1200px;min-height:700px;margin:0 auto;background:white;display:grid;grid-template-columns:200px minmax(0,1fr);box-shadow:0 12px 45px #1d26301c}
.main-column{min-width:0}
.brand{display:flex;align-items:center;gap:12px;font-weight:900}
.brand img{width:27px;height:27px;object-fit:contain}
.brand strong{font-size:17px;letter-spacing:-.04em}
.brand small{display:block;font-size:10px;letter-spacing:.16em;font-weight:650;opacity:.64}
.mobile-menu{display:none;background:transparent;border:1px solid currentColor;border-radius:6px;padding:7px 11px}
.site-nav ul{padding:0;margin:0;list-style:none}
.nav-item{position:relative;min-width:0}
.nav-row{display:flex;align-items:center;min-width:0}
.nav-link{display:block;font-weight:750;font-size:13px;flex:1;min-width:0}
.submenu-toggle{border:0;background:transparent;color:inherit;padding:12px;opacity:.7}
.submenu{padding:4px 12px 12px!important;list-style:none;background:#ffffff16}
.submenu li a{display:block;padding:6px 5px;font-size:11px;opacity:.88}
.submenu a:hover,.nav-link:hover{text-decoration:underline;text-underline-offset:4px}

왜 필요한가

오류 복구

본문이 화면 밖으로 밀림 / 고정 1000px을 모바일에도 적용했거나 minmax(0,1fr)가 없습니다. / T04 미디어쿼리와 .main-column{min-width:0}을 확인합니다.

직접 확인

T18 모바일 화면

T19 / 28

사이드 메뉴 세로 디자인

좌측 로고·메뉴 간격과 색상·높이를 적용합니다.

정상 결과: 왼쪽 세로 메뉴가 초록색으로 표시됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_SIDE */

.side-brand{background:var(--primary);color:white;padding:32px 16px 18px;display:flex;flex-direction:column;min-height:700px}
.side-brand .brand{flex-wrap:wrap;gap:6px 9px;line-height:1.5;margin-bottom:40px}
.side-brand .brand small{width:100%;padding-left:0}
.side-brand .nav-item{border-bottom:1px solid #ffffff29}
.side-brand .nav-row{min-height:55px}
.side-brand .nav-link{padding:12px 5px}
.sidebar-foot{margin:auto 0 0;color:#cce7d8;font-size:9px;letter-spacing:.13em}
.top-header{height:100px;background:var(--primary);color:white;display:flex;align-items:center;justify-content:space-between;gap:22px;padding:0 32px}
.top-header .site-nav{flex:1;max-width:660px}
.top-header .site-nav>ul{display:flex;align-items:center;justify-content:space-between;gap:14px}
.top-header .nav-item{flex:1;min-width:0}
.top-header .nav-row{justify-content:center}
.top-header .nav-link{text-align:center;white-space:nowrap}
.top-header .submenu{position:absolute;z-index:8;top:100%;min-width:145px;background:var(--primary);box-shadow:0 9px 14px #13223627}

왜 필요한가

오류 복구

메뉴가 너무 좁음 / aside 폭과 padding을 중복 계산했습니다. / box-sizing 및 .site-shell의 200px 설정을 확인합니다.

직접 확인

T19 모바일 화면

T20 / 28

본문 상단 제목 100px CSS

상단 안내의 크기와 여백을 완성합니다.

정상 결과: PC 본문의 제목 영역이 100px로 고정됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_HEADER */

.masthead{height:100px;background:linear-gradient(100deg,#f4f9f2,#e5eee2);color:var(--primary);padding:24px 30px;display:flex;align-items:center;justify-content:space-between;gap:12px}
.masthead span{letter-spacing:.16em;font-size:11px;font-weight:750}
.masthead strong{font-size:17px;letter-spacing:-.04em}

왜 필요한가

오류 복구

메인 상단이 너무 높음 / 상단 영역에 큰 padding을 추가했습니다. / masthead의 height와 box-sizing을 확인합니다.

직접 확인

T20 모바일 화면

T21 / 28

공지·갤러리·배너 3영역 CSS

콘텐츠 영역을 3열 카드로 정리합니다.

정상 결과: 공지와 배너, 바로가기가 PC에서 옆으로 나란히 보입니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_CONTENT */

.content-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr) minmax(0,.8fr);min-height:200px}
.news-panel{min-width:0;padding:20px 24px}
.tab-buttons{display:flex;gap:19px;border-bottom:1px solid #e1e5ea;margin-bottom:12px}
.tab-buttons button{background:transparent;border:0;border-bottom:3px solid transparent;padding:0 1px 10px;font-weight:850;color:#89949f}
.tab-buttons button[aria-selected=true]{color:var(--primary);border-color:var(--accent)}
.notice-list{padding:0;margin:0;list-style:none}
.notice-open{display:flex;align-items:center;justify-content:space-between;gap:12px;border:0;width:100%;background:transparent;padding:7px 0;text-align:left;font-size:11px;color:#344251}
.notice-open span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.notice-open time{color:#9cabb6;white-space:nowrap;font-size:10px}
.notice-open:hover span{text-decoration:underline}
.gallery-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
.gallery-item{min-width:0;display:block;font-size:10px;text-align:center}
.gallery-item img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;border-radius:4px}
.gallery-item span{display:block;margin-top:4px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
.story-card,.quick-card{padding:43px 23px 15px;display:flex;flex-direction:column;min-width:0;min-height:200px}
.story-card{background:var(--soft)}
.quick-card{background:var(--primary);color:white}
.story-card>span:first-child,.quick-card>span:first-child{font-weight:800;letter-spacing:.14em;font-size:10px;opacity:.72}
.story-card strong,.quick-card strong{font-size:17px;margin:8px 0 9px;letter-spacing:-.05em}
.story-card small,.quick-card small{font-size:11px;opacity:.76}
.card-arrow{font-size:10px;opacity:.8;margin-top:auto!important}

왜 필요한가

오류 복구

카드 폭이 지나치게 좁음 / 바깥 Grid를 풀지 않은 상태로 안쪽 카드만 여러 열로 나눴습니다. / 모바일에서는 .content-grid 2열/공지 1열 규칙을 확인합니다.

직접 확인

T21 모바일 화면

T22 / 28

푸터·팝업 최종 디자인 CSS

푸터와 팝업 가리개·닫기 버튼을 꾸밉니다.

정상 결과: 푸터 100px, 레이어 팝업 표시 스타일이 완성됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. css/style.css

Ctrl+F로 주석을 찾아 교체

찾을 주석: /* E02:CSS_FOOTPOP */

.site-footer{display:flex;gap:15px;justify-content:space-between;align-items:center;min-height:100px;padding:23px 32px;background:#f6f8fb;border-top:1px solid #e9edf0}
.site-footer strong{font-size:17px;display:block}
.site-footer small{font-size:10px;color:#96a3b0;display:block}
.footer-links{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px 15px;max-width:370px;font-size:10px;color:#8b9aab}
.footer-links small{width:100%;text-align:right}
.popup-backdrop{position:fixed;inset:0;background:#0a142bbf;z-index:100;display:grid;place-items:center;padding:20px}
.popup-card{position:relative;background:white;width:min(460px,100%);border-radius:8px;padding:34px;box-shadow:0 25px 75px #0005}
.popup-card h2{font-size:21px;margin:11px 0}
.popup-card p{color:#586977;font-size:13px}
.popup-label{color:var(--primary);font-weight:900;letter-spacing:.14em;font-size:10px}
.popup-close{position:absolute;right:18px;top:12px;background:transparent;border:0;font-size:26px}
.popup-ok{background:var(--primary);color:white;border:0;padding:9px 20px;border-radius:5px;margin-top:10px}

왜 필요한가

오류 복구

팝업이 콘텐츠 뒤에 숨음 / z-index가 낮거나 popup-backdrop 스타일이 누락됐습니다. / z-index와 fixed inset:0을 확인합니다.

직접 확인

T22 모바일 화면

T23 / 28

JavaScript 파일 기본 구조 준비

메뉴·좌우 슬라이드·탭·팝업을 차례로 추가할 준비를 합니다.

정상 결과: script.js에 실행 함수와 4개 표시 주석이 준비됩니다.

JS는 이 단계에서만 전체 입력하고 이후 주석을 하나씩 교체합니다.

1. js/script.js

빈 파일 전체에 입력

/* E02 - K-BOOKS 자체 제작 실기 참고 JS: 좌우 슬라이드 */
(()=>{
  'use strict';
  // E02:JS_SLIDER
  // E02:JS_MENU
  // E02:JS_TABS
  // E02:JS_POPUP
})();

왜 필요한가

오류 복구

스크립트 문법 오류 / 괄호 하나가 빠지거나 코드 전체를 복사하지 않았습니다. / 첫 부분 (()=>{와 마지막 })();를 확인합니다.

직접 확인

T23 모바일 화면

T24 / 28

좌우 슬라이드 JS·3초 이내 자동 반복

translateX로 슬라이드 3장을 좌우 이동시키고 버튼도 연결합니다.

정상 결과: 2.6초마다 그림이 좌우 이동하고 이전/다음/일시정지 버튼이 작동합니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. js/script.js

Ctrl+F로 주석을 찾아 교체

찾을 주석: // E02:JS_SLIDER

  const track=document.querySelector('#slideTrack');
  const slides=[...document.querySelectorAll('.slide')];
  const dots=[...document.querySelectorAll('.slider-dots button')];
  const pauseButton=document.querySelector('#pauseSlide');
  let current=0,paused=window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  let timer=null;
  function showSlide(next){
    current=(next+slides.length)%slides.length;
    // E02 과제의 핵심: 가로는 전체 폭의 100%씩 translateX 이동
    const offset=`translateX(-${current*100}%)`;
    track.style.transform=offset;
    dots.forEach((dot,i)=>{if(i===current)dot.setAttribute('aria-current','true');else dot.removeAttribute('aria-current');});
  }
  function stopTimer(){if(timer!==null)clearInterval(timer);timer=null;}
  function startTimer(){stopTimer();if(!paused&&slides.length>1)timer=setInterval(()=>showSlide(current+1),2600);}
  pauseButton.addEventListener('click',()=>{paused=!paused;pauseButton.setAttribute('aria-pressed',String(paused));pauseButton.textContent=paused?'재생':'일시정지';startTimer();});
  document.querySelector('#prevSlide').addEventListener('click',()=>showSlide(current-1));
  document.querySelector('#nextSlide').addEventListener('click',()=>showSlide(current+1));
  dots.forEach((dot,i)=>dot.addEventListener('click',()=>showSlide(i)));
  document.addEventListener('visibilitychange',()=>{if(document.hidden)stopTimer();else startTimer();});
  window.addEventListener('resize',()=>showSlide(current));
  pauseButton.setAttribute('aria-pressed',String(paused));
  pauseButton.textContent=paused?'재생':'일시정지';
  showSlide(0);startTimer();

왜 필요한가

오류 복구

상하로 움직이거나 그림이 안 변함 / translateY를 사용했거나 slideTrack ID가 다릅니다. / translateX와 #slideTrack을 확인합니다.

직접 확인

T24 모바일 화면

T25 / 28

모바일 메뉴와 하위 메뉴 열기·닫기

햄버거 버튼과 각 하위 메뉴의 펼침 상태를 연결합니다.

정상 결과: 390px에서 메뉴를 열고 하위 목록을 펼칠 수 있습니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. js/script.js

Ctrl+F로 주석을 찾아 교체

찾을 주석: // E02:JS_MENU

  const menuButton=document.querySelector('.mobile-menu'),menu=document.querySelector('#siteNav');
  menuButton.addEventListener('click',()=>{const open=menu.classList.toggle('open');menuButton.setAttribute('aria-expanded',String(open));});
  document.querySelectorAll('.submenu-toggle').forEach(button=>{
    button.addEventListener('click',()=>{const target=document.getElementById(button.getAttribute('aria-controls'));const open=button.getAttribute('aria-expanded')!=='true';button.setAttribute('aria-expanded',String(open));target.hidden=!open;});
  });

왜 필요한가

오류 복구

메뉴가 버튼을 눌러도 안 열림 / siteNav ID 또는 mobile-menu 클래스가 틀렸습니다. / HTML과 JS의 id/class를 서로 비교합니다.

직접 확인

T25 모바일 화면

T26 / 28

공지/갤러리 탭 전환 JS

공지와 갤러리 두 패널의 hidden을 전환합니다.

정상 결과: 탭을 클릭하면 공지와 갤러리가 번갈아 표시됩니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. js/script.js

Ctrl+F로 주석을 찾아 교체

찾을 주석: // E02:JS_TABS

  const tabs=[document.querySelector('#noticeTab'),document.querySelector('#galleryTab')];
  const panels=[document.querySelector('#noticePanel'),document.querySelector('#galleryPanel')];
  tabs.forEach((tab,i)=>tab.addEventListener('click',()=>{
    tabs.forEach((t,k)=>{t.setAttribute('aria-selected',String(i===k));t.tabIndex=i===k?0:-1;panels[k].hidden=i!==k;});
  }));

왜 필요한가

오류 복구

공지와 갤러리가 겹쳐 보여요 / hidden을 두 패널 모두에서 잘못 처리했습니다. / selected·hidden 조건 i===k를 확인합니다.

직접 확인

T26 모바일 화면

T27 / 28

공지 레이어 팝업 열기·닫기 JS

공지 클릭 시 팝업 표시, 닫기·확인·Esc를 연결합니다.

정상 결과: 공지 팝업이 열리고 닫기·확인·Esc로 닫힙니다.

이전 단계의 작성 내용을 지우지 말고 표시 주석만 찾아 바꾸세요. Ctrl+S 저장 후 Chrome에서 다시 확인하세요.

1. js/script.js

Ctrl+F로 주석을 찾아 교체

찾을 주석: // E02:JS_POPUP

  const popup=document.querySelector('#noticePopup'),popupTitle=document.querySelector('#popupTitle');
  const closeButton=document.querySelector('.popup-close');let opener=null;
  function closePopup(){popup.hidden=true;if(opener)opener.focus();}
  document.querySelectorAll('.notice-open').forEach(button=>button.addEventListener('click',()=>{opener=button;popupTitle.textContent=button.dataset.title||button.textContent.trim();popup.hidden=false;closeButton.focus();}));
  closeButton.addEventListener('click',closePopup);
  document.querySelector('.popup-ok').addEventListener('click',closePopup);
  popup.addEventListener('click',event=>{if(event.target===popup)closePopup();});
  document.addEventListener('keydown',event=>{if(event.key==='Escape'&&!popup.hidden)closePopup();});

왜 필요한가

오류 복구

팝업이 닫히지 않음 / popup-close 또는 popup-ok 요소 이름이 맞지 않습니다. / HTML의 class와 JS querySelector를 비교합니다.

직접 확인

T27 모바일 화면

T28 / 28

최종 PC·모바일·오프라인 제출 점검

문제지와 파일 구조·버튼 기능·제출 용량을 확인합니다.

정상 결과: PC 1200px에서 사이드바200/본문1000, 상단100/슬라이드300/푸터100이며 모바일에서 겹침이 없습니다.

모의시험 모드에는 학습 단계와 정답 코드를 표시하지 않습니다.

1. Chrome · 제출 전 체크

검사

① index.html과 css/style.css, js/script.js 상대경로 확인
② images 7개 로딩, 콘솔 오류 없음
③ PC 1200px: 사이드바 200px / 본문 1000px
④ 상단 100px / 슬라이드 300px / 콘텐츠 200px / 푸터 100px 목표 확인
⑤ 슬라이드 2.6초 자동 좌우 이동, 이전/다음/일시정지
⑥ 모바일 메뉴·하위 메뉴·공지/갤러리 탭·팝업/닫기
⑦ 인터넷 끊고 index.html을 직접 열어 정상 작동
⑧ 결과 폴더 10MB 이하, 제출 경로는 시험 지시를 따름
⑨ 다음 단계: 힌트 없는 180분 모의시험 도전

왜 필요한가

오류 복구

다른 PC에서 이미지가 깨짐 / index.html만 복사해 css/js/images 폴더가 누락됐습니다. / 전체 프로젝트 폴더의 상대경로와 파일 누락을 확인합니다.

직접 확인

T28 모바일 화면