Handling conflicts

김동현·2026년 3월 18일

mdn 학습 번역 - CSS

목록 보기
7/190

충돌하는 규칙들 (Conflicting rules)

CSS는 Cascading Style Sheets의 약자입니다. 여기서 첫 번째 단어인 Cascading(폭포수처럼 위에서 아래로 흐르는)은 이해하기 엄청나게 중요합니다. 캐스케이드가 어떻게 동작하는지 아는 것이 CSS를 이해하는 핵심 열쇠거든요.

프로젝트를 진행하다 보면 분명히 "이 요소에 적용되어야 할 CSS가 왜 안 먹히지?" 하는 순간이 올 겁니다. 보통 이런 문제는 동일한 요소에 대해 동일한 속성을 다르게 지정하는 '두 개 이상의 규칙'을 만들었을 때 발생합니다.

캐스케이드(Cascade)와 이와 밀접하게 관련된 개념인 명시도(Specificity)는 이런 충돌이 발생했을 때 어떤 규칙을 적용할지 제어하는 메커니즘입니다. 요소에 적용되고 있는 스타일 선언이 여러분이 기대한 것이 아닐 수 있으므로, 이 메커니즘이 어떻게 작동하는지 반드시 이해해야 합니다.

여기서 또 하나 중요한 개념은 상속(Inheritance)입니다. 기본적으로 일부 CSS 속성값은 현재 요소의 부모 요소에 설정된 값을 물려받고(상속받고), 어떤 속성들은 물려받지 않습니다. 이것 역시 예상치 못한 동작을 일으킬 수 있는 원인이 됩니다.

우리가 다루게 될 이 핵심 개념들을 간단히 살펴보고, 각각의 개념들이 서로 어떻게 상호작용하는지 알아보겠습니다. 처음에는 조금 까다롭게 느껴질 수 있지만, CSS를 계속 작성하다 보면 점차 머릿속에 맑게 그려질 거예요.


캐스케이드 (Cascade)

스타일시트는 캐스케이드(위에서 아래로 흐름) 합니다. 아주 단순하게 말하자면, CSS 규칙의 '출처'와 '순서'가 중요하다는 뜻입니다. 두 규칙이 완전히 동일한 명시도(우선순위)를 가질 경우, 스타일시트에서 가장 마지막에(아래에) 정의된 규칙이 최종적으로 적용됩니다. (이 외에도 캐스케이드 레이어(cascade layers)처럼 영향을 미치는 다른 고급 개념들이 있지만, 여기서는 깊게 다루지 않겠습니다.)

아래 예제를 보면 <h1> 요소에 적용될 수 있는 두 개의 규칙이 있습니다. 결과적으로 <h1>의 글자 색상은 파란색(blue)이 됩니다. 두 규칙 모두 출처가 같고, 선택자(h1)도 동일하여 명시도가 완벽히 같지만, 소스 코드 순서상 마지막에 작성된 규칙이 승리하기 때문입니다.

<h1>This is my heading.</h1>
h1 {
  color: red;
}
h1 {
  color: blue;
}

💡 강사의 실무 팁!
"나중에 선언된 코드가 이긴다"는 규칙은 실무에서 정말 중요합니다. 예를 들어 외주 라이브러리(Bootstrap이나 Tailwind 등)의 CSS 파일을 <head>에서 먼저 불러오고, 우리가 직접 작성한 style.css를 그 밑에서 불러와야 라이브러리의 기본 디자인을 덮어쓰기(Overriding)할 수 있습니다. 순서가 꼬이면 내 스타일이 안 먹히게 되겠죠?


명시도 (Specificity)

