본문 바로가기
language/css

🎯 CSS 선택자 완전 정리 – 기본, 결합, 속성, 가상 클래스/요소

by 죄니안죄니 2025. 5. 11.

🎯 CSS 선택자 완전 정리 – 기본, 결합, 속성, 가상 클래스/요소

CSS에서 선택자(selector)는 스타일을 적용할 HTML 요소를 지정하는 가장 기본적인 개념입니다. 하지만 실제로는 다양한 형태의 선택자가 있으며, 이를 정확히 이해하면 유지보수와 확장성이 훨씬 쉬워집니다.


1️⃣ 기본 선택자

  • * (전체 선택자): 문서의 모든 요소를 선택
    * { margin: 0; }
  • 태그명 선택자: 특정 태그를 선택
    p { font-size: 16px; }
  • .클래스 선택자: 특정 클래스가 지정된 요소
    .title { color: red; }
  • #아이디 선택자: 특정 id가 지정된 요소
    #main { background: #eee; }

✔️ 주의: id는 페이지 내에서 유일해야 하며, 너무 자주 사용하는 것은 지양하는 것이 좋습니다.


2️⃣ 결합 선택자

<div class="button">
  <span class="primary">텍스트</span>
  <div>
    <span class="primary">텍스트2</span>
  </div>
</div>
<div class="sibling">
</div>
<div class="sibling2">
</div>
<div class="sibling3">
</div>
  • 후손 선택자: A B → A 내부의 B (후손 모두)
    div p { ... }
  • 자식 선택자: A > B → A의 자식인 B (직속 자식만)
    ul > li { ... }
  • 인접 형제 선택자: A + B → A 바로 뒤의 B
    h1 + p { ... }
  • 일반 형제 선택자: A ~ B → A 뒤에 오는 모든 B 형제
    h1 ~ p { ... }

실전 팁: 자식 선택자는 구조가 명확할 때 사용하고, 후손 선택자는 성능에 영향을 줄 수 있으니 최소화하는 것이 좋습니다.


3️⃣ 속성 선택자

  • [type] → 속성이 존재하는 요소
    input[type]
  • [type="text"] → 정확히 일치하는 값
    input[type="text"]
  • [href^="https"] → 특정 값으로 시작
    a[href^="https"]
  • [href$=".pdf"] → 특정 값으로 끝남
    a[href$=".pdf"]
  • [data-role*="admin"] → 특정 문자열을 포함
    div[data-role*="admin"]

활용 팁: data-* 속성과 조합하면 JavaScript 없이도 유연한 스타일링 가능!

✅ CSS 예시

[data-status="active"] {
  background-color: green;
}

[data-role*="admin"] {
  font-weight: bold;
}
[attr="val"] {...}	/* 정확히 일치 */
[attr^="val"] {...}	/* val로 시작 */
[attr$="val"] {...}	/* val로 끝남 */
[attr*="val"] {...}	/* val을 포함 */

✅ HTML 예시

<div data-role="admin">👑 관리자</div>
<div data-role="admin-user">👤 관리자-사용자</div>
<div data-role="user-admin-view">👁 사용자-관리자 보기</div>
<div data-role="user">🙋 일반 사용자</div>
<button id="highlightBtn" class="btn">관리자 관련 요소 강조</button>
<button id="undoHighlightBtn" class="btn">초기화</button>

<script>
document.getElementById("highlightBtn").addEventListener("click", () => {
  // 'admin'이라는 단어가 포함된 data-role을 가진 요소들을 모두 선택
  const adminElements = document.querySelectorAll('[data-role*="admin"]');
  
  // 각 요소에 스타일을 적용
  adminElements.forEach(el => {
    el.style.backgroundColor = "gold";
    el.style.border = "2px solid orange";
    el.style.padding = "0 5px";
  });
});
document.getElementById("undoHighlightBtn").addEventListener("click", () => {
  // 'admin'이라는 단어가 포함된 data-role을 가진 요소들을 모두 선택
  const adminElements = document.querySelectorAll('[data-role*="admin"]');
  
  // 각 요소에 스타일을 적용
  adminElements.forEach(el => {
    el.style.backgroundColor = "";
    el.style.border = "";
    el.style.padding = "";
  });
});

</script>
<style>
.btn {
	border: 1px solid black;
    display:border-box;
    background: gray;
}
</style>

 

