Computer >> 컴퓨터 >  >> 프로그래밍 >> CSS

CSS 특이성(Specificity) 완벽 가이드 – 선택자 우선순위 계산법 총정리

CSS 특이성(Specificity)은 발음하기도 어려운 용어일 뿐만 아니라(빠르게 세 번 말해보세요!), 이해하기도 꽤 까다로운 개념입니다. CSS에서 가장 헷갈리기 쉬운 개념 중 하나죠. 이 글에서는 특이성이 무엇을 의미하는지, 어떻게 계산되는지, 그리고 선택자의 순위가 페이지에 실제로 렌더링되는 스타일을 어떻게 결정하는지 차근차근 살펴보겠습니다. 걱정하지 마세요. 이 글을 끝까지 읽으면 특이성 전문가가 될 수 있을 겁니다!

특이성(Specificity)이란 무엇인가?

CSS 특이성은 기본적으로 선택자(selector)가 얼마나 '중요한지'를 나타내는 지표입니다. 각 선택자는 그 유형에 따라 고유한 특이성 값을 부여받으며, 숫자가 클수록 더 구체적(specific)이고 해당 CSS 블록이 스타일 경쟁에서 승리할 확률이 높아집니다.

특이성은 네 개의 숫자로 표현되며, 각 숫자는 쉼표로 구분됩니다.

선택자 특이성 = 0, 0, 0, 0

특이성을 쉼표 없이 네 자리 숫자 문자열로 보면(예: 0000, 0101, 1001 등), 숫자가 클수록 더 구체적이라는 뜻입니다.

선택자별 특이성 순위 (낮음 → 높음)

이제 특이성이 낮은 순서부터 높은 순서까지 각 선택자 유형을 하나씩 살펴보겠습니다.

* (전체 선택자)

전체 선택자(universal selector)입니다. 특이성은 0, 0, 0, 0으로, 보통 스타일시트 상단에서 볼 수 있습니다. 중요도 면에서 가장 낮은 순위를 차지하며, 사실상 어떤 CSS 규칙이든 이를 덮어쓰거나 추가할 수 있습니다.

>, +, 공백(' '), ~ (결합자)

결합자(combinator)는 주로 유형 선택자나 클래스 선택자의 자식 또는 형제 요소를 선택할 때 사용되며, 특이성에 아무런 영향을 주지 않고 전체 숫자에도 포함되지 않습니다. 부정 의사 클래스(:not()) 역시 이 범주에 속합니다.

div, form, ul, li 등 (유형 선택자)

이것들은 유형(type) 선택자입니다. HTML에서 태그라고 할 수 있는 거의 모든 요소를 포괄하며, 특이성 가중치는 0, 0, 0, 1입니다. 여러 유형 선택자가 결합자로 연결되면 선택자 값들이 서로 합산됩니다.

div li {} ⇐ 특이성은 0, 0, 0, 2

div li p {} ⇐ 특이성은 0, 0, 0, 3

.main-container, .navbar, [type="text"], :hover (클래스 · 속성 · 의사 클래스 선택자)

그다음으로 특이성이 높은 것은 클래스(class), 속성(attribute), 의사 클래스(pseudo-class) 선택자입니다.

클래스 선택자는 이름 앞의 점(.)으로 표시됩니다. 속성 선택자는 유형 선택자 옆 대괄호 안에 속성을 넣어 표현하고, 의사 클래스 선택자는 더 구체적인 스타일링이 필요한 특수 상태의 요소에 사용됩니다. 이들의 특이성은 0, 0, 1, 0입니다.

div.navlink a {}

— 위 선택자의 특이성은 0, 0, 1, 1입니다. 클래스 선택자 하나와 유형 선택자 하나가 포함되어 있기 때문입니다.

div.header li.list-item-disc button[type="submit"] {}

— 위 선택자의 특이성은 0, 0, 3, 3입니다. 유형 선택자 3개와 클래스 선택자 2개, 속성 선택자 1개가 포함되어 있기 때문입니다. 만약 속성 선택자를 제거하면 특이성은 0, 0, 2, 3이 됩니다. 23이 33보다 작으므로, 특이성이 0, 0, 3, 3인 선택자가 더 높은 특이성을 가지며 스타일 경쟁에서 승리합니다.

#blue, #hidden 등 (ID 선택자)

ID 선택자는 CSS 선택자 중 가장 높은 특이성을 가집니다: 1, 0, 0, 0. ID 속성은 문서 내에서 고유해야 하기 때문에, CSS에서 ID를 지정하면 보통 특정 단일 요소를 정확히 타겟팅하게 됩니다.

div#orange {}