명시도(Specificity)는 브라우저가 요소에 어떤 속성값을 적용할지 결정할 때 사용하는 일종의 '점수 계산 알고리즘'입니다. 동일한 요소에 대해 서로 다른 선택자를 가진 여러 규칙들이 똑같은 속성을 다르게 지정하려고 할 때, 이 명시도가 누가 이길지를 결정합니다. 명시도는 기본적으로 선택자가 얼마나 '구체적으로' 대상을 콕 집어냈는지를 측정하는 지표입니다:

  • 타입(요소/태그) 선택자는 구체성이 가장 떨어집니다. 페이지에 있는 해당 타입의 모든 요소를 선택하기 때문에 점수(가중치)가 낮습니다. 가상 요소(Pseudo-element) 선택자도 일반 요소 선택자와 동일한 명시도를 가집니다.
  • 클래스 선택자는 조금 더 구체적입니다. 특정 class 속성값을 가진 요소만 선택하기 때문에 점수가 더 높습니다. 속성(Attribute) 선택자와 가상 클래스(Pseudo-class)도 클래스와 동일한 가중치를 가집니다.
  • ID 선택자는 훨씬 더 구체적입니다. 특정 id 값을 가진 단 하나의 요소만 선택하죠. 따라서 가중치가 매우매우 높습니다.

아래 예제에도 <h1> 요소에 적용될 수 있는 두 가지 규칙이 있습니다. 이번에는 코드가 소스 순서상 color: blue가 나중에 작성되었음에도 불구하고, 결과적으로 글자 색상은 빨간색(red)이 됩니다. 왜냐하면 .main-heading이라는 클래스 선택자가 h1이라는 요소 선택자보다 더 높은 명시도 점수를 가지기 때문입니다. 명시도가 더 높은 쪽의 규칙이 승리하여 적용됩니다.

<h1 class="main-heading">This is my heading.</h1>
.main-heading {
  color: red;
}

h1 {
  color: blue;
}

명시도를 계산하는 구체적인 알고리즘은 조금 뒤에서 더 자세히 설명해 드릴게요.


상속 (Inheritance)

상속(Inheritance) 역시 충돌 해결에서 반드시 이해해야 할 맥락입니다. 어떤 CSS 속성값은 부모 요소에서 자식 요소로 알아서 흘러 내려가고(상속되고), 어떤 것들은 그렇지 않습니다.

예를 들어, 요소에 colorfont-family를 설정하면 별도로 폰트나 색상을 지정하지 않는 한 그 안에 들어있는 모든 자식 요소들도 동일한 색상과 폰트 스타일을 갖게 됩니다.

<p>
  As the body has been set to have a color of blue this is inherited through the
  descendants.
</p>
<p>
  We can change the color by specifically targeting an element with a different
  style, such as this
  <span>span</span>.
</p>
body {
  color: blue;
}

span {
  color: black;
}

하지만 모든 속성이 상속되는 것은 아닙니다. 예를 들어 width(너비)는 상속되지 않아요. 만약 부모 요소에 width: 50%를 줬다고 해서 그 안의 모든 자식들까지 부모 너비의 50%로 쪼그라든다면... 상상만 해도 끔찍하겠죠? CSS로 레이아웃 잡는 게 정말 지옥 같아질 겁니다!

📝 참고사항 (Note):
MDN CSS 속성 레퍼런스 페이지들을 보시면, "형식 정의(Formal definition)"라는 기술 정보 박스가 있습니다. 여기에는 해당 속성이 상속되는 속성인지 아닌지 등의 데이터가 나와 있습니다. 예시로 color 속성의 형식 정의 섹션을 확인해 보세요.


이 개념들이 어떻게 상호작용하는지 이해하기

방금 배운 이 세 가지 개념(캐스케이드, 명시도, 상속)이 힘을 합쳐 최종적으로 어떤 요소에 어떤 CSS가 적용될지를 통제합니다. 다음 섹션에서는 이들이 실제로 어떻게 맞물려 돌아가는지 살펴볼 거예요. 약간 복잡해 보일 수 있지만, CSS 경험이 쌓일수록 몸으로 체득하게 되니 너무 걱정 마세요. 그리고 기억이 안 나면 언제든 다시 찾아보면 됩니다! 최고로 숙련된 개발자들도 모든 세부 사항을 다 외우고 다니진 않거든요.


