도라의 개발 블로그

소프트웨어 개발/웹

[개발 기초 정리] HTML·CSS·JavaScript

boradora 2026. 7. 23. 09:45

작업일2026. 07

HTML CSS JavaScript 박스 모델 Flexbox / Grid DOM 이벤트

웹페이지는 HTML, CSS, JavaScript가 함께 구성합니다. HTML은 문서 구조를 만들고, CSS는 화면 표현을 정의하며, JavaScript는 사용자 입력과 상태 변화에 대응합니다. 프론트엔드의 기본 구성을 정리했습니다.

한 줄로 먼저: HTML은 문서 구조, CSS는 화면 표현, JavaScript는 동작과 상태 처리를 담당합니다.

웹 개발을 시작하면 이 셋을 거의 동시에 배우게 됩니다. 그래서 각 기술의 역할 범위가 헷갈리기 쉽습니다. 역할을 구분해두면 React 같은 프레임워크를 배울 때도 도움이 됩니다.

HTML: 문서의 구조

HTML(HyperText Markup Language)은 태그로 문서의 구조를 정의하는 마크업 언어입니다. 프로그래밍 언어가 아니라 "이건 제목이고 저건 문단이다"라고 표시(markup)해주는 언어입니다. 브라우저는 이 표시를 읽어 화면을 그립니다.

요소와 속성

HTML의 기본 단위는 요소(Element)입니다. 시작 태그, 내용, 끝 태그로 이루어집니다.

<h1>가장 큰 제목</h1>
<p>평범한 문단입니다.</p>
<br>                    <!-- 내용이 없는 요소는 끝 태그도 없습니다 -->

요소 안에 요소를 중첩할 수 있고, 속성(Attribute)으로 추가 정보를 줍니다. 속성은 항상 시작 태그 안에 들어갑니다.

<a href="https://example.com">링크</a>
<img src="cat.jpg" alt="고양이 사진">

alt는 이미지를 표시할 수 없을 때 대체로 제공할 텍스트입니다. 스크린 리더도 이 값을 읽으므로 접근성과 SEO에 영향을 줍니다. 의미 있는 이미지는 작성하는 것이 좋습니다.

id와 class

속성 의미 개수 주 용도
id 고유 식별자 문서에 하나만 특정 요소를 콕 집을 때
class 공유 가능한 이름 여러 개 가능 같은 스타일을 묶을 때

문서의 기본 뼈대

<!DOCTYPE html>              <!-- HTML5 문서라는 선언 -->
<html lang="ko">
  <head>                     <!-- 화면에 안 보이는 문서 정보 -->
    <meta charset="UTF-8">
    <title>페이지 제목</title>   <!-- 브라우저 탭에 뜨는 이름 -->
  </head>
  <body>                     <!-- 실제로 화면에 보이는 내용 -->
    <h1>안녕하세요</h1>
  </body>
</html>

역할별 태그

분류 대표 태그 쓰임
텍스트 h1~h6, p, span, strong, a 제목·문단·강조·링크
목록 ul, ol, li 순서 없는/있는 목록
table, thead, tbody, tr, th, td 행과 열 데이터
form, input, label, button 사용자 입력받기
미디어 img, video, audio 이미지·영상·소리
시맨틱 header, nav, main, section, footer 의미를 가진 구획
시맨틱 태그를 쓰는 이유: <div>만으로도 화면은 구성할 수 있습니다. 다만 <header>, <nav>를 사용하면 검색엔진과 스크린 리더가 콘텐츠의 역할을 더 정확히 해석할 수 있습니다. 화면은 같아도 문서의 의미 정보가 달라집니다.

Block과 Inline

요소마다 기본 배치 방식이 있습니다. 이를 알아야 widthheight의 적용 결과를 예측할 수 있습니다.

