CSS를 작성하다 보면 마치 "키-값" 쌍으로 데이터를 어딘가에 넘겨주는 듯한 느낌이 든다.
payload를 post에 태워보내 듯.
정말 CSS는 그런 식으로 동작하는 걸까?
브라우저는 이 선언형 언어를 어떻게 해석하고, 어떤 흐름으로 사용자 화면에 렌더링하는 건지 궁금해졌다.
1. CSS는 선언형이자 "맵" 기반 구조
- CSS는 기본적으로 속성: 값 형태의 선언적 문법을 갖고 있다
- 이는 내부적으로도 마치 자바스크립트 객체나 JSON처럼 "맵(Map) 구조"로 처리된다
.box {
color: red;
font-size: 16px;
}
→ 내부 구조는 다음처럼 해석된다
{
"color": "red",
"font-size": "16px"
}
2. CSS는 어떻게 브라우저에 의해 로드되고 처리될까?
1) 로드 (Loading)
- HTML 문서 내 <link> 또는 <style> 태그로 CSS 리소스를 참조
- 브라우저는 이를 비동기로 요청
2) 파싱 (Parsing)
- CSSParser가 문자열을 토큰(token) 으로 분리
- 각 selector별로 property: value 쌍을 CSSOM (CSS Object Model) 으로 구성
3) 스타일 계산 (Style Resolution)
- 각 DOM 요소에 대해 어떤 CSS 룰이 적용되는지 셀렉터 매칭
- 상속, 우선순위(cascade), 계산(computed value) 등을 반영하여 스타일 병합
4) 렌더 트리 구성 (Render Tree)
- 계산된 스타일 정보를 바탕으로 LayoutObject 생성
- ComputedStyle에서 color, font-size 등의 정보를 필드로 갖고 있음
5) 페인팅 (Painting)
- 렌더 트리를 따라 요소의 위치와 스타일이 정해지고, 픽셀 단위로 화면에 그려짐
3. Chrome의 CSSParser
Chrome(Blink 엔진)에서 CSSParser.cpp는 다음과 같이 파싱을 시작한다.
bool CSSParser::parseDeclarationList(
const CSSParserContext& context,
MutableStylePropertySet* propertySet,
const String& declaration)- 이 함수는 CSS 선언 문자열을 받아서 propertySet이라는 key-value 구조체에 각각 저장
- 이후 StyleResolver와 ComputedStyle을 통해 실제 요소에 반영됨
4. CSS는 결국 "데이터"이며, 자료구조가 본질이다
- JSON이든 CSS든 모두 결국 key-value 기반 데이터 표현 형식
- JSON은 객체(Object)를 텍스트로 직렬화한 형식이고, CSS는 시각 스타일을 위한 DSL
- 브라우저는 이를 모두 내부 자료구조(Map, Tree 등) 로 바꿔 처리함
CSS는 스타일을 정의하는 언어이지만, 브라우저는 그것을 구조화된 데이터로 받아들인다.
스타일링은 단순히 보기 좋게 꾸미는 것을 넘어, 시스템이 데이터를 다루는 방식을 이해하는 과정이 된다.
5. 데이터 관리 관점에서 나쁜 CSS
div {
color: red;
font-size: 14px;
}1) 클래스 없이 태그만 사용
- 구조적 구분 없이 모든 <div>에 동일한 스타일 적용 → 확장/재사용 불가
- 다른 목적의 div도 덩달아 스타일이 적용됨 → 의도치 않은 사이드 이펙트
- → 마치 데이터에서 key 없이 value만 나열한 것처럼 스코프가 없음
.btn { font-size: 14px; color: red; }
.button { font-size: 14px; color: red; }
.submit-btn { font-size: 14px; color: red; }2) 중복 속성이 많고, 규칙이 없음
- 모든 클래스가 같은 데이터를 중복해서 들고 있음 → DRY 원칙 위배
- 나중에 font-size를 바꾸려면 여러 곳을 수정해야 함
- → 데이터 관점에선 "같은 값을 여러 곳에 복붙"한 비효율적 구조
.red-font {
color: red;
}
.big-text {
font-size: 32px;
}3) 의미 없는 클래스 네이밍
- 스타일이 바뀌면 클래스명도 함께 바꿔야 함 → 비정상적 의존성
- UI의 역할이나 의미가 아닌, 값에 의존한 네이밍 → 데이터 레벨의 추상화 부족
- 결과적으로 데이터 의미 → 표현 방식으로 직결 → 유지보수에 취약
6. 데이터 관리 관점에서 좋은 CSS
.card__title {
font-size: 16px;
color: var(--primary-text);
}1) 역할 기반 네이밍 + 범위 제한
- 클래스명이 역할 기반 (title) + 컴포넌트명 (card__) → 데이터 스코프가 명확함
- 변수 사용(var(--primary-text))으로 데이터 추상화되어 있음
- 어디에서 쓰이는지, 어떤 역할인지 명시적으로 표현 → 재사용성 & 유지보수 좋음
.text-sm { font-size: 14px; }
.text-primary { color: var(--primary-text); }2) 공통 유틸리티 분리
- 작은 조각으로 나뉜 속성 → 컴포저블한 구조 (조합 가능)
- 중복 최소화, 예측 가능성 증가
- 마치 데이터베이스의 정규화처럼 데이터 재사용과 일관성 확보
.btn { background-color: gray; }
.btn--active { background-color: blue; }
.btn--disabled { opacity: 0.5; pointer-events: none; }3) 상태 분리 (modofier 패턴)
- 스타일을 상태별로 분리 → 논리적 상태와 UI 표현이 분리됨
- 데이터로 치면 "상태값"을 기준으로 스타일 조건 분기하는 셈
- 훨씬 유지보수하기 좋고, 확장도 쉬움
7. 결론
유지보수 쉬움, 리팩토링 쉬움, 버그 줄어듦(개발 습관, 협업 구조) 관점은 주관적이다.
사람, 문화, 팀 구성에 따라 얼마든지 다르게 느껴질 수 있다.
어떤 팀은 BEM이 최고라 하고, 어떤 팀은 Tailwind가 최고라 할 수 있는 "정답"이 없는 영역이다.
그럼에도 CSS의 데이터관리를 한번쯤 고려하며 작업해봐야 할 의의는 렌더링 최적화에 있다.
브라우저가 CSS 셀렉터를 어떻게 파싱하고 적용하느냐는 알고리즘적, 구조적 차원에서 성능에 영향을 준다.
/* 성능에 부담을 주는 구조 */
div ul li a span:hover {
color: red;
}
위 코드의 경우 DOM 트리를 따라가며 후방 탐색(backtrack) 해야 해서 성능이 떨어진다.
반면 다음처럼 클래스 기반 구조는 빠르게 매칭 가능해진다.
.btn--active {
color: red;
}
실제로 크롬이나 파이어폭스 개발자 문서에서도:
- “간결한 클래스 기반 셀렉터가 가장 빠르고 성능에 유리”하다고 명시
- → 이건 사람에 관계없는 기술적 진실
Analyze CSS selector performance during Recalculate Style events - Chrome DevTools
The truth about CSS selector performance - Microsoft Windows Blogs
8. 진짜 결론 : 브라우저 최적화를 고려해서 작업한다는 게 비즈니스에 주는 이점이 뭔가?
"SEO에 간접적인 기여를 한다."
CSS 자체는 SEO 랭킹에 직접적 영향을 주진 않는다.
하지만 렌더링 최적화는 SEO에 간접적으로 상당히 중요하다.
[Core Web Vitals 점수에 직접 영향] - web.dev.Google (Core web vitals)
- Largest Contentful Paint (LCP)
- 사용자가 페이지에 접속한 후 가장 큰 콘텐츠(텍스트/이미지 등)가 화면에 나타나는 데 걸린 시간
- 2.5초 이내면 ‘Good’ 평가, 4초 이상이면 ‘Poor’
- First Input Delay (FID)
- 사용자가 페이지를 클릭, 탭, 키보드 입력 등으로 상호작용했을 때
→ 브라우저가 실제로 응답을 시작하는 데까지 걸리는 시간
- 사용자가 페이지를 클릭, 탭, 키보드 입력 등으로 상호작용했을 때
- Cumulative Layout Shift (CLS)
- 페이지가 로드되거나 동작 중 예기치 않게 요소가 움직인 총 거리
(예: 글 읽고 있는데 이미지가 늦게 떠서 글이 밀림) - 0.1 이하가 'Good', 0.25 이상이면 'Poor'
- 페이지가 로드되거나 동작 중 예기치 않게 요소가 움직인 총 거리
이 점수는 Google 검색 순위에 실제 반영된다.
즉, 렌더링 최적화로 CLS/LCP 개선 → SEO 점수 개선에 기여한다는 것이다.
특히 모바일 페이지 최적화 시 중요하다.
[ LCP를 망치는 CSS구조의 예 ]
.hero {
font-size: 0; /* reset */
visibility: hidden;
animation: fadeIn 2s ease-in;
}
- 첫 진입 시 .hero가 숨겨진 상태로 있다가 애니메이션 후 보이게 됨
- 브라우저는 이걸 최종 렌더된 시점으로 판단 → LCP 시간이 늦어짐
[CLS를 유발하는 예]
<img src="/banner.png">
- width, height 없이 이미지 삽입 → 브라우저가 크기 모름
- 이미지 로드 시점에 레이아웃 밀림 → CLS 상승
- 방지 방안 (CLS 최적화)
- 공간 확보 : 이미지에 width, height 또는 aspect-ratio 지정
- FOIT 방지 : 글꼴 바뀜 방지 → font-display: swap 사용
- 레이아웃 점프 방지 : 동적 DOM 추가 시 placeholder 미리 렌더
*FOIT (Flash of Invisible Text) "텍스트가 보이지 않는 순간"
즉,
CSS 구조를 잘 설계하는 건 단순히 “예쁘게 정리”하기 위함이 아니라
브라우저가 더 빠르게 해석하고, 더 효율적으로 렌더링 하고, 사용자가 더 부드럽게 경험하도록 돕는 실질적인 퍼포먼스 최적화의 시작점이라 볼 수 있다.
렌더링 최적화된 CSS 구조 →
→ 빠른 콘텐츠 표시 (LCP 개선)
→ 안정적 레이아웃 (CLS 개선)
→ Core Web Vitals 점수 향상
→ SEO 순위 향상 (Google 기준)
9. 반성
문법 모양으로 시작한 의문점이 애니메이션, 인터렉션을 이유로 나도 모른세 LCP를 깎아 먹고 있었다는 것을 깨닫게 했다.
시멘틱, 콘텐츠 내용, 유저 진입 및 정체 시간 등이 월등하면 CSS가 주는 차이는 미미해질 수도 있겠지만 그게 아니라면 염두에 두고 작업해야겠다.
이 방식이 잘 적용될 수있는 claude지침을 생성하는 것이 다음 숙제.

'D.evelop > CSS' 카테고리의 다른 글
| QR 마스크 만들기 (SVG) (0) | 2025.05.10 |
|---|---|
| [CSS]권장 작성법 (0) | 2021.09.12 |
| [Flex]기존 css를 Flex속성으로 수정하기 (0) | 2021.09.06 |
| [CSS]영역과 위치 잡기 - position편 (0) | 2021.09.01 |
| [CSS]영역과 위치 잡기 - display편 (0) | 2021.08.31 |
댓글