상속 이해하기 (Understanding inheritance)

상속부터 시작하겠습니다. 아래 예제에는 <ul > 요소 안에 두 단계 깊이로 목록이 중첩되어 있습니다. 가장 바깥쪽 <ul>에 테두리(border), 패딩(padding), 그리고 글자 색상(font color)을 주었습니다.

color 속성은 상속되는 속성입니다. 그래서 color 속성값은 직계 자식인 첫 번째 <li>들뿐만 아니라 간접적인 자식인 중첩된 리스트 안의 요소들에게까지 모두 적용됩니다. 반면, 두 번째 중첩 리스트에는 special이라는 클래스를 주고 다른 색상을 적용했습니다. 그러면 이 새로운 색상이 다시 그 밑의 자식들에게 상속되어 내려갑니다.

<ul class="main">
  <li>Item One</li>
  <li>
    Item Two
    <ul>
      <li>2.1</li>
      <li>2.2</li>
    </ul>
  </li>
  <li>
    Item Three
    <ul class="special">
      <li>
        3.1
        <ul>
          <li>3.1.1</li>
          <li>3.1.2</li>
        </ul>
      </li>
      <li>3.2</li>
    </ul>
  </li>
</ul>
.main {
  color: rebeccapurple;
  border: 2px solid #cccccc;
  padding: 1em;
}

.special {
  color: black;
  font-weight: bold;
}

앞서 언급했듯 width, margin, padding, border 같은 속성들은 상속되지 않습니다. 만약 이 리스트 예제에서 테두리(border)가 자식들에게 상속되었다면, 모든 자식 리스트와 리스트 아이템에 테두리가 쳐져서 박스 안의 박스 안의 박스가 되는 대참사가 벌어졌을 거예요!

모든 CSS 속성에 대해 이게 상속이 되는지 안 되는지 표를 찾아볼 수도 있겠지만, 해당 속성이 꾸며주는 역할이 무엇인지 생각해보면 직관적으로 유추할 수 있는 경우가 많습니다. (색상, 폰트는 상속 O / 여백, 크기, 테두리는 상속 X)


상속 제어하기 (Controlling inheritance)

CSS는 상속을 직접 제어할 수 있는 5가지의 특별한 '전역 속성값'을 제공합니다. 모든 CSS 속성은 이 값들을 가질 수 있어요.

  • inherit
    • 선택된 요소의 속성값을 부모 요소의 값과 동일하게 강제로 맞춥니다. 한마디로 "상속 기능을 켠다"라고 볼 수 있습니다.
  • initial
    • 선택된 요소의 속성값을 CSS 명세서에 정의된 그 속성의 초깃값(initial value)으로 설정합니다.
  • revert
    • 선택된 요소의 속성값을 브라우저의 기본 스타일(User Agent stylesheet)로 되돌립니다. 많은 경우 unset과 비슷하게 동작합니다.
  • revert-layer
  • unset
    • 속성을 자연스러운 상태로 리셋합니다. 즉, 그 속성이 원래 상속되는 속성(예: color)이라면 inherit처럼 동작하고, 상속되지 않는 속성(예: border)이라면 initial처럼 동작합니다.

📝 참고사항 (Note):
각각의 값들이 어떻게 동작하는지에 대한 더 자세한 정보는 출처 유형(Origin types) 문서를 참조하세요.

상속 제어 속성 가지고 놀기

링크 리스트를 보면서 이런 전역 값들이 어떻게 동작하는지 탐구해 봅시다.