구분 Block 요소 Inline 요소
배치 혼자 한 줄 차지 (세로로 쌓임) 다른 요소와 한 줄에 나란히
크기 가로 폭을 최대로 내용 크기만큼만
width / height 적용됨 무시됨
div, p, h1, ul span, a, strong, img
주의: <span> 같은 inline 요소에는 width: 200px이 적용되지 않습니다. 크기를 지정하려면 display: block 또는 inline-block으로 변경해야 합니다.

CSS: 보이는 모습

CSS(Cascading Style Sheets)는 HTML 요소를 화면에 어떻게 보여줄지 정의하는 스타일시트 언어입니다. 색, 글꼴, 간격, 위치, 레이아웃이 전부 CSS의 영역입니다.

문법과 적용 방법

p {                    /* 선택자: 누구에게 */
  color: red;          /* 속성: 값 */
  text-align: center;
}

적용 방법은 세 가지입니다. 일반적으로는 스타일과 구조를 분리하기 위해 외부 파일(External) 방식을 사용합니다.

<link rel="stylesheet" href="style.css">   <!-- 권장 -->
<style> p { color: red; } </style>         <!-- 문서 안에 -->
<p style="color:red;">…</p>                <!-- 태그에 직접, 지양 -->

선택자: 누구에게 스타일을 줄까

선택자 예시 의미
요소 p { } 모든 p 태그
id #header { } id가 header인 요소
class .menu { } class가 menu인 요소
자식 div > p { } div의 바로 아래 p
후손 div p { } div 안의 모든 p
의사 클래스 a:hover { } 마우스를 올린 상태

우선순위: 스타일이 안 먹을 때

여러 규칙이 같은 요소를 가리키면 충돌합니다. 이때 명시도(Specificity)가 높은 쪽이 이깁니다.

인라인 스타일  >  id  >  class · 속성 · 의사클래스  >  요소
  • 명시도가 높은 규칙이 우선합니다. class 선택자 하나는 요소 선택자 여러 개보다 우선합니다.
  • 명시도가 같으면 나중에 작성한 규칙이 적용됩니다. 이것이 "Cascading"의 의미입니다.
!important는 예외적으로만 사용합니다. 우선순위 문제를 임시로 덮을 수 있어 유지보수가 어려워집니다. 먼저 선택자와 적용 순서를 확인하는 편이 좋습니다.

박스 모델

CSS 레이아웃의 기본 개념입니다. 모든 HTML 요소는 네 영역으로 구성됩니다.

margin  바깥 간격
border  테두리
padding  안쪽 여백
content
실제 내용이 들어가는 자리
width 는 맨 안쪽 content 의 너비만 가리킵니다
계산 시 주의: 기본값에서 widthcontent 영역만의 너비입니다. width: 300px; padding: 25px;이면 실제 차지하는 너비는 300 + 25×2 = 350px가 됩니다.

보통은 box-sizing: border-box;를 사용합니다. padding과 border를 width에 포함하므로 지정한 값으로 전체 너비를 관리할 수 있습니다.

position: 위치 잡기

동작 자주 쓰는 곳
static 기본값. 문서 흐름 그대로 거의 지정하지 않음
relative 원래 위치 기준으로 살짝 이동 (자리는 유지) 미세 조정, absolute의 기준점
absolute 가장 가까운 위치 지정 조상 기준 (문서 흐름에서 빠짐) 배지, 툴팁, 닫기 버튼
fixed 화면 기준 고정 (스크롤해도 안 움직임) 플로팅 버튼
sticky 스크롤하다 특정 지점에서 고정 상단 헤더, 목차

Flexbox와 Grid

과거에는 float로 레이아웃을 구성했지만, 현재는 Flexbox와 Grid를 주로 사용합니다.

구분 Flexbox Grid
차원 1차원 (가로 또는 세로) 2차원 (행 열)
잘하는 것 한 줄 안에서 정렬·간격 배분 페이지 전체를 격자로 나누기
쓰는 곳 내비게이션 바, 카드 안 요소 정렬 전체 레이아웃, 갤러리, 대시보드
/* Flexbox: 가운데 정렬 */
.container {
  display: flex;
  justify-content: center;   /* 메인 축(가로) 정렬 */
  align-items: center;       /* 교차 축(세로) 정렬 */
}