👑 관리자
👤 관리자-사용자
👁 사용자-관리자 보기
🙋 일반 사용자

실전 팁:  data-* 속성에는 단일값 말고 공백으로 구분 된 list처럼 다중값을 넣을 수도 있지만, 실제 현업에서는 data-* 속성에 단일 값을 넣는 경우가 훨씬 많습니다_ 상태, ID, 타입, 모드 등 조건을 분기하거나, 스타일링, 액션 제어가 대부분입니다. (9할)
다중 값을 쓰는 경우는 주로  특수한 상황이나 의도적으로 구조회된 데이터를 넣을 때만 사용합니다_ 사용자 역할, 태그처럼 한 요소가 여러 개의 속성을 가질 때 선택적으로 사용합니다. (1할)

 

참고:  data-* 속성 A to Z 보러가기 👉 https://blog.naver.com/devops_log/223867090210


4️⃣ 가상 클래스 선택자 (pseudo-classes)

  • :hover → 마우스 올렸을 때
  • :focus → 포커스 됐을 때
  • :nth-child(n) → n번째 자식 요소
    li:nth-child(2n) (짝수)
  • :first-child, :last-child → 첫/마지막 자식
  • :not(selector) → 제외할 때
    div:not(.active)

실전 팁: :hover:focus는 버튼, 링크 등 인터랙션 요소에 꼭 사용하세요.

✅ CSS 예시

/* 1. 버튼에 마우스를 올리면 색이 바뀜 */
button:hover {
  background-color: #007bff;
  color: white;
}

/* 2. 누르는 순간 눌리는 느낌 구현 */
button:active {
  transform: scale(0.98);
}

/* 3. 클릭 또는 탭키로 포커스된 input 강조 */
input:focus {
  outline: 2px solid #4CAF50;
}

/* 4. 체크된 항목에 따라 라벨 스타일 변경 */
input[type="checkbox"]:checked + label {
  font-weight: bold;
  color: green;
}

/* 5. 비활성화된 버튼에 스타일 지정 */
button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* 6. 리스트 항목 중 첫 번째, 마지막, 특정 순서 항목 지정 */
ul li:first-child {
  font-weight: bold;
}

ul li:nth-child(2) {
  color: red;
}

ul li:last-child {
  border-bottom: none;
}

/* 7. 제외조건 .primary가 아닌 버튼만 회색 처리 */
button:not(.primary) {
  background-color: #eee;
}

/*  8. :nth-of-type(n) – 특정 태그 순서 */
p:nth-of-type(2) {
  color: blue;
}

/* 9. :empty – 자식이 없는 요소 숨김 처리 */
div:empty {
  display: none;
}

/* 10. :root – CSS 변수 선언 시, 전역 변수의 시작 지점 (HTML의 최상단) */
:root {
  --main-color: #3f51b5;
}

button {
  background-color: var(--main-color);
}

 

✅ 보너스: Form 관련 자주 쓰는 가상 클래스

선택자ㅌ 선택자설명
:focus 포커스된 input 요소
:valid / :invalid 유효성 검사 통과 여부
:required / :optional 필수 입력 여부
:checked 체크박스 / 라디오 버튼 체크 여부
input:invalid {
  border-color: red;
}

5️⃣ 가상 요소 선택자 (pseudo-elements)

  • ::before, ::after → 요소 앞/뒤에 콘텐츠 삽입
    p::before { content: "👉"; }
  • ::first-letter → 첫 글자
    p::first-letter { font-size: 2em; }
  • ::selection → 텍스트 선택 영역 스타일링
    ::selection { background: yellow; }

 

✅ 사용예시

<p class="test">공지사항 <hr/> 공지사항 내용입니다. </p>
<input class="test" type="text" placeholder="이름을 입력하세요" />
<ul>
  <li class="test">항목 1</li>
  <li class="test">항목 2</li>
</ul>

<!-- 렌더링 시: 📌 공지사항 ... -->
<style>
/* 1. 앞뒤 콘텐츠 삽입 */
p.test::before {
    content: "📌 ";
}

p.test::after {
    content: " 끝!";
    color: red;
}

/* 2. 첫 글자에 스타일 (잡지) */
p.test::first-letter {
    font-size: 2em;
    color: darkblue;
    font-weight: bold;
}
/* 3. 첫 줄에만 스타일 */
p.test::first-line {
  color: green;
  font-variant: small-caps;
}

