
먼저 웹 페이지를 만들 때 가장 먼저 정리해야 하는 것은 역할 분담이다.
- HTML: 문서의 구조(Structure) 와 의미(semantic)를 정의
- CSS: 화면의 표현(Presentation) 과 레이아웃을 정의
- JavaScript: 사용자 입력/상태 변화에 대한 동작(Behavior) 을 정의
1. HTML 문법의 본질: “요소(Element) + 속성(Attribute) + 중첩(Nesting)”
HTML은 태그로 만들어진 요소(element) 들의 트리 구조이다. 브라우저는 이를 파싱해 DOM (Document Object Model, 문서 객체 모델) 을 만들고 렌더링한다.

1-1. 기본 형태
<tagname attr="value">content</tagname>
- tagname: 태그 이름 (예: div, p, form)
- attr="value": 속성(추가 정보)
- content: 텍스트 또는 다른 요소(중첩 가능)
1-2. “닫는 태그가 없는 요소”도 있다
대표적으로 meta, link, img, input, br 같은 태그는 void element(종료 태그 없음)로 자주 사용한다.
2. 로그인 화면 예제 코드
아래 코드는 다음 태그를 모두 포함해서 작성했다.
html, head, body, title, meta, div, a, script, link, img, span, p, li, ul, style, br, h1, h2, input, form, h3, nav, header, footer, iframe, strong, button, i
참고로 <!DOCTYPE html>은 HTML 문서의 가장 첫 줄에 위치하고 해당 문서가 HTML5 표준을 따르는 문서임을 브라우저에게 알려주는 문서 타입 선언(Document Type Declaration)이다. 브라우저가 문서를 올바르게 렌더링(표시)하도록 하는 필수적인 지시문이라고 할 수 있다. 이는 태그(tag)가 아니라 선언문(declaration)이며, 이 선언이 없으면 브라우저가 호환되지 않는 방식으로 문서를 해석하는 'quirks mode'로 작동할 수 있다.
자세한 설명은 코드 아래에 정리해놓았다.
그대로 저장해서 login.html로 열면 동작합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="로그인 화면 예제로 배우는 HTML/CSS 핵심 문법">
<title>로그인 | Example</title>
<!-- link: 외부 리소스 연결 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap">
<!-- style: CSS는 화면 표현/레이아웃 규칙 -->
<style>
/* ========== 1) 디자인 토큰(변수) ==========
:root에 선언한 사용자 정의 속성은 캐스케이드에 참여하며(상속/우선순위 영향),
var()로 어디서든 재사용할 수 있다. */
:root {
--bg: #0f1115;
--panel: rgba(255,255,255,0.04);
--border: rgba(255,255,255,0.10);
--text: #e7eaf0;
--muted: #b9c2d6;
--primary: #2f6bff;
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}
/* ========== 2) 전역 기본 스타일 ==========
CSS 박스 모델: content/padding/border/margin이 합쳐져 실제 박스 크기가 결정된다. */
body {
margin: 0;
background: var(--bg);
color: var(--text);
}
.wrap {
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* header/nav/footer: 의미 있는(semantic) 레이아웃 */
header, footer {
background: rgba(255,255,255,0.03);
border-bottom: 1px solid rgba(255,255,255,0.06);
}
footer {
border-bottom: none;
border-top: 1px solid rgba(255,255,255,0.06);
margin-top: auto;
}
.container {
width: min(960px, 92vw);
margin: 0 auto;
padding: 18px 20px;
}
/* ========== 3) 상단 영역 ==========
flex 정렬: align-items/justify-content로 주축/교차축 정렬을 제어한다. */
.brand {
display: flex;
align-items: center;
gap: 10px;
}
.brand img {
width: 36px;
height: 36px;
border-radius: 10px;
}
nav a {
color: var(--muted);
text-decoration: none;
margin-right: 12px;
}
nav a:hover { text-decoration: underline; }
/* ========== 4) 메인 레이아웃 ==========
grid는 2차원(행/열) 레이아웃에 강하다. */
.grid {
display: grid;
grid-template-columns: 1.15fr 0.85fr;
gap: 16px;
padding: 18px 0 24px;
}
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 14px;
padding: 18px;
}
.muted { color: var(--muted); }
.title { margin: 10px 0 4px; }
/* ========== 5) 폼 스타일 ==========
input은 폼 컨트롤, button은 상호작용 요소다. */
.field { margin-top: 12px; }
.field span {
display: inline-block;
margin-bottom: 6px;
font-size: 14px;
color: #cfd6e6;
}
.inputRow {
display: flex;
align-items: center;
gap: 10px; /* gap은 flex/grid 모두에서 간격 정의 가능 */
}
input {
flex: 1;
padding: 12px 12px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,0.12);
background: rgba(15,17,21,0.65);
color: var(--text);
outline: none;
}
input:focus {
border-color: rgba(113,168,255,0.7);
box-shadow: 0 0 0 3px rgba(113,168,255,0.15);
}
button {
padding: 12px 14px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,0.12);
background: var(--primary);
color: white;
cursor: pointer;
font-weight: 700;
white-space: nowrap;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.ghost { background: rgba(255,255,255,0.06); }
.error { margin-top: 10px; color: #ffb4b4; }
.helpList { margin: 10px 0 0; padding-left: 18px; color: #cfd6e6; }
.helpList li { margin: 6px 0; }
iframe {
width: 100%;
height: 140px;
border: 0;
border-radius: 12px;
background: rgba(0,0,0,0.2);
}
.tiny { font-size: 12px; color: var(--muted); }
.icon { opacity: 0.9; }
/* ========== 6) 반응형 ==========
화면 폭에 따라 그리드를 1열로 변경 */
@media (max-width: 820px) {
.grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="container">
<div class="brand">
<img src="https://dummyimage.com/72x72/2f6bff/ffffff.png&text=E" alt="Example 로고">
<div>
<h1 class="title">Example 서비스</h1>
<p class="muted">로그인 화면 예제로 HTML/CSS 문법을 익혀봅니다.</p>
</div>
</div>
<nav aria-label="상단 메뉴">
<a href="#login">로그인</a>
<a href="#guide">가이드</a>
<a href="#policy">정책</a>
</nav>
</div>
</header>
<div class="container">
<div class="grid">
<!-- 가이드 카드 -->
<div class="card" id="guide">
<h2>이 예제로 배우는 포인트</h2>
<p>
HTML은 <strong>구조</strong>, CSS는 <strong>표현</strong>, JavaScript는 <strong>동작</strong>을 담당합니다.
<br>
아래는 “현업에서 가장 자주 만나는 태그”들을 한 화면에 넣어 보는 연습입니다.
</p>
<ul class="helpList">
<li><strong>header/nav/footer</strong>로 의미 있는 레이아웃 만들기</li>
<li><strong>form/input/button</strong>로 로그인 폼 구성</li>
<li><strong>grid/flex</strong>로 레이아웃/정렬 처리</li>
<li><strong>iframe</strong>로 약관/안내 삽입(샌드박스 적용)</li>
</ul>
<h3>팁</h3>
<p class="tiny">
폼 검증은 프론트에서도 하지만, 보안상 최종 검증은 반드시 서버에서 해야 합니다.
</p>
</div>
<!-- 로그인 카드 -->
<div class="card" id="login">
<h2>로그인</h2>
<p class="muted">
계정이 없다면 <a href="#policy">정책</a>을 확인한 뒤 가입하세요.
</p>
<form id="loginForm" action="/login" method="post" novalidate>
<div class="field">
<span>이메일</span>
<div class="inputRow">
<i class="icon" aria-hidden="true">@</i>
<input id="email" name="email" type="email"
placeholder="name@example.com"
autocomplete="username" required>
</div>
</div>
<div class="field">
<span>비밀번호</span>
<div class="inputRow">
<input id="password" name="password" type="password"
placeholder="••••••••"
autocomplete="current-password"
minlength="8" required>
<button type="button" class="ghost" id="togglePw">보기</button>
</div>
<p class="tiny">
비밀번호는 8자 이상 권장입니다. <a href="/reset">재설정</a>
</p>
</div>
<div id="errorBox" class="error" role="alert" aria-live="polite" style="display:none;">
<p id="errorMsg"></p>
</div>
<div class="field">
<button type="submit" id="submitBtn">로그인</button>
</div>
</form>
<h3 id="policy">약관 요약</h3>
<iframe
title="약관 요약"
sandbox
srcdoc="
<div style='font-family:Inter,system-ui; padding:12px; color:#111;'>
<h3 style='margin:0 0 6px;'>Terms (Demo)</h3>
<p style='margin:0 0 8px;'>로그인 시 약관 및 개인정보 처리방침에 동의한 것으로 간주됩니다.</p>
<p style='margin:0;'>공용 PC에서는 자동완성/저장을 주의하세요.</p>
</div>
">
</iframe>
</div>
</div>
</div>
<footer>
<div class="container">
<p class="muted">
© <strong>Example</strong> · <a href="/privacy">Privacy</a> · <a href="/terms">Terms</a>
</p>
</div>
</footer>
<script>
(function () {
const form = document.getElementById("loginForm");
const email = document.getElementById("email");
const password = document.getElementById("password");
const togglePw = document.getElementById("togglePw");
const errorBox = document.getElementById("errorBox");
const errorMsg = document.getElementById("errorMsg");
const submitBtn = document.getElementById("submitBtn");
function showError(msg) {
errorMsg.textContent = msg;
errorBox.style.display = "block";
}
function clearError() {
errorMsg.textContent = "";
errorBox.style.display = "none";
}
togglePw.addEventListener("click", () => {
const isPw = password.type === "password";
password.type = isPw ? "text" : "password";
togglePw.textContent = isPw ? "숨기기" : "보기";
password.focus();
});
form.addEventListener("submit", (e) => {
clearError();
const emailValue = (email.value || "").trim();
const pwValue = password.value || "";
if (!emailValue) {
e.preventDefault();
showError("이메일을 입력하세요.");
email.focus();
return;
}
if (!emailValue.includes("@")) {
e.preventDefault();
showError("이메일 형식이 올바르지 않습니다.");
email.focus();
return;
}
if (pwValue.length < 8) {
e.preventDefault();
showError("비밀번호는 8자 이상이어야 합니다.");
password.focus();
return;
}
submitBtn.disabled = true;
submitBtn.textContent = "로그인 중...";
});
})();
</script>
</div>
</body>
</html>
3. HTML 문법
3-1. <html> / <head> / <body>: 문서의 3층 구조
- <html>: 문서의 최상위 루트(모든 요소는 그 자손이어야 함)
- <head>: 화면에 직접 보이진 않지만 메타데이터/리소스 연결이 들어간다.
- <body>: 사용자가 실제로 보는 콘텐츠가 들어간다.
3-2. <meta>: 문서/디바이스 환경을 브라우저에 알려준다
- charset="utf-8": 한글 포함 유니코드 문자 인코딩
- name="viewport": 모바일/반응형에서 “화면 폭” 기준을 맞추는 필수 설정. width=device-width, initial-scale=1 같은 선언이 일반적이다.
3-3. <title>: 탭 제목/검색 결과 제목
탭에 보이는 제목이며 SEO (검색엔진 최적화, Search Engine Optimization) 에도 영향을 준다.
3-4. <link>: 외부 리소스 연결(성능에도 영향)
- rel="stylesheet": 외부 CSS를 불러온다. 기본적으로 <head>의 stylesheet 링크는 파싱 중 발견되면 렌더링에 영향을 줄 수 있다(렌더링 블로킹과 관련).
- rel="preconnect": 곧 필요할 것으로 예상되는 오리진에 미리 연결을 준비해 로딩을 빠르게 할 수 있다.
- <link>는 “현재 문서와 외부 리소스 관계”를 명시한다.
4. HTML의 의미 있는 레이아웃(semantic)
4-1. <header> / <nav> / <footer>의 장점
div만으로도 레이아웃은 가능하지만, header/nav/footer처럼 의미가 있는 태그를 쓰면:
- 문서 구조가 읽기 쉬워지고
- 스크린리더/검색엔진이 영역을 더 잘 이해한다.
4-2. 제목 태그 <h1>~<h3>
제목 태그는 섹션 제목을 나타내고 <h1>이 가장 높은 레벨이다.
로그인 화면에서는 보통:
- <h1>: 서비스/페이지의 최상위 의미
- <h2>: 카드 제목(로그인, 가이드)
- <h3>: 하위 섹션(팁, 약관 요약)
4-3. 텍스트 태그: <p>, <span>, <strong>, <i>, <br>
- <p>: 문단
- <span>: 인라인 라벨/짧은 텍스트 묶음(예제에서 입력 필드 라벨 역할)
- <strong>: “중요도 강조”(단순 굵게가 아니라 의미적으로 강조)
- <i>: 관용적으로 아이콘/기울임 텍스트(예제의 @ 아이콘)
- <br>: 줄바꿈(남발하면 문단 구조가 깨지니 문단은 p가 우선)
4-4. 리스트: <ul>, <li>
<ul>(Unordered List)는 항목들의 모음”을 나타내는데, 그 항목들의 순서가 중요하지 않을 때 사용한다. 즉, 항목을 위아래로 바꿔도 문서의 의미가 크게 변하지 않는 목록이다.
예를 들면:
- 기능/특징 목록(Features)
- 체크리스트(순서가 “절차”가 아닌 경우)
- 메뉴 링크 모음(“어떤 링크가 있는지”가 중요하고 순서가 절대적이지 않을 때)
반대로 순서 자체가 의미(단계/절차/랭킹/우선순위)라면 <ol>을 쓴다. MDN은 <ol>과 <ul> 모두 “항목 리스트”를 표현하지만, <ol>은 순서가 의미 있는 경우라고 정리한다.
<li>(List Item)는 리스트의 “항목 하나”를 의미한다. <li>는 반드시 부모가 <ul> 또는 <ol>(또는 <menu>) 이어야 한다. 즉 <li>를 단독으로 쓰면 문법적으로 잘못된 마크업이다. 또 <ul>의 “리스트 항목”은 <ul>의 자식인 <li>들로 구성된다. (즉, <ul>은 직접 자식으로 <li>만 두는 것이 표준적인 구조이다.)
4-5. 이미지: <img>와 alt
<img>는 문서에 이미지 리소스를 삽입(embed) 하는 요소이다. 기본적으로 다음 형태를 갖는다.
<img src="/assets/logo.png" alt="Example 서비스 로고">
- src: 이미지 URL
- alt: 이미지를 볼 수 없을 때 제공되는 대체 텍스트(text alternative)
브라우저는 이미지가 아직 로드되지 않았거나 로드 실패/이미지 로딩 비활성화 상태여도 alt를 통해 사용자에게 의미를 전달할 수 있다.
5. 폼(<form>)은 “전송 단위”, 입력(<input>)은 “컨트롤”
5-1. <form>의 역할과 method 선택
<form>은 입력 컨트롤을 묶어 서버로 전송하는 단위다. DOM에서는 HTMLFormElement로 다뤄진다.
로그인처럼 민감정보(비밀번호)를 보내는 경우 GET은 피해야 한다. URL에 노출될 위험이 있어 보안상 부적절하기 때문이다. 그래서 보통 POST를 사용한다.
HTML <form>을 한 문장으로 정의하면 이렇게 볼 수 있다.
- <form> = “브라우저가 어떤 HTTP 요청을 보낼지”를 정의하는 전송 단위
<form>의 속성들은 사용자가 제출(submit)할 때 어디(action)로, 어떤 방식(method)으로 데이터를 보낼지 설정한다. - <input> = “사용자가 값을 넣는 입력 컨트롤(위젯)”
<input>은 웹 폼에서 데이터를 받기 위한 interactive control이며 type/속성 조합에 따라 동작이 달라진다.
예시 1) 로그인 폼: <form>이 “전송”, <input>이 “컨트롤”
<form action="/login" method="post">
<input
type="email"
name="email"
autocomplete="username"
required
placeholder="name@example.com"
>
<input
type="password"
name="password"
autocomplete="current-password"
minlength="8"
required
placeholder="비밀번호"
>
<button type="submit">로그인</button>
</form>
5-2. <input>: 가장 강력하고 복잡한 태그 중 하나
input은 사용자에게서 데이터를 받는 인터랙티브 컨트롤이고 type과 다양한 속성 조합에 따라 동작이 달라진다.
예제에서 핵심 속성은 다음과 같다.
- type="email": 이메일 키보드/검증 UX
- type="password": 비밀번호 마스킹
- name="email": 서버 전송 시 키가 됨(스펙에서도 name은 “요소의 이름”을 의미)
- required: 값이 없으면 폼이 제출되지 않도록 하는 불리언 속성 (MDN 웹 문서)
- minlength="8": 최소 글자 수 제약 (MDN 웹 문서)
- autocomplete="username", autocomplete="current-password": 브라우저 자동완성 힌트
이 속성은 “사용자가 입력할 값이 무엇을 의미하는지”를 설명하는 방식으로 정의된다. - action="/login": 폼 데이터를 보낼 목적지 URL
- method="post": 데이터를 요청 본문(body) 으로 보냄(POST)
즉, <form>은 “제출 버튼을 눌렀을 때 어떤 HTTP 요청이 나갈지”를 통째로 결정한다.
<input>은 간단하게 말해서 사용자가 값을 조작하는 UI 위젯이라고 할 수 있다.
5-3. 서버로 실제 전송되는 키는 무엇으로 정해지나? → name
폼 제출 시 서버로 가는 데이터는 보통 name=value 쌍이다.
여기서 키(key) 가 되는 것이 name이고, 표준은 name이 “폼 제출에서 사용되는 이름”이라고 명시한다.
예를 들어 사용자가 이렇게 입력했다면:
- email = a@b.com
- password = 12345678
서버는 대략 이런 형태(키/값)를 받는다:
- email=a%40b.com
- password=12345678
중요한 포인트: name이 없으면 제출 데이터에서 빠질 수 있다. (성공적인 컨트롤만 포함되는 규칙과 연결)
예시 2) 검색 폼: 같은 <form>이지만 method가 바뀌면 전송 방식이 바뀐다
<form action="/search" method="get">
<input type="search" name="q" placeholder="검색어">
<button type="submit">검색</button>
</form>
- method="get"이면 폼 데이터가 URL 뒤에 ?로 붙어서 전송된다.
예: 사용자가 koreaaimap을 입력하면 브라우저는 대략 이런 URL로 이동한다.
/search?q=koreaaimap
그래서 검색처럼 “조회” 성격(부수효과 없음) 인 경우 GET이 자주 쓰이고, 비밀번호처럼 민감한 데이터는 URL에 노출될 수 있어 GET을 피하는 것이 원칙이다(POST 권장).
5-4. 제출에 포함되는 입력
disabled가 붙은 컨트롤은 수정/포커스가 불가할 뿐 아니라 폼 제출에서도 제외된다.
<input name="email" value="a@b.com" disabled>
이 경우 email은 폼 데이터에 안 들어갈 수 있다. (MDN FormData도 “name이 있고 disabled가 아닌 성공적인 컨트롤만 포함”된다)
폼 제출 이벤트는 form에서 발생하고, 어떤 버튼이 제출을 트리거했는지 SubmitEvent.submitter로 알 수 있다.
5-5. <button>: type을 명시하라
button은 기본 type이 환경에 따라 submit처럼 동작할 수 있어, 의도에 맞게 type="button" / type="submit"을 명시하는 습관이 좋다.
6. <iframe>: “문서 안의 문서” + 보안(샌드박스) 기본
iframe은 외부/내부 문서를 페이지 안에 삽입한다. 다만 보안상 권장되는 패턴은 sandbox 적용이다. sandbox가 걸리면 상위 페이지 이동 같은 동작이 제한된다.
(예제는 srcdoc로 내부 문자열을 넣고, sandbox를 추가했다.)
7. CSS 해설: 문법을 “규칙(rule) 단위”로 이해하기
CSS는 기본적으로 “선택자(selector) + 선언 블록(declarations)”으로 구성된다.
selector {
property: value;
}
7-1. 선택자(Selector)와 우선순위(Specificity)
여러 규칙이 한 요소에 동시에 적용되면, 브라우저는 캐스케이드(cascade) + 구체성(specificity) 알고리즘으로 “어떤 규칙을 적용할지” 결정한다. 구체성은 선택자 가중치를 계산해 경쟁 규칙 중 어떤 선언이 적용되는지 결정한다.
예제에서 등장하는 선택자들:
- 요소 선택자: body, input, button
- 클래스 선택자: .card, .muted, .grid
- 아이디 선택자: #loginForm(JS에서 주로 사용)
- 의사 클래스(pseudo-class): :hover, :focus, :disabled
7-2. 박스 모델(Box Model): 레이아웃 오류의 1순위 원인
모든 요소는 박스로 렌더링되고, content → padding → border → margin이 합쳐져 실제 공간을 차지한다.
예제에서 body { margin:0; }를 둔 이유는 브라우저 기본 여백을 제거해 디자인 기준점을 맞추기 위함이다.
7-3. Flex vs Grid: 언제 무엇을 쓰나?
- Flex: 한 방향(가로/세로) 정렬에 강함. align-items, justify-content로 축 정렬을 제어한다.
예제의 .brand, .inputRow가 Flex다. - Grid: 행/열을 함께 다루는 2차원 레이아웃에 강함. grid-template-columns로 열 구조를 정의한다.
예제의 .grid가 Grid다.
또한 gap은 flex/grid 모두에서 “아이템 간 간격”을 표준적으로 제공한다.
<header class="toolbar">
<div class="brand">Example</div>
<nav class="menu">
<a href="#login">로그인</a>
<a href="#help">도움말</a>
<a href="#policy">정책</a>
</nav>
<div class="actions">
<button type="button">회원가입</button>
<button type="button">로그인</button>
</div>
</header>
<section class="cardGrid">
<article class="card">카드 1</article>
<article class="card">카드 2</article>
<article class="card">카드 3</article>
<article class="card">카드 4</article>
<article class="card">카드 5</article>
<article class="card">카드 6</article>
</section>
/* FLEX: 상단 툴바는 “가로 한 줄 정렬(1차원)” */
.toolbar {
display: flex;
align-items: center; /* 세로축 정렬 */
justify-content: space-between;
gap: 16px;
padding: 12px 16px;
border-bottom: 1px solid #ddd;
}
.menu {
display: flex; /* 메뉴도 가로로(1차원) */
gap: 12px;
}
.actions {
display: flex; /* 버튼들을 한 줄로(1차원) */
gap: 8px;
}
/* GRID: 카드 영역은 “행+열(2차원)”을 동시에 설계 */
.cardGrid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3열 */
gap: 12px;
padding: 16px;
}
.card {
border: 1px solid #ddd;
border-radius: 12px;
padding: 16px;
}
7-4. CSS 변수(사용자 정의 속성): “디자인 토큰”을 코드로 만들기
:root에 --primary, --bg처럼 변수를 선언하고 var(--primary)로 재사용하면,
- 색/간격/폰트 스케일을 일관되게 유지할 수 있고
- 유지보수가 쉬워진다.
사용자 정의 속성은 캐스케이드에 참여하며, var()로 값을 가져온다.
<body data-theme="dark">
<div class="panel">
<h1 class="title">로그인</h1>
<p class="muted">CSS 변수를 사용해 디자인 토큰을 관리합니다.</p>
<button class="btnPrimary" type="button">Primary</button>
<button class="btnGhost" type="button">Ghost</button>
</div>
</body>
/* 1) 전역 토큰(디자인 시스템의 기준값) */
:root {
--bg: #ffffff;
--text: #111111;
--muted: #666666;
--primary: #2f6bff;
--border: #dddddd;
--radius: 14px;
--space: 16px;
}
/* 2) 테마 오버라이드: data-theme="dark"일 때 토큰만 바꿔도 전체 분위기 변경 */
body[data-theme="dark"] {
--bg: #0f1115;
--text: #e7eaf0;
--muted: #b9c2d6;
--border: rgba(255,255,255,0.12);
}
/* 3) 토큰 사용 */
body {
background: var(--bg);
color: var(--text);
margin: 0;
}
.panel {
margin: 24px auto;
width: min(520px, 92vw);
padding: var(--space);
border: 1px solid var(--border);
border-radius: var(--radius);
}
.muted {
color: var(--muted);
}
.btnPrimary {
background: var(--primary);
color: white;
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px 14px;
}
/* 4) var() fallback 예시:
--ghost-bg가 정의되지 않았으면 fallback(대체값)을 사용 */
.btnGhost {
background: var(--ghost-bg, rgba(255,255,255,0.06));
color: var(--text);
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px 14px;
}
7-5. 반응형(@media): 화면 조건에 따라 스타일을 바꾼다
미디어 쿼리는 장치 특성(뷰포트 폭 등)에 따라 스타일을 적용한다.
예제는 max-width: 820px에서 2열 그리드를 1열로 바꿔 모바일에서 읽기 쉽게 만들었다.
<section class="layout">
<aside class="sidebar">사이드바</aside>
<main class="content">
<h1>메인 콘텐츠</h1>
<p>화면이 좁아지면 레이아웃을 1열로 바꾸고, 여백/글자 크기도 조정합니다.</p>
</main>
</section>
.layout {
display: grid;
grid-template-columns: 280px 1fr; /* 데스크톱: 2열 */
gap: 16px;
padding: 16px;
}
.sidebar, .content {
border: 1px solid #ddd;
border-radius: 12px;
padding: 16px;
}
/* 1) 전통적인 max-width 문법 */
@media (max-width: 820px) {
.layout {
grid-template-columns: 1fr; /* 모바일: 1열 */
}
.sidebar {
order: 2; /* 필요하면 순서 변경도 가능 */
}
.content h1 {
font-size: 20px;
}
}
/* 2) 같은 의미의 range syntax 예시(지원 브라우저 환경이라면 더 간결) */
@media (width <= 520px) {
.layout {
padding: 10px;
gap: 10px;
}
.sidebar, .content {
padding: 12px;
}
}
8. 태그 28개 “한 줄 역할 요약” (예제에서 어디에 쓰였나)
- 문서 뼈대: html/head/body/title/meta
- 외부 리소스/동작: link/style/script
- 레이아웃/구역: header/nav/footer/div
- 제목/텍스트: h1/h2/h3/p/span/strong/i/br
- 링크/목록: a/ul/li
- 폼: form/input/button
- 미디어/임베드: img/iframe
HTML 요소 전체 목록을 기능별로 확인하고 싶다면 MDN의 요소 레퍼런스를 확인하면 된다.
https://developer.mozilla.org/ko/docs/Web/HTML/Reference/Elements
HTML 요소 참고서 - HTML: Hypertext Markup Language | MDN
요소 설명 HTML 문서의 루트(최상위 요소)를 나타내므로 root 요소라고도 합니다. 다른 모든 요소는 이 요소의 자손이어야 합니다. 메타데이터는 스타일, 스크립트, 각종 소프트웨어(검색 엔진, 브
developer.mozilla.org
더 자세하게 알아보고 싶다면 아래 사이트를 참고하면 좋다.
https://developer.mozilla.org/ko/docs/Web/HTML
HTML: Hypertext Markup Language | MDN
HTML 소개 웹 개발이 처음이시라면 HTML 기본 문서에서 HTML이란 무엇인지, 그리고 HTML의 사용법을 배워보세요. HTML 자습서 HTML 학습지를 방문하시면 HTML 사용법과 자습서, 완전한 예제를 찾아보실
developer.mozilla.org
'웹 (Web) > 프런트엔드' 카테고리의 다른 글
| Vanilla Javascript 기초 총정리 (0) | 2026.01.19 |
|---|---|
| React를 위한 JavaScript 기초 훑어보기 (2/2) (1) | 2026.01.09 |
| React를 위한 JavaScript 기초 훑어보기 (1/2) (0) | 2026.01.06 |
| 주요 html 엔티티 정리 (1) | 2026.01.06 |
| 2025 프론트엔드 개발자 로드맵 (roadmap.sh Frontend Developer) (0) | 2025.09.01 |