실습 개요
목표: 지금까지 배운 HTML 태그와 CSS를 한 페이지에 모두 모아 완성형 랜딩 페이지를 만든다.
사용 범위: 시맨틱 태그 · 기본 선택자(태그/아이디/클래스) · 조합 선택자(후손/자식/인접 형제) · 색상과 배경 · 박스 모델 · 표/폼 꾸미기
진행 방식: HTML 구조는 미리 제공된다. 여러분은 비어 있는
<style>안에 CSS만 작성하면 된다.
이번 실습에서는 "주말 코딩 스터디" 회원을 모집하는 한 페이지짜리 랜딩 페이지를 완성한다. HTML 뼈대는 이미 짜여 있으니, 단계별 요구사항을 따라 CSS를 채워 가며 밋밋한 문서를 보기 좋은 페이지로 바꿔 보자.
페이지는 아래 5개 영역으로 구성된다.
┌──────────────────────────────────────────┐
│ <header> 제목 + 소개문구 + <nav> 메뉴 │ ← 머리말
├──────────────────────────────────────────┤
│ <main> │
│ <section> 스터디 소개 (텍스트 + 이미지) │
│ <section> 일정·회비 (표 table) │
│ <section> 활동 내용 (목록 ul) │
│ <section> 신청하기 (폼 form) │
├──────────────────────────────────────────┤
│ <footer> 저작권 · 문의 │ ← 꼬리말
└──────────────────────────────────────────┘완성된 모습 (목표)
먼저 우리가 최종적으로 만들 결과물을 확인하자. 아래는 모든 단계를 끝낸 완성본이다. 실습 중간중간 "내가 지금 어디로 가고 있는지" 헷갈릴 때 이 화면을 기준으로 삼으면 된다.
🔗 완성본 새 탭에서 크게 보기
지금은 '목표 화면'만 눈에 담아 두세요
위 화면은 여러분이 직접 만들 결과물이다. 코드를 그대로 따라 적기보다, "머리말은 보라색 그라데이션, 본문은 흰 카드, 표는 선이 하나로 합쳐짐" 같은 전체 인상을 먼저 파악하고 단계로 들어가자. 각 단계의 정답 코드는 아래에서 하나씩 펼쳐 볼 수 있다.
0. 실습 준비
작업 폴더를 하나 새로 만든다. (예:
study-signup)VS Code로 그 폴더를 열고, 그 안에
index.html파일을 만든다.아래 시작용 HTML을 통째로 복사해
index.html에 붙여 넣는다.Live Server로 띄워 두고,
<style>안을 채울 때마다 브라우저에서 결과를 확인하며 진행한다.
이 실습에서 HTML은 건드리지 않는다
HTML 구조는 이미 완성돼 있다. 우리가 채울 곳은 <head> 안 <style> ... </style> 사이뿐이다. (여유가 되면 <!-- TODO --> 주석 부분의 HTML을 직접 채워 봐도 좋다.)
시작용 HTML (그대로 복사하세요)
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>종합 실습 - 주말 코딩 스터디 모집</title>
<!-- (선택) 웹 폰트: 제목용. 인터넷이 없으면 맑은 고딕으로 대체됩니다. -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&display=swap" rel="stylesheet">
<style>
/* ===== 여기에 CSS를 작성하세요 ===== */
</style>
</head>
<body>
<!-- ===== 머리말 ===== -->
<header>
<h1 id="main-title">주말 코딩 스터디</h1>
<p class="tagline">비전공자도 환영! 매주 토요일, 함께 성장해요 🚀</p>
<nav>
<ul class="menu">
<li><a href="#about">소개</a></li>
<li><a href="#schedule">일정·회비</a></li>
<li><a href="#activities">활동</a></li>
<li><a href="#apply">신청</a></li>
</ul>
</nav>
</header>
<!-- ===== 본문 ===== -->
<main>
<!-- 소개: 텍스트 태그 + 이미지 -->
<section id="about">
<h2>스터디 소개</h2>
<p>매주 토요일 오후, 카페에 모여 웹 개발을 공부하는 모임입니다.</p>
<p>
<strong>혼자 공부하다 지치셨나요?</strong>
함께라면 <em>꾸준함</em>이 쉬워집니다. HTML/CSS부터 차근차근 시작합니다.
</p>
<img class="intro-img" src="https://placehold.co/700x260?text=Weekend+Coding+Study"
alt="주말 코딩 스터디 모임 사진">
</section>
<!-- 일정/회비: 표 -->
<section id="schedule">
<h2>모집 일정 & 회비</h2>
<p>아래 일정을 확인하고 신청해 주세요.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>내용</th>
</tr>
</thead>
<tbody>
<tr><td>모집 인원</td><td>10명 (선착순)</td></tr>
<tr><td>모임 시간</td><td>매주 토요일 14:00 ~ 17:00</td></tr>
<tr><td>장소</td><td>강남역 스터디 카페</td></tr>
<!-- TODO(선택): '회비' 행을 직접 한 줄 추가해보세요 -->
</tbody>
</table>
</section>
<!-- 활동: 목록 -->
<section id="activities">
<h2>이런 걸 함께 해요</h2>
<p>입문자 눈높이에 맞춘 커리큘럼으로 진행합니다.</p>
<ul>
<li>HTML/CSS로 나만의 웹 페이지 만들기</li>
<li>매주 작은 미션 & 코드 리뷰</li>
<li>JavaScript 맛보기와 미니 프로젝트</li>
<!-- TODO(선택): 활동 항목을 하나 더 추가해보세요 -->
</ul>
</section>
<!-- 신청: 폼 -->
<section id="apply">
<h2>신청하기</h2>
<p>아래 양식을 작성하면 신청이 완료됩니다.</p>
<form>
<div class="form-row">
<label for="name">이름 <span class="required">*</span></label>
<input class="input" type="text" id="name" name="name" placeholder="홍길동" required>
</div>
<div class="form-row">
<label for="email">이메일 <span class="required">*</span></label>
<input class="input" type="email" id="email" name="email" placeholder="example@email.com" required>
</div>
<div class="form-row">
<label>관심 분야</label><br>
<label><input type="radio" name="topic" value="web" checked> 웹 개발</label>
<label><input type="radio" name="topic" value="data"> 데이터 분석</label>
<label><input type="radio" name="topic" value="app"> 앱 개발</label>
</div>
<div class="form-row">
<label>참여 가능 시간</label><br>
<label><input type="checkbox" name="time" value="am"> 오전</label>
<label><input type="checkbox" name="time" value="pm" checked> 오후</label>
</div>
<div class="form-row">
<label for="msg">하고 싶은 말</label>
<textarea class="input" id="msg" name="msg" rows="4" placeholder="자유롭게 적어주세요"></textarea>
</div>
<div class="form-row">
<label><input type="checkbox" required> 개인정보 수집에 동의합니다 <span class="required">*</span></label>
</div>
<button type="submit" class="btn">신청서 제출</button>
</form>
</section>
</main>
<!-- ===== 꼬리말 ===== -->
<footer>
<p>© 2026 주말 코딩 스터디 · 문의: study@example.com</p>
</footer>
</body>
</html>정답 코드는 숨겨져 있어요
각 단계의 "정답 코드 보기" 박스는 기본적으로 접혀 있다. 먼저 스스로 작성해 본 뒤, 막히거나 다 풀고 확인할 때 박스를 클릭해서 펼쳐 보자. 정답을 보기 전에 직접 끙끙대 보는 시간이 가장 많이 남는다.
1단계. 페이지 기본값 + 머리말(header) 꾸미기
가장 먼저 페이지 전체의 분위기를 정하는 기본값과, 맨 위 머리말(header) 을 꾸민다.
요구사항
[태그 선택자]
body에 글꼴("맑은 고딕"), 글자색, 줄 간격(line-height), 은은한 배경색을 준다.[색상/배경]
header에 배경색(또는 그라데이션)을 주고 글자색을 흰색으로, 가운데 정렬한다.[아이디 선택자]
#main-title에 큰 글씨를 적용한다. (페이지에 하나뿐인 대표 제목)[클래스 선택자]
.tagline(소개 문구)을 살짝 작고 연하게 만든다.
왜 #main-title은 아이디고 .tagline은 클래스인가요?
대표 제목은 페이지에 딱 하나 → 아이디(#). 소개 문구·버튼·필수표시처럼 여러 번 재사용할 수 있는 스타일 → 클래스(.). 이 구분을 의식하며 작성하는 게 이번 실습의 핵심이다.
정답 코드 보기 (클릭해서 펼치기)
/* [CSS 기초] 전체 페이지 기본값 */
body {
font-family: "맑은 고딕", "Malgun Gothic", sans-serif;
color: #333;
line-height: 1.7;
margin: 0;
background-color: #f5f5f7;
}
/* [시맨틱] header — 머리말 / [색상] 그라데이션 배경 */
header {
background-image: linear-gradient(135deg, #7b1fa2, #5e35b1);
color: white;
text-align: center;
padding: 48px 20px 0;
}
/* [아이디 선택자] 페이지에 하나뿐인 대표 제목 */
#main-title {
font-family: "Black Han Sans", "맑은 고딕", sans-serif;
font-size: 40px;
margin: 0;
}
/* [클래스 선택자] 재사용 가능한 소개 문구 */
.tagline {
font-size: 18px;
opacity: 0.9;
}2단계. 내비게이션 메뉴 (후손 · 자식 선택자)
머리말 안 메뉴(nav) 를 가로로 정렬하고 링크 모양을 다듬는다. 이번 단계의 목표는 후손 선택자와 자식 선택자의 차이를 손으로 익히는 것이다.
요구사항
[후손 선택자
nav a]nav안의 모든 링크의 밑줄을 없애고 글자색을 바꾼다. (text-decoration: none)[자식 선택자
.menu > li].menu의 직계<li>를 가로로 나열하고(display: inline-block) 목록 점(list-style)을 없앤다.메뉴 전체(
.menu)의 기본 여백을 없애고 가운데 정렬한다.
완성 예시
nav a(후손)와 .menu > li(자식)의 차이
nav a→nav안에 있는 모든 깊이의<a>에 적용 (후손)..menu > li→.menu바로 한 단계 아래<li>에만 적용 (직계 자식). 이 차이를 코드로 설명할 수 있어야 한다.
정답 코드 보기 (클릭해서 펼치기)
/* [시맨틱] nav — 메뉴 영역 */
nav {
background-color: rgba(0, 0, 0, 0.15);
margin-top: 24px;
}
.menu {
margin: 0;
padding: 0;
text-align: center;
}
/* [자식 선택자 A > B] .menu의 '직계' li 만 가로 배치 */
.menu > li {
display: inline-block;
list-style: none;
}
/* [후손 선택자 A B] nav 안의 '모든' 링크 스타일 */
nav a {
display: inline-block;
padding: 14px 18px;
color: white;
text-decoration: none; /* 링크 밑줄 제거 */
font-weight: bold;
}3단계. 본문 레이아웃 + 카드 (박스 모델)
본문(main)을 화면 가운데로 모으고, 각 section을 카드처럼 만든다. 제목(h2)과 요약문도 다듬는다.
요구사항
[박스 모델]
main에max-width: 760px; margin: 0 auto;를 줘 본문을 가운데 정렬한다.[박스 모델]
section에padding·margin·border-radius·box-shadow를 줘 카드처럼 만든다. (배경은 흰색)[태그 선택자] 모든
h2에 공통 색과 아래 테두리(border-bottom)를 준다.[인접 형제
h2 + p] 각 제목 바로 다음 문단(요약문)만 회색 기울임으로 강조한다.소개 이미지(
.intro-img)가 카드 너비에 꽉 차도록width: 100%를 준다.
h2 + p는 '바로 다음' 하나만
인접 형제 선택자 h2 + p는 h2 직후에 붙어 있는 첫 <p> 하나에만 적용된다. 두 번째, 세 번째 문단은 회색이 되면 안 된다. 적용 결과를 브라우저에서 꼭 확인하자.
정답 코드 보기 (클릭해서 펼치기)
/* [태그 선택자] 페이지 안의 모든 h2 */
h2 {
color: #5e35b1;
border-bottom: 2px solid #ede7f6; /* [박스모델] 아래 테두리 */
padding-bottom: 8px;
}
/* [인접 형제 A + B] h2 '바로 다음' 첫 문단(요약문)만 */
h2 + p {
color: #777;
font-style: italic;
}
/* [시맨틱] main — 본문을 가운데로 */
main {
max-width: 760px;
margin: 0 auto; /* 좌우 auto → 가운데 정렬 */
padding: 20px;
}
/* [박스 모델] section을 카드처럼 */
section {
background-color: white;
padding: 24px;
margin-bottom: 24px;
border-radius: 12px; /* 둥근 모서리 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); /* 그림자 */
}
/* 소개 이미지 */
.intro-img {
width: 100%;
border-radius: 8px;
display: block;
margin-top: 12px;
}4단계. 일정·회비 표 꾸미기
schedule 영역의 표(table) 를 깔끔하게 다듬는다.
요구사항
[표 꾸미기]
table에width: 100%와border-collapse: collapse를 준다.th,td에border와padding을 줘 칸을 보기 좋게 만든다.머리 행(
thead th)에 배경색을 줘 본문과 구분한다.
완성 예시
구분 | 내용 |
|---|---|
모집 인원 | 10명 (선착순) |
모임 시간 | 매주 토요일 14:00 ~ 17:00 |
장소 | 강남역 스터디 카페 |
border-collapse: collapse가 없으면?
이 속성이 없으면 셀마다 테두리가 따로 그려져 선이 이중으로 보인다. collapse는 맞닿은 셀 선을 하나로 합쳐 준다.
정답 코드 보기 (클릭해서 펼치기)
/* [표 꾸미기] 일정/회비 표 */
table {
width: 100%;
border-collapse: collapse; /* 셀 사이 이중선을 하나로 합침 */
}
th, td {
border: 1px solid #ddd;
padding: 10px 12px;
text-align: center;
}
thead th {
background-color: #ede7f6; /* 머리 행 배경 */
color: #5e35b1;
}5단계. 신청 폼 + 버튼 꾸미기
apply 영역의 폼(form) 입력칸과 제출 버튼을 다듬는다. 이번 단계의 핵심은 글자 입력칸에만 스타일을 주는 것이다.
요구사항
[클래스 선택자] 글자 입력칸에만 붙은
.input(이름·이메일·하고 싶은 말)에width: 100%·padding·border를 준다..input에box-sizing: border-box를 줘 입력칸이 칸을 넘치지 않게 한다.[클래스 선택자]
.btn(제출 버튼)에 배경색 · 글자색 · 둥근 모서리 ·cursor: pointer를 준다.[클래스 선택자]
.required(필수 표시*)를 빨간색으로 만든다.
라디오·체크박스는 건드리지 않는다
.input 클래스는 글자 입력칸(text/email/textarea)에만 붙어 있다. 라디오·체크박스에는 .input이 없으므로 width: 100%의 영향을 받지 않는다. 선택자를 클래스로 좁힌 덕분이다.
입력칸이 오른쪽으로 넘쳐요
width: 100%에 padding까지 더해지면 칸을 넘친다. box-sizing: border-box;를 주면 padding이 너비 안에 포함돼 넘침이 사라진다. (1단계 박스 모델에서 배운 내용)
정답 코드 보기 (클릭해서 펼치기)
/* [폼 꾸미기] 신청 폼 */
.form-row { margin-bottom: 16px; }
label { display: inline-block; font-weight: bold; margin-bottom: 4px; }
/* [클래스 선택자] 글자 입력칸(.input)만 넓힘
💬 라디오·체크박스엔 .input이 없어서 영향 없음 */
.input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 6px;
box-sizing: border-box; /* padding을 width 안에 포함 → 넘침 방지 */
font-family: inherit; /* 본문과 같은 글꼴 */
}
/* 제출 버튼 */
.btn {
background-color: #5e35b1;
color: white;
border: none;
padding: 12px 28px;
border-radius: 999px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
}
/* 필수 표시(*) — 클래스 재사용 */
.required { color: crimson; }
/* [시맨틱] footer — 꼬리말 */
footer {
text-align: center;
color: #999;
padding: 24px;
font-size: 14px;
}전체 완성 코드
모든 단계를 합친 최종 <style>이다. 먼저 스스로 끝까지 만들어 본 뒤 펼쳐서 내 코드와 비교해 보자.
전체 정답 <style> 보기 (클릭해서 펼치기)
/* ===== [CSS 기초] 전체 페이지 기본값 ===== */
body {
font-family: "맑은 고딕", "Malgun Gothic", sans-serif;
color: #333;
line-height: 1.7;
margin: 0;
background-color: #f5f5f7;
}
/* [태그 선택자] 모든 h2 */
h2 {
color: #5e35b1;
border-bottom: 2px solid #ede7f6;
padding-bottom: 8px;
}
/* [인접 형제] h2 바로 다음 요약문 */
h2 + p {
color: #777;
font-style: italic;
}
/* ===== [시맨틱] header ===== */
header {
background-image: linear-gradient(135deg, #7b1fa2, #5e35b1);
color: white;
text-align: center;
padding: 48px 20px 0;
}
#main-title { /* [아이디] */
font-family: "Black Han Sans", "맑은 고딕", sans-serif;
font-size: 40px;
margin: 0;
}
.tagline { /* [클래스] */
font-size: 18px;
opacity: 0.9;
}
/* ===== [시맨틱] nav / [조합 선택자] ===== */
nav {
background-color: rgba(0, 0, 0, 0.15);
margin-top: 24px;
}
.menu {
margin: 0;
padding: 0;
text-align: center;
}
.menu > li { /* [자식 선택자] */
display: inline-block;
list-style: none;
}
nav a { /* [후손 선택자] */
display: inline-block;
padding: 14px 18px;
color: white;
text-decoration: none;
font-weight: bold;
}
/* ===== [시맨틱] main / section ===== */
main {
max-width: 760px;
margin: 0 auto;
padding: 20px;
}
section { /* [박스 모델] 카드 */
background-color: white;
padding: 24px;
margin-bottom: 24px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.intro-img {
width: 100%;
border-radius: 8px;
display: block;
margin-top: 12px;
}
/* ===== [표 꾸미기] ===== */
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 10px 12px;
text-align: center;
}
thead th {
background-color: #ede7f6;
color: #5e35b1;
}
/* ===== [폼 꾸미기] ===== */
.form-row { margin-bottom: 16px; }
label { display: inline-block; font-weight: bold; margin-bottom: 4px; }
.input { /* [클래스] 글자 입력칸만 */
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 6px;
box-sizing: border-box;
font-family: inherit;
}
.btn {
background-color: #5e35b1;
color: white;
border: none;
padding: 12px 28px;
border-radius: 999px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
}
.required { color: crimson; }
/* ===== [시맨틱] footer ===== */
footer {
text-align: center;
color: #999;
padding: 24px;
font-size: 14px;
}도전 과제
여유가 있다면 아래를 추가해 본다. 모두 지금까지 배운 선택자로 가능하다.
[가상 클래스]
nav a:hover로 마우스를 올린 메뉴의 색을 바꿔 본다.[가상 클래스
:nth-child]tbody tr:nth-child(2n)로 표의 짝수 줄에 배경색을 줘 얼룩말 무늬를 만든다.[가상 클래스]
.btn:hover에 색·그림자를 더해 버튼이 눌리는 느낌을 만든다.시작용 HTML의
<!-- TODO -->부분(표의 '회비' 행, 활동 항목 하나)을 직접 채운다.
제출 / 점검 체크리스트
완성했다면 아래를 스스로 점검한다.
header→nav→main(section 4개) →footer구조가 그대로 살아 있고, 각 영역이 의도대로 꾸며졌다.메뉴 링크를 누르면 해당
section으로 스크롤 이동한다. (href="#id"↔id)nav a(후손)와.menu > li(자식)의 차이를 코드로 설명할 수 있다.각
section제목 바로 다음 문단만 회색 기울임이 되었다. (h2 + p)표의 선이 깔끔하게 하나로 합쳐졌다. (
border-collapse)폼 입력칸이 카드를 넘치지 않는다. (
box-sizing: border-box)라디오·체크박스는
width: 100%의 영향을 받지 않았다. (.input클래스로 범위를 좁힘)이미지에 의미 있는
alt가 있다.