/* 4. ::placeholder 텍스트 스타일 */
input.test::placeholder {
  color: #888;
  font-style: italic;
}

/* 5. 텍스트 드래그했을 때 스타일 */
p.test::selection {
    background: yellow;
    color: black;
}

/* 6. ul, ol의 불릿(•) 또는 번호에 스타일 */
li.test::marker {
  color: red;
  font-size: 1.5em;
}

</style>

공지사항


공지사항 내용입니다.

 

  • 항목 1
  • 항목 2

 

주의: ::before ::after 사용 시 content 속성은 필수입니다. 없으면 아무것도 안 보임.

 

✅ 실무에서 가장 많이 쓰는 조합

✅ 버튼 또는 태그 꾸미기

.button::after {
  content: '→';
  margin-left: 8px;
  color: #333;
}

<button class="button">더보기</button>

✅ 리스트 항목 강조 (커스텀 불릿)

li::before {
  content: "✔ ";
  color: green;
}
 

✅ 팝업/모달 뒤에 배경 흐림 처리

.modal::before {
  content: "";
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.5);
  z-index: -1;
}

🔍 마무리 – 선택자 우선순위 요약

선택자 우선순위 점수 설명
인라인 스타일 1000 요소에 style="color:red"처럼 직접 지정한 경우
#id 100 #header, #main처럼 id지정한 경우
.class, :pseudo-class, [attribute] 10 클래스, 의사 클래스(:hover), 속성 선택자
태그, ::pseudo-element 1 HTML 태그 이름 자체를 선택하거나 의사 요소 사용
* (전체), 결합자 0 전체 선택자나 단순 구조 결합자는 점수 없음

예시 비교

<div id="myDiv" class="box" style="color: red;">텍스트</div>

<style>
    div { color: blue; }              /* 점수: 1 */
    .box { color: green; }            /* 점수: 10 */
    #myDiv { color: orange; }         /* 점수: 100 */
</style>
이럴 경우 실제 적용되는 색상은? 👉 style="color:red"1000점으로 가장 높아서 빨강(red)이 최종 적용됩니다.

 

  • 숫자가 높을수록 우선 적용됨
  • !important점수와 상관없이 우선순위를 무시하고 최상위로 적용됨
  • 점수가 같다면 나중에 선언된 것이 우선
  • 선택자의 종류를 알면 상황에 맞는 정확한 스타일링이 가능해집니다. 가독성과 유지보수를 위해 너무 복잡한 선택자 조합은 피하는 것이 좋습니다.

예제 1: 지나치게 구체적인 조합

html body div.container ul > li.active span.label {
  color: red;
}

점수 계산:

  • 태그 선택자: html, body, div, ul, li, span6 × 1 = 6
  • 클래스: .container, .active, .label3 × 10 = 30
  • 결합자(>, 공백)점수 없음

총점: 36

➡️ 너무 많은 구조에 의존해 스타일을 지정하고 있음
➡️ HTML 구조가 바뀌면 깨지기 쉬움


예제 2: ID클래스, 속성 혼합

 
#header .menu li.item[type="button"]:hover::after {
  content: "!";
}

점수 계산:

  • #header100
  • .menu, .item, :hover3 × 10 = 30
  • [type="button"]10
  • li1
  • ::after1

총점: 142

➡️ 점수가 너무 높아서 다른 스타일로 덮기가 매우 어려움
➡️ 이런 스타일은 나중에 오버라이드하려면 **!important**까지 써야 하는 경우도 생김


🧨 이런 복잡한 선택자의 단점

  1. 재사용이 어려움
    HTML 구조가 바뀌면 선택자가 무용지물됨
  2. 유지보수가 어려움
    다른 사람이 스타일 구조를 파악하기 어려움
  3. 우선순위 전쟁 발생
    나중에 덮어쓰려면 강한 선택자나 !important 사용이 필요해짐

추천 방식

  • .button.primary 처럼 클래스 조합 중심 (<div class="button primary">버튼</div> 두 클래스 모두 가진 요소)
  • ID최소 사용
  • 너무 깊은 구조 의존(X) → .menu-item.active처럼 간결하게

 

 

📌 다음 글 예고

다음 글: “CSS 박스 모델(Box Model) 완전 이해 – margin, border, padding, content” 에서는 실제 브라우저가 요소의 크기를 계산하는 방식을 시각적으로 설명합니다.

댓글