예를 들어:
1. 두 번째 리스트 아이템에는 my-class-1이라는 클래스가 있습니다. 이 클래스는 내부에 있는 <a> 태그의 색상을 inherit으로 강제 설정합니다. 그래서 부모(body의 초록색) 색상을 물려받게 되죠. 만약 이 규칙을 지우면 링크 색상이 어떻게 변할까요? (기본 파란색으로 변할 겁니다!)
2. 세 번째와 네 번째 링크의 색상이 왜 그렇게 나오는지 이해가 되시나요? 세 번째 링크는 initial로 설정되어 있습니다. 즉 브라우저의 링크 기본색(파란색)이 아니라 CSS 속성 color 자체의 초기값(보통 검은색)을 사용하게 됩니다. 네 번째 링크는 unset으로 설정되어 있는데, color는 본래 상속되는 속성이기 때문에 부모의 색상인 초록색을 가져오게 됩니다.
3. 만약 여러분이 CSS에 a { color: red; } 라고 추가한다면, 어떤 링크들의 색상이 변할까요?
4. (핵심 팁) 아래에 설명할 all 속성을 읽어보신 후, 여기로 다시 돌아와서 CSS 코드의 colorall로 바꿔보세요 (all: unset; 등). 두 번째 링크가 갑자기 다음 줄로 넘어가고 글머리 기호(bullet)가 생기는 걸 보실 수 있을 거예요. 어떤 속성들이 리셋된 걸까요?

<ul>
  <li>Default <a href="#">link</a> color</li>
  <li class="my-class-1">Inherit the <a href="#">link</a> color</li>
  <li class="my-class-2">Reset the <a href="#">link</a> color</li>
  <li class="my-class-3">Unset the <a href="#">link</a> color</li>
</ul>
body {
  color: green;
}

.my-class-1 a {
  color: inherit;
}

.my-class-2 a {
  color: initial;
}

.my-class-3 a {
  color: unset;
}

모든 속성 한 번에 리셋하기 (Resetting all property values)

CSS의 단축 속성인 all을 사용하면 (거의) 모든 CSS 속성에 상속 제어 값들을 한 방에 일괄 적용할 수 있습니다! 값으로는 inherit, initial, revert, revert-layer, unset 중 하나를 사용할 수 있어요. 이건 기존에 적용되었던 지저분한 스타일들을 싹 다 취소하고, 새롭게 스타일링을 시작하기 위한 아주 편리하고 깨끗한 도화지를 만드는 방법입니다.

아래 예제에는 두 개의 인용구(<blockquote>)가 있습니다. 첫 번째는 <blockquote> 자체에 스타일이 적용되어 있습니다. 두 번째는 fix-this라는 클래스를 통해 all 속성을 unset으로 설정해 두었어요. 그 결과 기존의 오렌지 배경이나 파란색 테두리가 싹 날아간 것을 볼 수 있습니다.

<blockquote>
  <p>This blockquote is styled</p>
</blockquote>

<blockquote class="fix-this">
  <p>This blockquote is not styled</p>
</blockquote>
blockquote {
  background-color: orange;
  border: 2px solid blue;
}

.fix-this {
  all: unset;
}

💡 강사의 실무 팁!
all: unset;은 실무에서 기본 버튼 스타일이나 기본 input 스타일을 초기화할 때 정말정말 많이 씁니다! 브라우저가 기본적으로 제공하는 못생긴 회색 버튼의 배경색, 테두리, 아웃라인 등을 하나하나 border: none; background: none; 치는 것보다 button { all: unset; } 한 줄이면 완전히 깨끗한 텍스트 상태로 돌아가기 때문에 그 위에 내 마음대로 스타일을 입히기가 훨씬 수월해집니다.


캐스케이드 완벽 이해하기 (Understanding the cascade)

이제 우리는 저 깊숙이 중첩된 문단이 왜 body 태그와 똑같은 색상을 가지게 되는지 '상속'을 통해 알게 되었습니다. 이제부터는 두 개 이상의 스타일 블록이 "서로 다른 값을 가지고 같은 요소를 꾸미려 할 때" 브라우저가 어떻게 승자를 결정하는지(캐스케이드 알고리즘)를 낱낱이 파헤쳐 보겠습니다.