/* Grid: 2열 레이아웃 */
.layout {
  display: grid;
  grid-template-columns: 1fr 2fr;   /* 1:2 비율로 두 열 */
  gap: 20px;
}

둘은 함께 사용할 수 있습니다. 예를 들어 페이지 레이아웃에는 Grid, 내부 요소 정렬에는 Flexbox를 사용할 수 있습니다.

JavaScript: 페이지의 동작

JavaScript는 웹페이지의 동작과 로직을 구현하는 프로그래밍 언어입니다. HTML 콘텐츠와 CSS 스타일을 변경하고, 사용자 입력에 반응할 수 있습니다.

먼저 세 가지 성격을 알아두면 쉽게 이해할 수 있습니다. 

  • 인터프리터: 컴파일 없이 브라우저가 한 줄씩 읽으며 바로 실행합니다.
  • 동적 타입: 변수를 만들 때 타입을 정하지 않습니다. 값이 들어가는 순간 결정됩니다.
  • 싱글 스레드: 한 번에 하나씩만 처리합니다. 그래서 비동기가 중요해집니다.

변수: var, let, const

키워드 스코프 재선언 재할당 지금 쓰나?
var 함수 가능 가능 ❌ 쓰지 않습니다
let 블록 { } 불가 가능 ⭕ 값이 바뀔 때
const 블록 { } 불가 불가 기본값
권장 방식: 기본적으로 const를 사용하고, 재할당이 필요할 때만 let을 사용합니다. 새 코드에서는 var를 피하는 편이 좋습니다.

호이스팅(Hoisting)은 JS가 변수 선언을 스코프 맨 위로 끌어올리는 동작입니다. var는 값이 없는 채로 올라가서 선언 전에 접근해도 에러 대신 undefined가 나옵니다. 이게 조용한 버그의 원인이 되며, let·const가 권장되는 이유 중 하나입니다.

데이터 타입

구분 원시형 (Primitive) 객체형 (Object)
종류 number, string, boolean, null, undefined object, array, function
저장 방식 값 자체를 저장 값이 있는 주소를 저장
변경 불변 가변

객체를 복사한 뒤 원본 변경이 함께 반영되는 문제가 생길 수 있습니다. 객체 변수는 값 자체가 아니라 참조를 복사하기 때문입니다.

DOM: HTML을 객체로 다루기

DOM(Document Object Model)은 브라우저가 HTML 문서를 트리 구조의 객체로 표현한 것입니다. JavaScript는 DOM을 통해 페이지 콘텐츠와 상태를 변경합니다. 즉 JavaScript는 HTML 소스 자체가 아니라 브라우저가 구성한 DOM을 조작합니다.

// ① 요소를 찾고
const el = document.getElementById("demo");

// ② 내용을 바꾸고
el.innerHTML = "안녕하세요";

// ③ 스타일도 바꿉니다
el.style.color = "red";

요소를 찾는 방법은 여러 가지입니다. CSS 선택자를 사용할 수 있는 querySelector()가 자주 쓰입니다.

document.getElementById("demo");      // id로
document.querySelector(".menu");      // CSS 선택자로 (첫 번째 하나)
document.querySelectorAll(".menu");   // CSS 선택자로 (전부)

이벤트

클릭, 입력, 스크롤처럼 페이지에서 발생하는 일이 이벤트입니다. 이벤트 등록에는 여러 방법이 있지만, 일반적으로 addEventListener를 사용합니다.

<!-- 비권장: HTML에 JS를 섞으면 관리가 안 됩니다 -->
<button onclick="doSomething()">클릭</button>
// 권장: 핸들러를 여러 개 달 수 있고, HTML과 완전히 분리됩니다
const btn = document.getElementById("myBtn");

