이번 편의 결과물: 디자인 시안과 자신의 무스타일 마크업을 나란히 확인하고, css_fundamentals 01편에서 이어받을 파일 경로를 안내받는다. · 다루는 개념: 마크업이 스타일링 대상이 되는 방식, 디자인 시안과 현재 마크업 비교, class/id 네이밍이 선택자 설계에 미치는 영향
이 편에서 만드는 파일
web-practice/portfolio/
├ index.html (최종 상태 확인 — 이 편의 정본 코드)
├ projects.html (최종 상태 확인 — 이 편의 정본 코드)
└ contact.html (최종 상태 확인 — 이 편의 정본 코드)개념 정리
지금까지 만든 세 파일에는 스타일이 하나도 없지만, 요소마다 class가 이미 붙어 있다. 이 class들은 css_fundamentals가 선택자로 그대로 쓸 이름이다. 지금 이름을 지어 둔 덕분에 다음 과목은 마크업을 다시 건드리지 않고 css/tokens.css·css/styles.css 두 파일만 새로 만들면 된다.
| 지금 붙인 class/id | css_fundamentals에서 하는 일 |
|---|---|
.site-header | 상단 고정(position: sticky), 높이·배경·테두리 지정 |
.logo | 글자 크기·굵기 지정 |
.site-nav | 가로 배치(Flexbox), 링크 간격 지정 |
.hero | 세로 여백, 제목 크기, 버튼 스타일 |
.about | 프로필 사진과 문단의 배치(Flexbox/Grid) |
.skills | 정의 목록을 카드형으로 배치 |
.career | 표 헤더 배경, 행 구분선 |
.project-grid | Grid 레이아웃(모바일 1열 → 데스크톱 3열) |
.project-card | 배경·테두리·hover 그림자 전환 |
.contact-form | 필드 간격, 포커스 링, 오류 상태 테두리 |
.site-footer | 배경·여백·SNS 링크 배치 |
css_fundamentals는 이 이름들의 뜻을 바꾸지 않는다. .project-card는 계속 프로젝트 카드를 가리키고, CSS는 그 카드를 어떻게 보여줄지만 정한다. 이것이 HTML(구조)과 CSS(표현)를 나누는 이유다.
시안(design-spec.md)과 지금 마크업을 나란히 보면 색·간격·글꼴이 전부 비어 있을 뿐, 어떤 콘텐츠가 어디 있는지는 이미 시안과 같은 순서다. 다음 과목은 이 순서를 바꾸지 않고 겉모습만 입힌다.
실습
1. 디자인 시안과 현재 마크업 나란히 보기
public/practice/portfolio/design-spec.md의 “1. 사이트 구성” 표와 지금 작성한 세 파일의 섹션 순서를 비교한다. index.html은 히어로 → 소개 → 기술 스택 → 경력, projects.html은 카드 그리드, contact.html은 폼 → 연락처 정보 순서로 이미 일치한다.
2. 세 파일의 완성 상태를 정본으로 확인
아래는 이 시점의 index.html·projects.html·contact.html 전체 코드다. 지금까지 작업한 파일과 한 줄씩 대조한다.
<!-- web-practice/portfolio/index.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Zeno Kim — 소개</title>
</head>
<body>
<header class="site-header">
<a class="logo" href="index.html">Zeno Kim</a>
<nav class="site-nav" aria-label="주 메뉴">
<ul>
<li><a href="index.html" aria-current="page">소개</a></li>
<li><a href="projects.html">프로젝트</a></li>
<li><a href="contact.html">연락</a></li>
</ul>
</nav>
</header>
<main>
<section class="hero" id="hero">
<h1>웹을 만드는 개발자, Zeno Kim입니다.</h1>
<p>HTML·CSS·JavaScript로 누구나 쓸 수 있는 화면을 만듭니다.</p>
<a class="button" href="projects.html">프로젝트 보기</a>
<a class="button" href="contact.html">연락하기</a>
</section>
<section class="about" id="about">
<h2>소개</h2>
<img src="images/profile.svg" alt="Zeno Kim의 프로필 사진" width="400" height="400" />
<p>
저는 3년째 프론트엔드를 공부하고 만들고 있습니다. 화면이 예뻐 보이는 것보다
<strong>누가 봐도 쓸 수 있는가</strong>를 먼저 생각합니다.
</p>
<p>
처음에는 디자인 툴로 화면을 그리다가, 직접 만들어 보고 싶어서 HTML과 CSS를
배웠습니다. 지금은 React로 서비스 화면을 만들고 있습니다.
</p>
<p>접근성과 웹 표준을 좋아합니다. 스크린 리더로 제 사이트를 읽어 보는 일이 습관이 됐습니다.</p>
</section>
<section class="skills" id="skills">
<h2>기술 스택</h2>
<dl>
<dt>마크업·스타일</dt>
<dd>HTML</dd>
<dd>CSS</dd>
<dd>Sass</dd>
<dt>언어</dt>
<dd>JavaScript</dd>
<dd>TypeScript</dd>
<dt>프레임워크</dt>
<dd>React</dd>
<dd>Next.js</dd>
<dt>도구</dt>
<dd>Git</dd>
<dd>Vite</dd>
<dd>Figma</dd>
</dl>
</section>
<section class="career" id="career">
<h2>경력</h2>
<table>
<caption>Zeno Kim의 경력 요약</caption>
<thead>
<tr>
<th scope="col">기간</th>
<th scope="col">회사</th>
<th scope="col">역할</th>
<th scope="col">주요 업무</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">2024.03 – 현재</th>
<td>스튜디오 파도</td>
<td>프론트엔드 개발자</td>
<td>사내 디자인 시스템 컴포넌트 구현, 접근성 개선</td>
</tr>
<tr>
<th scope="row">2023.01 – 2024.02</th>
<td>오늘의 기록</td>
<td>주니어 개발자</td>
<td>기록 서비스 웹 화면 개발, 반응형 전환</td>
</tr>
<tr>
<th scope="row">2022.06 – 2022.12</th>
<td>코드포올 부트캠프</td>
<td>수료생</td>
<td>팀 프로젝트 3건, 발표 우수상</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4">2022년부터 현재까지 총 3개 조직에서 경력을 쌓았습니다.</td>
</tr>
</tfoot>
</table>
</section>
</main>
<footer class="site-footer">
<p>© 2026 Zeno Kim. 이 사이트는 학습용으로 제작되었습니다.</p>
<nav aria-label="소셜 링크">
<ul>
<li><a href="https://github.com/example" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="https://www.linkedin.com/in/example" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
<li><a href="https://blog.example.com" target="_blank" rel="noopener noreferrer">Blog</a></li>
</ul>
</nav>
</footer>
</body>
</html><!-- web-practice/portfolio/projects.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Zeno Kim — 프로젝트</title>
</head>
<body>
<header class="site-header">
<a class="logo" href="index.html">Zeno Kim</a>
<nav class="site-nav" aria-label="주 메뉴">
<ul>
<li><a href="index.html">소개</a></li>
<li><a href="projects.html" aria-current="page">프로젝트</a></li>
<li><a href="contact.html">연락</a></li>
</ul>
</nav>
</header>
<main>
<h1>프로젝트</h1>
<p>직접 만들고 배운 것들을 모았습니다.</p>
<div class="project-grid">
<article class="project-card" data-project-id="1">
<img src="images/project-01.svg" alt="오늘의 날씨 프로젝트 화면" width="1200" height="800" />
<h2>오늘의 날씨</h2>
<p>위치 기반 날씨를 카드로 보여주는 웹앱입니다. 오프라인에서도 마지막 데이터를 보여줍니다.</p>
<p>핵심 파일은 <code>weather-app/src/geo.js</code>입니다.</p>
<pre><code>navigator.geolocation.getCurrentPosition((pos) => {
fetchWeather(pos.coords.latitude, pos.coords.longitude);
});</code></pre>
<p>API는 <code>/api/weather?lat=1&lon=2</code> 형태로 호출됩니다.</p>
<blockquote cite="https://example.com/reviews/weather">
<p>실제 위치 기반 정보가 정확해서 매일 씁니다.</p>
<footer>— 박서연, <cite>같은 팀 동료</cite></footer>
</blockquote>
<a href="https://example.com/weather" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
<article class="project-card" data-project-id="2">
<img src="images/project-02.svg" alt="독서 기록 bookshelf 프로젝트 화면" width="1200" height="800" />
<h2>독서 기록 bookshelf</h2>
<p>읽은 책과 별점을 기록하고 통계를 보는 React 앱입니다.</p>
<p>통계는 <code>booksReducer.js</code>의 <code>reduce</code> 호출로 계산합니다.</p>
<pre><code><BookCard rating={5} /></code></pre>
<blockquote cite="https://example.com/reviews/bookshelf">
<p>다 읽은 책을 한눈에 정리해 주니 독서 습관 관리가 쉬워졌습니다.</p>
<footer>— 김도윤, <cite>베타 사용자</cite></footer>
</blockquote>
<a href="https://example.com/bookshelf" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
<article class="project-card" data-project-id="3">
<img src="images/project-03.svg" alt="메모 앱 notes 프로젝트 화면" width="1200" height="800" />
<h2>메모 앱 notes</h2>
<p>브라우저 저장소만으로 동작하는 메모 앱입니다. 검색은 Worker가 담당합니다.</p>
<p>검색 로직은 <code>search.worker.js</code>에 있습니다.</p>
<blockquote cite="https://example.com/reviews/notes">
<p>메모가 몇백 개여도 검색이 즉시 됩니다.</p>
<footer>— 이하은, <cite>베타 사용자</cite></footer>
</blockquote>
<a href="https://example.com/notes" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
<article class="project-card" data-project-id="4">
<img src="images/project-04.svg" alt="접근성 체크리스트 프로젝트 화면" width="1200" height="800" />
<h2>접근성 체크리스트</h2>
<p>WCAG 항목을 하나씩 점검하는 정적 사이트입니다.</p>
<p>항목마다 <code>role</code>과 <code>aria-*</code> 속성 예시를 보여줍니다.</p>
<pre><code><button aria-pressed="false">저장됨</button></code></pre>
<blockquote cite="https://example.com/reviews/a11y">
<p>사내 접근성 교육 자료로 그대로 썼습니다.</p>
<footer>— 정민재, <cite>같은 팀 동료</cite></footer>
</blockquote>
<a href="https://example.com/a11y" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
<article class="project-card" data-project-id="5">
<img src="images/project-05.svg" alt="디자인 토큰 미리보기 프로젝트 화면" width="1200" height="800" />
<h2>디자인 토큰 미리보기</h2>
<p>CSS 변수로 만든 색·간격 토큰을 한 화면에서 비교합니다.</p>
<p>토큰은 <code>--color-primary</code>처럼 커스텀 속성으로 정의합니다.</p>
<blockquote cite="https://example.com/reviews/tokens">
<p>디자인 시스템 문서로 참고하기 좋습니다.</p>
<footer>— 오지훈, <cite>디자이너</cite></footer>
</blockquote>
<a href="https://example.com/tokens" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
<article class="project-card" data-project-id="6">
<img src="images/project-06.svg" alt="todo-cli 프로젝트 화면" width="1200" height="800" />
<h2>todo-cli</h2>
<p>터미널에서 쓰는 할 일 관리 도구입니다. 파일에 저장하고 통계를 냅니다.</p>
<p>
터미널에 <kbd>todo add "책 읽기"</kbd>를 입력하면 <samp>할 일이 추가되었습니다</samp>가 출력됩니다.
</p>
<blockquote cite="https://example.com/reviews/todo-cli">
<p>마우스 없이 할 일을 관리하니 더 빠릅니다.</p>
<footer>— 최유나, <cite>베타 사용자</cite></footer>
</blockquote>
<a href="https://example.com/todo-cli" target="_blank" rel="noopener noreferrer">바로가기</a>
</article>
</div>
</main>
<footer class="site-footer">
<p>© 2026 Zeno Kim. 이 사이트는 학습용으로 제작되었습니다.</p>
<nav aria-label="소셜 링크">
<ul>
<li><a href="https://github.com/example" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="https://www.linkedin.com/in/example" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
<li><a href="https://blog.example.com" target="_blank" rel="noopener noreferrer">Blog</a></li>
</ul>
</nav>
</footer>
</body>
</html><!-- web-practice/portfolio/contact.html -->
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Zeno Kim — 연락</title>
</head>
<body>
<header class="site-header">
<a class="logo" href="index.html">Zeno Kim</a>
<nav class="site-nav" aria-label="주 메뉴">
<ul>
<li><a href="index.html">소개</a></li>
<li><a href="projects.html">프로젝트</a></li>
<li><a href="contact.html" aria-current="page">연락</a></li>
</ul>
</nav>
</header>
<main>
<h1>연락</h1>
<p>협업·채용 제안은 아래 폼으로 보내 주세요. 보통 2일 안에 답합니다.</p>
<div class="contact-form" id="contact-form">
<form method="post" action="contact.html">
<fieldset>
<legend>연락 정보 입력</legend>
<div class="form-field">
<label for="name">이름</label>
<input type="text" id="name" name="name" required />
</div>
<div class="form-field">
<label for="email">이메일</label>
<input type="email" id="email" name="email" required />
<p class="form-help">답장을 받을 주소를 적어 주세요.</p>
</div>
<div class="form-field">
<label for="inquiry-type">문의 유형</label>
<select id="inquiry-type" name="inquiryType">
<option value="collab">협업 제안</option>
<option value="hiring">채용 제안</option>
<option value="etc" selected>기타</option>
</select>
</div>
<div class="form-field">
<label for="message">메시지</label>
<textarea id="message" name="message" minlength="10" required></textarea>
<p class="form-help">프로젝트 내용이나 일정을 간단히 적어 주세요.</p>
</div>
<div class="form-field">
<label for="agree">
<input type="checkbox" id="agree" name="agree" required />
개인정보 수집·이용에 동의합니다
</label>
</div>
<button type="submit">보내기</button>
</fieldset>
</form>
</div>
<aside class="faq">
<!-- modern_html: FAQ 아코디언 -->
</aside>
</main>
<footer class="site-footer">
<p>© 2026 Zeno Kim. 이 사이트는 학습용으로 제작되었습니다.</p>
<nav aria-label="소셜 링크">
<ul>
<li><a href="https://github.com/example" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="https://www.linkedin.com/in/example" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
<li><a href="https://blog.example.com" target="_blank" rel="noopener noreferrer">Blog</a></li>
</ul>
</nav>
</footer>
</body>
</html>3. 다음 과목 출발점 확인
css_fundamentals 01편은 이 세 파일을 그대로 이어받는다. 새로 만들 파일은 web-practice/portfolio/css/tokens.css와 css/styles.css 두 개뿐이고, 각 HTML 파일의 head에 link 태그를 추가하는 것이 첫 작업이다. 지금 만든 class·id는 손대지 않는다.
4. 확인
- 시안(
design-spec.md)의 섹션 순서와 지금 마크업의 섹션 순서가 일치한다 - 위 세 코드 블록과 자신의 파일이 구조·클래스명 기준으로 같다(내용 문구는 원고를 그대로 옮겼는지만 대조)
css_fundamentals01편에서 어떤 파일 경로에link태그를 추가할지 말할 수 있다
직접 해보기
design-spec.md의 “4. 레이아웃 스펙 — 프로젝트 카드” 절을 읽고, 지금 마크업의 .project-card 안에서 시안이 요구하는 순서(썸네일 → 제목 → 설명 → 태그 → 버튼)와 실제 순서가 같은지 확인해 보자.
정답 보기
지금 마크업은 썸네일(img) → 제목(h2) → 설명(p) → 코드 예시(p/pre, 카드에 따라 있음) → 인용구(blockquote) → 링크(a) 순서다. 시안이 요구하는 태그 리스트(칩)는 아직 없고, “자세히” 버튼도 modern_html에서 dialog로 구현되므로 지금은 대신 외부 링크로 대체했다. 두 가지 모두 뒤 과목에서 채워질 자리라는 점을 확인한다.
자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
css_fundamentals를 시작하니 class가 시안과 안 맞는다 | 이 과목에서 임의로 class명을 다르게 지음 | 이 편의 정본 코드에 나온 class명으로 지금 맞춰 둔다 |
| link 태그를 이 과목에서 미리 추가했다 | CSS 파일이 없는데 head에 link만 넣어 404가 남 | css_fundamentals 01편에서 tokens.css를 만든 다음에 추가한다 |
| 세 파일의 헤더·푸터가 서로 다르다 | 편마다 한 파일씩만 고치며 다른 파일에 반영을 빠뜨림 | 지금 정본 코드와 세 파일을 나란히 비교해 헤더·푸터를 통일한다 |