div ~ div와 div:not(:first-of-type)는 요소를 선택하는 관점에서 보면 결과가 완전히 동일합니다. 두 선택자 모두 '형제 요소 중 첫 번째 div를 제외한 나머지 모든 div'를 가리키기 때문입니다.
두 선택자의 의미
- div ~ div (일반 형제 결합자): 같은 부모 아래에서 앞쪽에 div 형제가 하나라도 있는 모든 div를 선택합니다.
- div:not(:first-of-type): 형제 요소들 사이에서 첫 번째 div가 아닌 모든 div를 선택합니다.
실제 코드로 어떤 요소들이 매칭되는지 확인해 보겠습니다:
<section>
<div></div> <!-- div:first-child 또는 div:first-of-type -->
<div></div> <!-- div+div 또는 div~div 또는 div:nth-of-type(2) -->
<p></p>
<div></div> <!-- div+p+div 또는 div~div 또는 div:nth-of-type(3),
단, div+div는 해당되지 않음 -->
</section>
<section>
<h1></h1> <!-- h1:first-child -->
<div></div> <!-- div:first-of-type 또는 div:nth-child(2) -->
<div></div> <!-- div~div 또는 div:nth-of-type(2) 또는 div:nth-child(3) -->
</section>
첫 번째 section에서 세 번째 div처럼 앞에 p 같은 다른 요소가 있어도 div ~ div는 여전히 매칭됩니다. 일반 형제 결합자는 바로 인접한 형제가 아니라 '앞쪽에 존재하는' div 형제만 있으면 되기 때문입니다.
결정적인 차이점: 명시도(Specificity)
그렇다면 차이는 어디에 있을까요? 바로 CSS 우선순위에 있습니다. 두 선택자가 각각의 CSS 규칙으로 동일한 요소에 적용될 때, div:not(:first-of-type) 쪽이 더 높은 명시도를 가져 승리하게 됩니다.
div ~ div: 타입 선택자 2개 → 명시도 (0, 0, 2)div:not(:first-of-type): 타입 선택자 1개 +:not()안의 의사 클래스 1개 → 명시도 (0, 1, 1)
명시도 계산 시 :not() 자체는 점수에 포함되지 않지만, 괄호 안의 :first-of-type 의사 클래스는 정상적으로 계산됩니다. 따라서 스타일 충돌이 발생하면 div:not(:first-of-type)로 작성된 규칙이 우선 적용된다는 점을 기억해 두면 좋습니다.