btn.addEventListener("click", () => {
  document.getElementById("demo").innerHTML = "눌렸습니다!";
});

이벤트 전파: <div> 안의 <button>을 클릭하면 이벤트는 요소 계층을 따라 전달됩니다. 전달 방향에는 두 가지가 있습니다.

구분 버블링 (Bubbling) 캡처링 (Capturing)
방향 자식 → 부모 (안에서 밖으로) 부모 → 자식 (밖에서 안으로)
설정 기본값 addEventListener(t, fn, true)
활용도 높음 (이벤트 위임의 원리) 낮음

자주 쓰는 제어 두 가지도 알아두면 좋습니다.

  • event.preventDefault(): 태그의 기본 동작을 막습니다. 폼 제출 시 새로고침되는 걸 막을 때 거의 항상 씁니다.
  • event.stopPropagation(): 부모로 전파되는 걸 중단합니다.

비동기

JavaScript는 싱글 스레드로 실행됩니다. 네트워크 요청처럼 시간이 걸리는 작업을 동기적으로 처리하면 UI 응답이 지연될 수 있습니다. 이런 작업을 기다리는 동안 다른 코드를 실행하도록 처리하는 방식이 비동기입니다.

비동기 처리 방식은 다음과 같이 발전해 왔습니다.

세대 방식 특징
1세대 Callback 중첩이 깊어지면 코드 흐름을 파악하기 어려움
2세대 (ES6) Promise 상태를 객체로 만들어 .then() / .catch()로 연결
3세대 (ES8) async / await 비동기 코드를 동기 코드처럼 읽을 수 있음. 현재 널리 사용

Promise는 비동기 작업의 완료 또는 실패 결과를 나타내는 객체입니다. 세 가지 상태를 가집니다.

  • Pending: 아직 진행 중
  • Fulfilled: 성공, 결과값이 있음
  • Rejected: 실패, 에러가 있음
// async/await: Promise를 훨씬 읽기 쉽게 쓴 것
async function getData() {
  const res  = await fetch("https://api.example.com/data");
  const data = await res.json();
  console.log(data);
}

셋이 함께 일하는 모습

버튼을 클릭하면 문구가 바뀌는 예제입니다. HTML, CSS, JavaScript가 각각 무엇을 처리하는지 확인할 수 있습니다.

<!-- HTML : 무엇이 있는가 -->
<button id="btn">클릭</button>
<p id="msg" class="greeting">안녕하세요</p>

<!-- CSS : 어떻게 보이는가 -->
<style>
  .greeting { color: navy; font-size: 20px; }
</style>

<!-- JavaScript : 어떻게 움직이는가 -->
<script>
  document.getElementById("btn").addEventListener("click", () => {
    document.getElementById("msg").innerHTML = "반갑습니다!";
  });
</script>

HTML이 버튼과 문단을 만들고, CSS가 색과 크기를 정의하며, JavaScript가 클릭 이벤트에 따라 내용을 변경합니다. 세 파일로 분리해도 동일하게 동작합니다. 역할을 분리하면 코드 관리가 쉬워집니다.

정리하면

한 줄 정리: HTML은 구조, CSS는 표현, JavaScript는 동작과 로직을 담당합니다. 역할을 분리하는 것이 유지보수 가능한 프론트엔드 코드의 기본입니다.

처음부터 모든 태그와 속성을 외울 필요는 없습니다. 문법은 필요할 때 문서를 참고하면 됩니다. 대신 block과 inline의 차이, 박스 모델의 계산 방식, DOM의 개념은 반드시 이해해두는 것이 좋습니다. 이 세 가지를 기준으로 나머지 개념을 확장해 나갈 수 있습니다.

참고

앞 글에서는 이 파일들이 서버에서 브라우저까지 어떻게 도착하는지(HTTP·DNS·TCP/IP) 정리했습니다.