승자를 결정할 때는 3가지 요소를 고려해야 하며, 아래 목록은 중요도가 낮은 것부터 높은 순서로 정렬되어 있습니다. 즉, 아래 번호가 더 나중일수록 앞의 규칙을 덮어씁니다:

  1. 소스 순서 (Source order)
  2. 명시도 (Specificity)
  3. 중요도 (Importance)

브라우저가 정확히 어떤 CSS를 적용할지 알아내기 위해 이 요소들을 어떻게 사용하는지 보겠습니다.


소스 순서 (Source order)

이미 소스 순서가 캐스케이드에서 어떤 역할을 하는지 확인했었죠. 명시도 점수(가중치)가 완전히 똑같은 규칙이 여러 개 있다면, CSS 파일 안에서 등장 순서대로 속성 및 속성 값이 덮어써집니다.

이건 명시도가 같을 때만 작동하는 타이 브레이커(동점자 판별기) 같은 겁니다. 자, 그럼 그 점수 자체인 명시도를 알아보러 가볼까요?


명시도 (Specificity)

"아니 분명히 이 코드가 파일 맨 아래에 있는데 왜 적용이 안 되지?!" 하는 상황을 자주 겪으실 겁니다. 앞서 작성된 코드인데도 불구하고 나중에 작성된 코드를 무시하고 적용된다면, 그 이유는 바로 앞의 규칙이 더 높은 명시도(Higher specificity)를 가지고 있기 때문입니다.

앞서 본 것처럼 클래스(.) 선택자는 일반 태그 이름 선택자보다 가중치가 큽니다.

여기서 한 가지 알아두셔야 할 점은, 덮어쓰기가 일어날 때 '규칙 전체'가 통째로 무효화되는 것은 아니라는 점입니다. 여러 곳에서 중복으로 선언된 특정 '속성(property)'만 덮어써집니다.

이런 성질을 이용하면 CSS의 코드 중복을 획기적으로 줄일 수 있습니다. 아주 흔한 실무 패턴이죠. 일반적인 요소에 기본적인 공통 스타일을 쫙 깔아두고, 약간 다른 형태가 필요할 때만 클래스를 추가해서 '변경할 속성만' 명시도로 덮어쓰는 겁니다.

아래 스타일시트에서 <h2> 태그에 대한 공통 스타일을 정의해두고, 특정 클래스(.small, .bright)를 가진 녀석들만 일부 속성을 변경하도록 설정했습니다.

<h2>Heading with no class</h2>
<h2 class="small">Heading with class of small</h2>
<h2 class="bright">Heading with class of bright</h2>
h2 {
  font-size: 2em;
  color: black;
  font-family: "Georgia", serif;
}

.small {
  font-size: 1em;
}

.bright {
  color: rebeccapurple;
}

자, 이제 브라우저가 명시도 점수를 계산하는 방식을 살펴봅시다. 우리는 요소(태그) 선택자가 점수가 낮고 클래스 선택자가 이길 수 있다는 걸 알았습니다. 본질적으로 브라우저는 다양한 선택자 종류별로 포인트(점수)를 부여하고, 이를 다 더해서 누가 더 높은지 겨루게 합니다.