— 위 선택자의 특이성은 1, 0, 0, 1입니다. 유형 선택자와 ID 선택자가 함께 사용되었기 때문입니다. 그렇다면 아래 두 규칙 중 어떤 스타일이 특이성 규칙에 따라 승리할까요?

div#orange {
background-color: orange;
}
div.square {
background-color: green;
width: 150px;
height: 150px;
}
  1. 첫 번째 div는 유형 선택자와 ID 선택자를 함께 가지므로 전체 특이성은 1, 0, 0, 1입니다.
  2. 두 번째 div는 유형 선택자와 클래스 선택자를 가지므로 전체 특이성은 0, 0, 1, 1입니다.

1001이 11보다 크므로 첫 번째 div가 승리하고 배경색은 주황색(orange)이 됩니다. 흥미로운 점은, 이 결과가 CSS의 캐스케이딩(cascading, 위에서 아래로 적용되는) 특성과는 무관하게 오직 특이성만으로 결정된다는 사실입니다.

인라인 스타일(Inline Style)

인라인 스타일은 이 체계에서 어디에 위치할까요? 인라인 스타일은 ID 선택자보다도 구체적입니다. HTML 태그에 직접 작성된 인라인 스타일은 CSS 파일 안의 거의 모든 규칙을 능가하며, 특이성 측면에서 가장 높은 가중치를 가집니다.

!important 규칙

!important 키워드는 모든 것을 능가합니다. 가장 강력한 선언으로, CSS에서 이전에 설정된 어떤 규칙이든 덮어씁니다. 다만 남용하면 코드 유지보수가 어려워지므로 꼭 필요한 경우에만 사용하는 것이 좋습니다.

연습 문제

아래 코드에는 임의의 클래스 이름과 ID 이름을 가진 샘플 컨테이너 페이지가 담겨 있습니다. CSS 실력을 활용해 특정 요소만 다른 색으로 바꾸고 나머지 div는 원래 색을 유지하는 연습을 해보세요. 그 규칙을 CSS로 어떻게 작성하시겠습니까? 특정 요소를 타겟팅하기 위해 최고 수준의 특이성이 필요하다면 어떤 선택자 조합을 사용하시겠습니까? 코드 안의 주석에 담긴 질문들에 스스로 답해 보면서 특이성 감각을 익혀보세요.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CSS Specificity</title>
<style>
body {
width: 100%;
max-width: 800px;
margin: 0 auto;
}

main {
width: 100%;
height: 800px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}

div {
text-align: center;
}
*.square {
/* 여기서 별(*)은 무슨 의미일까요?
특이성 숫자에 영향을 줄까요?
가설을 직접 코드로 테스트해 보세요! */
height: 150px;
width: 150px;
background-color: black;
color: white;
margin: 20px;
}

div.flex {
/* 이 선택자는 .square 클래스와 경쟁할까요,
아니면 함께 작동할까요?
여기에 width와 height를 추가하면 어떻게 될까요?
그 이유는 무엇일까요?

이 선택자는 div 선택자를 덮어쓸까요? 왜 그럴까요?
두 선택자의 특이성을 비교해 보세요. */
display: flex;
justify-content: center;
align-items: center;
}
div#red {
/* 왜 이 규칙이 승리할까요?
이 div의 특이성은 얼마일까요? */
background-color: darkred;
}
#red {
/* 앞선 선택자에서 'div'를 빼면 어떻게 될까요?
어느 쪽이 이길까요? */
background-color: red;
}

main > div {
font-size: 55px;
font-weight: bold;
}

div#self-align {
/* 여기서는 무슨 일이 일어날까요? */
align-self: center;
}
</style>
</head>
<body>
<main>
<div id="self-align" class="square flex">1</div>
<div class="square flex">2</div>
<div class="square flex">3</div>
<div class="square flex">4</div>
<div class="square flex">5</div>
<div id="red" class="square flex">6</div>
<div class="square flex">7</div>
<div class="square flex">8</div>
<div class="square flex">9</div>
<div class="square flex">10</div>
<div class="square flex">11</div>
<div class="square flex">12</div>
</main>
</body>
</html>

마무리

이 글에서는 CSS 특이성에 대해 살펴보았습니다. 어떤 선택자가 다른 선택자보다 더 구체적인지, 그리고 실제 특이성 숫자를 어떻게 계산하는지 배웠습니다. 한 번 읽어서 바로 이해되지 않더라도 낙담하지 마세요. 소화해야 할 정보가 분명히 많습니다. 핵심은 전체 선택자 → 유형 선택자 → 클래스 · 속성 · 의사 클래스 선택자 → ID 선택자 → 인라인 스타일 → !important 순으로 특이성이 높아진다는 흐름을 기억하는 것입니다. 꾸준히 연습하다 보면 어느새 특이성 계산이 익숙해져 있을 겁니다!