거의 한 달 가까이 대면 스터디가 쉬었습니다. 그 사이 비대면 자료로는 HTML/CSS 마무리와 JavaScript 초반부까지 진행됐습니다. 그래서 오늘은 먼저 앞으로의 진행 방식을 조금 바꿔보려고 합니다.
지금까지는 비대면에서 배운 내용을 대면에서 다시 복습하고, 문제를 풀고, 면접 질문을 짚는 흐름이었습니다. 그런데 앞으로 JavaScript와 React로 들어가면, 비대면 자료만으로도 문법과 사용법은 어느 정도 따라갈 수 있습니다. 대면에서 같은 문법을 다시 설명하면 시간이 조금 아깝습니다.
그래서 앞으로 대면 스터디는 문법을 다시 강의하는 시간보다, 혼자 강의 영상이나 글로는 얻기 어려운 실무 맥락과 개발 환경의 큰 그림을 다루는 시간으로 바꿔보겠습니다.
다시 말해 비대면이 "배우는 시간"이라면, 대면은 "개발자가 되는 시간"에 가깝게 운영해보려고 합니다. 오늘은 그 첫 번째 차시로, 지금까지 배운 HTML, CSS, JavaScript가 브라우저 안에서 어떻게 연결되는지 큰 그림을 잡아보겠습니다.
오늘 모든 문법을 다시 외우는 게 목표는 아닙니다. "아, 이 개념이 여기랑 연결되는구나" 하는 감각을 되살리는 것이 목표입니다.
HTML은 화면의 구조를 만듭니다. 단순히 글자와 박스를 놓는 것이 아니라, 이 영역이 제목인지, 본문인지, 버튼인지, 링크인지 브라우저와 검색엔진, 스크린리더에게 알려주는 역할을 합니다.
<header>사이트 상단</header>
<main>
<section>
<h1>스터디 모집</h1>
<p>프론트엔드 기초부터 함께 공부합니다.</p>
<button>신청하기</button>
</section>
</main>
HTML을 잘 쓴다는 건 "div를 적게 쓴다"가 아니라, 브라우저가 이해할 수 있는 구조로 의미를 남긴다는 뜻입니다. 이 구조가 뒤에서 볼 DOM의 출발점이 됩니다.
CSS는 HTML 구조에 스타일을 입히고, 요소들을 어디에 어떻게 배치할지 결정합니다. 특히 4주차에서는 Position, Flexbox, Grid, 반응형 단위처럼 "레이아웃을 만드는 도구"들을 다뤘습니다.
static: 기본값. 문서 흐름대로 배치됩니다.relative: 원래 자기 위치를 기준으로 이동합니다. 원래 자리는
남습니다.absolute: 가장 가까운 positioned 조상을 기준으로 배치되고,
문서 흐름에서 빠집니다.fixed: 뷰포트 기준으로 고정됩니다. 스크롤해도 따라옵니다..card {
position: relative;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
}
absolute를 쓸 때 부모에 position: relative를 주는 패턴은 계속 나옵니다.
"이 요소가 어디를 기준으로 움직이는가"를 보는 것이 Position의 핵심입니다.
Flexbox는 한 방향으로 정렬할 때 강합니다. 가로 메뉴, 버튼 묶음, 카드 내부 정렬 같은 곳에서 자주 씁니다. Grid는 행과 열을 동시에 다루는 2차원 레이아웃에 강합니다.
/* 한 줄/한 방향 정렬 */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 행과 열을 함께 다루는 레이아웃 */
.card-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
}
면접식으로 짧게 말하면 한 방향이면 Flex, 행과 열을 함께 잡으면 Grid 입니다. 실제 프로젝트에서는 페이지 큰 구조는 Grid, 컴포넌트 내부 정렬은 Flex로 섞어 쓰는 경우가 많습니다.
JavaScript는 HTML/CSS로 만들어진 화면에 동작을 붙입니다. 버튼을 눌렀을 때 값이 바뀌고, 입력값을 검증하고, 서버에서 데이터를 가져와 화면을 바꾸는 일이 JS의 영역입니다.
JS는 동적 타입 언어입니다. 변수에 타입을 미리 고정하지 않고, 값이 들어가는 순간 타입이 결정됩니다.
let value = 1;
value = "hello";
value = true;
이 자유로움은 편하지만, 동시에 헷갈리는 동작도 많습니다. 예를 들어 typeof null은 'object'이고, NaN === NaN은 false입니다. 그래서 JS는 "문법을
안다"보다 이상하게 동작하는 지점을 알고 있다가 중요합니다.
console.log(a); // undefined
var a = 1;
console.log(b); // ReferenceError
let b = 1;
둘 다 호이스팅은 일어납니다. 차이는 var는 선언과 undefined 초기화가 함께
처리되고, let/const는 선언은 알지만 초기화 전까지 TDZ에 있다는 점입니다.
그래서 요즘 코드는 기본적으로 const, 필요할 때 let, var는 거의 쓰지 않는
방향으로 갑니다.
6주차에서는 JS의 핵심 세 가지인 객체, 함수, 스코프를 다뤘습니다.
const user = { name: "Jin", age: 24 };
const sameUser = user;
sameUser.age = 25;
console.log(user.age); // 25
원시값은 값 자체가 복사되지만, 객체는 참조가 복사됩니다. 같은 객체를 두 변수가 바라보는 상황이 생기는 거죠. 이 감각은 이후 React의 상태 관리와 불변성으로 이어집니다.
function outer() {
const name = "outer";
function inner() {
console.log(name);
}
inner();
}
스코프는 변수를 어디서 찾을 수 있는지 정하는 규칙입니다. 특히 JS는 함수가 호출된 위치가 아니라 선언된 위치를 기준으로 상위 스코프가 정해집니다. 이것을 렉시컬 스코프라고 부릅니다.
이제 오늘의 본론입니다. 우리가 쓴 HTML, CSS, JS는 그냥 바로 화면에 보이는 게 아닙니다. 브라우저가 여러 단계를 거쳐 코드를 해석하고, 배치하고, 색칠하고, 합성해서 화면에 보여줍니다.
HTML → DOM
CSS → CSSOM
DOM + CSSOM → Render Tree
Render Tree → Layout(Reflow) → Paint → Composite
JavaScript → DOM/CSSOM 변경 → 필요한 단계 다시 실행
브라우저는 HTML 문자열을 그대로 화면에 그리지 않습니다. HTML을 읽어서 DOM(Document Object Model)이라는 트리 구조로 바꿉니다.
<body>
<h1>Hello</h1>
<button>Click</button>
</body>
Document
└─ body
├─ h1
└─ button
DOM은 JS가 HTML을 조작할 수 있게 해주는 객체 구조입니다.
document.querySelector, appendChild, textContent 같은 API가 이 DOM을
다룹니다.
CSS도 마찬가지로 그냥 문자열로 남아있지 않습니다. 브라우저는 CSS를 읽고 CSSOM(CSS Object Model)을 만듭니다. 어떤 요소에 어떤 스타일이 적용되는지 계산하기 위한 구조입니다.
button {
background-color: black;
color: white;
}
button:hover {
background-color: #333;
}
여기에는 선택자 우선순위, 상속, 기본 스타일, 미디어 쿼리 같은 CSS 규칙이 함께 반영됩니다. 우리가 CSS를 복잡하게 쓰면, 브라우저도 그만큼 계산할 것이 많아집니다.
브라우저는 DOM과 CSSOM을 합쳐서 실제로 화면에 그릴 노드만 모은 Render Tree를 만듭니다. 여기서 중요한 점은 DOM에 있다고 해서 무조건 화면에 그려지는 것은 아니라는 점입니다.
.hidden {
display: none;
}
.invisible {
visibility: hidden;
}
display: none: Render Tree에서 빠집니다. 자리도 차지하지
않습니다.visibility: hidden: 보이지만 않을 뿐, 레이아웃 자리는
남습니다.이 차이를 알면 "왜 얘는 사라졌는데 공간도 같이 없어지고, 얘는 안 보이는데 공간은 남지?" 같은 상황을 이해할 수 있습니다.
2-4. Layout(Reflow) — 박스의 위치와 크기를 계산한다
Render Tree가 만들어지면 브라우저는 각 요소가 화면 어디에, 어떤 크기로 놓일지 계산합니다. 이 단계를 Layout 또는 Reflow라고 부릅니다.
다음과 같은 속성을 바꾸면 레이아웃 계산이 다시 필요해집니다.
width, heightmargin, paddingtop, leftfont-sizeReflow가 비싼 이유는 내 요소 하나만 다시 계산하면 끝나는 게 아니라, 주변 요소들의 위치까지 영향을 받을 수 있기 때문입니다.
위치와 크기가 정해지면 이제 색을 칠합니다. 배경색, 글자색, 그림자, 테두리, 이미지 같은 시각적 요소가 이 단계에서 그려집니다.
다음 속성은 보통 Layout까지는 다시 하지 않고 Paint부터 영향을 줍니다.
background-colorcolorbox-shadowborder-color마지막으로 브라우저는 여러 레이어를 합쳐 최종 화면을 만듭니다. 여기서 중요한
속성이 transform과 opacity입니다. 이
둘은 많은 경우 Layout과 Paint를 건너뛰고 Composite 단계에서 처리될 수 있어
애니메이션에 유리합니다.
/* 비싼 애니메이션이 되기 쉬움: 위치 계산을 다시 해야 함 */
.bad-move {
position: relative;
left: 100px;
}
/* 더 선호되는 방식: 합성 단계에서 처리하기 쉬움 */
.good-move {
transform: translateX(100px);
}
같은 "움직임"처럼 보여도 브라우저 입장에서는 비용이 다릅니다. 그래서 실무에서
부드러운 애니메이션을 만들 때는 top, left보다 transform을 우선
고려합니다.
JavaScript는 DOM이나 스타일을 바꿉니다. 그러면 브라우저는 필요한 만큼 렌더링 과정을 다시 수행합니다.
const button = document.querySelector("button");
const box = document.querySelector(".box");
button.addEventListener("click", () => {
box.textContent = "변경됨";
box.style.width = "300px";
});
위 코드에서 텍스트와 너비가 바뀌면 브라우저는 DOM/CSSOM 변화에 맞춰 다시 계산합니다. 너비가 바뀌었으니 Layout이 다시 필요할 수 있고, 화면도 다시 칠해집니다.
이 감각은 React를 배울 때 중요해집니다. React에서 상태가 바뀌면 컴포넌트가 다시 계산되고, 그 결과 DOM 변경이 필요하면 브라우저 렌더링으로 이어집니다. 그래서 나중에 "리렌더링", "상태", "불변성"을 배울 때 오늘의 파이프라인이 밑바탕이 됩니다.
HTML은 구조를 만들고, CSS는 배치와 스타일을 정하고, JavaScript는 동작을 붙입니다. 그리고 브라우저는 이 세 가지를 DOM, CSSOM, Render Tree, Layout, Paint, Composite 단계를 거쳐 실제 화면으로 바꿉니다.
앞으로 React를 배우면 "상태가 바뀌면 화면이 다시 그려진다"는 말을 계속 듣게 됩니다. 오늘 본 렌더링 파이프라인은 그 말을 이해하기 위한 바닥입니다. 문법을 외우는 것도 중요하지만, 브라우저가 실제로 무슨 일을 하는지 아는 사람은 코드를 조금 다르게 보게 됩니다.
다음 차시에서는 코드 문법에서 한 걸음 더 나가서, AI 시대에 프론트엔드 개발자가 실제로 어떻게 일하는지, 그리고 프론트엔드 개발자가 팀 안에서 어떤 위치를 가지는지 이야기해보겠습니다.