선택자의 명시도 점수는 3개의 열(column)을 가진 숫자로 계산되며, 이해하기 쉽게 백의 자리, 십의 자리, 일의 자리라고 생각하셔도 무방합니다.

  • ID (백의 자리): 전체 선택자 안에 들어있는 ID 선택자(#) 1개당 1점(100점)을 부여합니다.
  • 클래스 (십의 자리): 전체 선택자 안에 들어있는 클래스(.), 속성([type="text"] 등), 가상 클래스(:hover 등) 1개당 1점(10점)을 부여합니다.
  • 요소 (일의 자리): 전체 선택자 안에 들어있는 태그(요소) 이름(p, div 등) 또는 가상 요소(::before 등) 1개당 1점(1점)을 부여합니다.

📝 참고사항 (Note):
전체 선택자(*), 결합자(+, >, ~, 공백), 그리고 명시도를 무시하는 특별한 가상 클래스인 :where()는 명시도 점수에 아무런 영향도(0점) 주지 않습니다.

아래 표를 보면서 감을 잡아보세요. 직접 계산해 보고, MDN의 선택자 레퍼런스를 참고하시면 좋습니다.

선택자ID 수 (백)클래스류 수 (십)요소 수 (일)총 명시도 점수
h10010-0-1
h1 + p::first-letter0030-0-3
li > a[href*="en-US"] > .inline-warning0220-2-2
#identifier1001-0-0

심층 명시도 예제 파헤치기 (In-depth specificity example)

조금 복잡한 예제로 확실히 다져볼까요? 코드를 보면서 아래 설명을 차근차근 따라와 보세요.

<div class="container" id="outer">
  <div class="container" id="inner">
    <ul>
      <li class="nav"><a href="#">One</a></li>
      <li class="nav"><a href="#">Two</a></li>
    </ul>
  </div>
</div>
/* 1. 명시도: 1-0-1 */
#outer a {
  background-color: red;
}

/* 2. 명시도: 2-0-1 */
#outer #inner a {
  background-color: blue;
}

/* 3. 명시도: 1-0-4 */
#outer div ul li a {
  color: yellow;
}

/* 4. 명시도: 1-1-3 */
#outer div ul .nav a {
  color: white;
}

/* 5. 명시도: 0-2-4 */
div div li:nth-child(2) a:hover {
  border: 10px solid black;
}

/* 6. 명시도: 0-2-3 */
div li:nth-child(2) a:hover {
  border: 10px dashed black;
}

/* 7. 명시도: 0-3-3 */
div div .nav:nth-child(2) a:hover {
  border: 10px double black;
}

a {
  display: inline-block;
  line-height: 40px;
  font-size: 20px;
  text-decoration: none;
  text-align: center;
  width: 200px;
  margin-bottom: 10px;
}

ul {
  padding: 0;
}

li {
  list-style-type: none;
}

무슨 일이 벌어지고 있는지 하나씩 뜯어봅시다. (위 코드의 1번부터 7번 규칙까지만 보면 됩니다. 각 규칙 위에 명시도를 주석으로 달아두었어요!)

  • 1번과 2번 규칙은 링크의 background-color를 두고 싸우고 있습니다. 2번 규칙이 이겨서 파란색(blue)이 되었습니다. 왜냐하면 2번 선택자에는 ID가 두 개나 들어있어서 명시도가 2-0-1 이고, 1번은 1-0-1 이기 때문입니다.
  • 3번과 4번 규칙은 글자 색상(color)을 두고 싸웁니다. 4번이 이겨서 하얀색(white)이 됩니다. 4번은 요소(태그) 개수는 하나 적지만 대신 가중치가 더 높은 클래스(.nav)를 가지고 있기 때문입니다. 승리한 명시도는 1-1-31-0-4 입니다. (십의 자리가 1이므로 0인 3번을 압살합니다)
  • 5번, 6번, 7번 규칙은 마우스를 올렸을 때(:hover) 테두리(border)를 두고 삼파전을 벌입니다. 6번은 5번보다 요소 개수가 하나 적어서(0-2-3 vs 0-2-4) 일단 탈락입니다. 하지만 7번 규칙이 5번과 요소 개수는 비슷하지만 요소 하나를 클래스(.nav)로 바꿨기 때문에, 십의 자리가 하나 더 높습니다(0-3-3 vs 0-2-4). 결국 7번이 최종 승리하여 겹줄(double) 테두리가 그려집니다.

💡 강사의 실무 팁! (명시도의 함정)
여기서 주의할 점! 각 자릿수는 상위 자릿수를 절대 이길 수 없습니다. 즉, 클래스 선택자를 100만 개 이어 붙인다고 하더라도, 단 1개의 ID 선택자를 이길 수 없다는 뜻입니다. (1-0-0 이 0-9999-0 을 이깁니다!) 그래서 점수 계산할 때는 항상 가장 앞자리(ID)부터 비교하고, 동점일 때만 다음 자리로 넘어가는 식으로 평가해야 합니다.

ID vs 클래스 (IDs versus classes)

ID 선택자는 이렇게 어마무시한 명시도를 가지고 있습니다. 클래스나 타입 선택자를 다 무시하고 군림하죠. 게다가 ID는 한 페이지에 단 하나만 존재할 수 있다는 제약까지 있습니다.
이러한 강한 명시도와 재사용 불가라는 특성 때문에, 실무에서는 스타일링을 목적으로 ID를 사용하는 것을 극도로 기피합니다. 무조건 클래스(Class)를 우선적으로 사용하세요!

만약 HTML을 직접 수정할 수 없는 환경이어서 어쩔 수 없이 ID를 잡고 스타일링해야 한다면, 명시도 폭주를 막기 위해 #header 대신 속성 선택자 형태인 [id="header"]를 사용하는 것이 꿀팁입니다. 이러면 클래스와 똑같은 십의 자리(10점) 명시도만 가지게 되어 나중에 스타일을 덮어쓰기가 훨씬 수월해집니다.


인라인 스타일 (Inline styles)

HTML 태그 안에 직접 써넣는 style 속성 선언(인라인 스타일)은 명시도 따위를 전부 무시해버리고 무조건 최우선으로 적용됩니다. (예외: !important 제외)
인라인 스타일은 선택자가 없지만, 명시도로 굳이 치자면 1-0-0-0 이라고 볼 수 있습니다. ID가 수백 개 있어도 인라인 스타일 한 줄이면 다 씹어먹습니다. (물론 이 방식도 유지보수 최악이므로 지양해야 합니다!)


!important (절대 권력)

인라인 스타일마저 짓밟고 모든 캐스케이드 계산을 뒤엎을 수 있는 CSS계의 절대 마법 주문이 있습니다. 바로 !important 플래그입니다. 이것을 속성값 뒤에 붙이면 그 선언은 가장 강력한 명시도를 갖게 되며, 인라인 스타일조차 이겨버립니다.

⚠️ 강사의 강력한 경고! (Warning):
다른 사람이 짠 코드에서 !important를 발견하면 "아, 이거 제일 쎄게 먹이려고 썼구나" 하고 이해하시면 됩니다. 하지만 여러분은 절대로, 정말 어쩔 수 없는 최후의 수단이 아니라면 절대로 사용하지 마세요! > !important는 CSS의 자연스러운 흐름(Cascade)을 완전히 파괴합니다. 프로젝트가 커지면 나중에 "이거 왜 안 바뀌지?!" 하면서 !important를 이기기 위해 또 다른 !important를 남발하는 끔찍한 악순환에 빠지게 됩니다.

아래 예제에서 두 개의 문단을 보세요.

<p class="better">This is a paragraph.</p>
<p class="better" id="winning">One selector to rule them all!</p>
#winning {
  background-color: red;
  border: 1px solid black;
}

.better {
  background-color: gray;
  border: none !important;
}

p {
  background-color: blue;
  color: white;
  padding: 5px;
}

이 코드를 분석해 볼까요?
1. 3번째 규칙인 <p> 태그 선택자가 파란 배경에 흰 글씨, 패딩 5px을 주고 있습니다. 하지만 위쪽의 규칙들이 명시도가 더 높기 때문에 배경색(background-color)은 덮어씌워 집니다.
2. 첫 번째 <p>.better 클래스만 가지고 있어서 회색(gray) 배경이 됩니다.
3. 두 번째 <p>.better 클래스와 함께 winning이라는 ID까지 갖고 있습니다. ID 명시도가 더 높으니 배경색은 빨간색(red)이 되고, 테두리(border)도 1px 검은색으로 그려져야 맞습니다.
4. 하지만 두 번째 요소에 테두리(border)가 안 보입니다! 왜일까요? 바로 .better 클래스 안에 있는 border: none !important; 때문입니다. 비록 ID 선택자가 명시도는 높지만, !important 플래그가 붙어버렸기 때문에 이 선언이 모든 걸 무시하고 최종 승리한 것입니다.

📝 참고사항 (Note):
!important가 붙은 선언을 이길 수 있는 유일한 방법은, 명시도가 같거나 더 높은 다른 선택자에서 똑같이 !important를 선언하여 뒤에 배치하는 것뿐입니다. (진흙탕 싸움의 시작이죠)

실무에서 !important를 써야만 하는 아주 예외적인 상황은 딱 하나입니다. 제어 권한이 없는 서드파티 라이브러리나 CMS 툴에서 뱉어내는 인라인 스타일을 강제로 덮어써야 할 때입니다. 그 외에는 피하세요!


CSS 위치의 영향 (The effect of CSS location)

마지막으로, CSS 선언의 우선순위는 "그 스타일시트를 누가 만들었느냐"에 따라서도 달라진다는 점을 알아두셔야 합니다.

사용자(User)는 개발자가 만든 스타일을 덮어쓰기 위해 자신만의 커스텀 스타일시트를 브라우저에 설정할 수 있습니다. 예를 들어, 시각 장애가 있는 사용자가 글씨를 쉽게 읽기 위해 모든 웹페이지의 폰트 크기를 두 배로 강제 설정하는 경우가 여기에 해당합니다.

스타일을 덮어쓰는 순서 (Order of overriding declarations)

충돌하는 선언들은 아래의 순서대로 적용되며, 번호가 클수록(나중에 위치할수록) 이전의 것들을 덮어씁니다:

  1. User agent style sheets (브라우저 기본 스타일): 아무런 스타일도 주지 않았을 때 <div>는 블록이고 <span>은 인라인인 이유입니다.
  2. User style sheets의 일반 선언: 사용자가 브라우저 설정으로 추가한 스타일.
  3. Author style sheets의 일반 선언: 우리 같은 웹 개발자들이 CSS 파일에 작성한 코드!
  4. Author style sheets의 !important 선언: 개발자가 쓴 절대 마법.
  5. User style sheets의 !important 선언: 사용자가 쓴 절대 마법.
  6. User agent style sheets의 !important 선언: 브라우저 기본값의 절대 마법.

📝 참고사항 (Note):
!important가 붙으면 우선순위가 뒤집히는 것을 볼 수 있습니다. (원래는 개발자가 작성한 3번이 사용자의 2번을 이기지만, !important가 붙으면 사용자의 5번이 개발자의 4번을 이깁니다). 왜냐하면 사용자가 가독성이나 접근성 등의 절박한 이유로 웹페이지의 디자인을 억지로 바꿔야 할 권리를 보장해 줘야 하기 때문입니다.


요약 (Summary)

이 글의 내용을 대부분 이해하셨다면, 축하합니다! CSS가 동작하는 가장 근본적인 메커니즘을 깨우치신 겁니다.

만약 캐스케이드, 명시도, 상속이 완벽하게 이해되지 않더라도 너무 좌절하지 마세요! 이것들은 지금까지 배운 내용 중 가장 복잡하고, 현업에서 구르는 프로 개발자들도 가끔 헷갈려서 찾아보는 내용이니까요. 학습을 진행하시면서 틈틈이 이 글을 다시 읽어보며 곱씹어보는 것을 추천해 드립니다.

profile
프론트에_가까운_풀스택_개발자

0개의 댓글