안녕하세요! 프론트엔드 개발자 취업을 향해 한 걸음 더 나아가고 계시네요! 선택자(Selector)는 CSS의 가장 기본이 되면서도, 실무에서 복잡한 UI를 다룰 때 성능과 직결되는 아주 중요한 뼈대입니다.
단순히 '클래스 이름으로 스타일을 준다'를 넘어서, 브라우저가 어떻게 선택자들의 구조를 파악하고 요소를 찾아가는지 그 원리를 아는 것은 훌륭한 프론트엔드 개발자가 되기 위한 필수 과정이죠. 공식 문서의 내용을 하나도 빠짐없이, 제 실무 팁과 함께 구어체로 알기 쉽게 번역해 드리겠습니다. 시작해 볼까요?
CSS 선택자는 트리 구조 내에서 요소(element) 또는 요소들의 특정 패턴을 나타냅니다. "선택자(selector)"라는 용어는 단일 선택자 (simple selector), 복합 선택자 (compound selector), 또는 복잡한 선택자 (complex selector)를 모두 지칭할 수 있습니다. 이러한 선택자들이 :has() 가상 클래스(pseudo-class)의 매개변수로 포함될 때는, 하나 이상의 앵커(기준) 요소에 상대적인 요소를 나타내는 상대 선택자 (relative selectors)라고 부릅니다.
이러한 선택자들은 쉼표로 구분된 선택자 목록 (selector list)으로 결합될 수 있습니다. 관대하지 않은 선택자(non-forgiving selector) 목록에서 선택자가 하나라도 유효하지 않으면(invalid), 전체 선택자 목록이 무효화됩니다.
💡 강사의 실무 팁 1: 브라우저는 선택자를 어떻게 읽을까요?
여러분, 브라우저가 CSS 선택자를 읽을 때는 우리가 글을 읽는 방향과 반대로 '오른쪽에서 왼쪽으로' 읽는다는 사실 알고 계셨나요?
나중에 복잡한 선택자(.container .list .item a)를 작성하실 때, 브라우저는 제일 끝에 있는a태그를 먼저 싹 다 찾은 다음 그 부모들을 거슬러 올라가며 조건을 맞춘답니다. 그래서 오늘 배우실 선택자의 '구조'를 잘 이해하셔야 렌더링 성능이 좋은 효율적인 CSS를 짤 수 있어요!
단일 선택자(simple selector)는 다른 선택자 구성 요소나 결합자(combinator)와 결합되지 않고 단독으로 사용되는, 단일 구성 요소를 가진 선택자입니다. 단일 타입(태그) 선택자, 속성 선택자, 또는 가상 클래스 등이 여기에 속합니다. 어떤 요소가 단일 선택자와 일치(match)한다고 하는 것은, 해당 단일 선택자가 그 요소를 정확하게 설명하고 있음을 의미합니다. 단 하나의 기본 선택자(basic selector), 속성 선택자(attribute selector), 가상 클래스(pseudo-class), 또는 가상 요소(pseudo-element) 선택자를 포함하는 모든 선택자는 단일 선택자입니다.
#myId {
}
[pattern*="\d"] {
}
복합 선택자(compound selector)는 결합자 (combinator)로 분리되지 않은 단일 선택자(simple selectors)들의 연속된 나열입니다. 복합 선택자는 단일 요소에 대해 여러 조건이 동시에 만족해야 함을 나타냅니다. 어떤 요소가 복합 선택자와 일치한다고 하는 것은, 해당 요소가 복합 선택자를 구성하는 모든 단일 선택자와 동시에 일치할 때를 말합니다.
a#selected {
}
[type="checkbox"]:checked:focus {
}
복합 선택자에서 타입 선택자 (type selector)나 전체 선택자 (universal selector)는 반드시 선택자 나열의 가장 맨 처음에 와야 합니다. 나열 내에서 타입 선택자나 전체 선택자는 단 하나만 허용됩니다. 공백(whitespace)은 자손 결합자 (descendant combinator)를 의미하기 때문에, 복합 선택자를 구성하는 단일 선택자들 사이에는 절대로 공백이 허용되지 않습니다.
💡 강사의 실무 팁 2: 띄어쓰기의 마법 (가장 흔한 실수!)
이제 막 프론트엔드를 공부하시는 분들이 가장 많이 하는 실수가 바로 이 '띄어쓰기'입니다.
a#selected(띄어쓰기 없음) ➡️<a id="selected">인 바로 그 요소를 찾습니다. (복합 선택자)
a #selected(띄어쓰기 있음) ➡️<a>태그 안에 자식으로 들어있는id="selected"요소를 찾습니다. (복잡한 선택자)
띄어쓰기 하나로 의미가 완전히 달라지니 실무 코드를 작성하실 때 꼭 주의하세요!
복잡한 선택자(complex selector)는 하나 이상의 단일 선택자 및/또는 복합 선택자들이 공백(자손 결합자)을 포함한 결합자(combinators)에 의해 분리된 연속된 나열입니다.
복잡한 선택자는 여러 요소들의 집합에 대해 여러 조건이 동시에 만족해야 함을 나타냅니다.
a#selected > .icon {
}
.box h2 + p {
}
선택자는 오른쪽에서 왼쪽으로 읽을 수 있습니다. 예를 들어, a#selected > .icon은 selected라는 id를 가진 <a> 요소의 직계 자식(direct children)이면서 icon이라는 클래스를 가진 모든 요소와 일치합니다. .box h2 + p 선택자는 box라는 클래스를 가진 요소의 자손인 <h2> 요소들 바로 다음에 오는(형제 관계) 첫 번째 <p> 요소들과 일치합니다.
선택자 목록(selector list)은 단일, 복합, 그리고/또는 복잡한 선택자들을 쉼표(,)로 구분한 목록입니다. 어떤 요소가 이 선택자 목록에 있는 선택자 중 어느 하나(최소 하나 이상)와 일치할 때, 해당 요소는 이 선택자 목록과 일치한다고 합니다.
#main,
article.heading {
}
만약 관대하지 않은 선택자 (non-forgiving selector) 목록에서 선택자가 단 하나라도 문법에 맞지 않아 유효하지 않다면(invalid), 전체 선택자 목록이 완전히 무효화됩니다.
#main,
:bad-pseudoclass,
.validClass {
/* `:bad-pseudoclass`가 유효하지 않으므로, 이 스타일 블록 전체가 무효화됩니다. */
}
:is()와 :where() 가상 클래스를 사용하면 관대한 선택자 목록 (forgiving selector lists)을 구성할 수 있습니다.
💡 강사의 실무 팁 3: 관대한 선택자(Forgiving Selector) 활용하기
위의 예제처럼 콤마로 연결된 선택자들 중 오타가 나서 하나라도 망가지면 멀쩡한.validClass스타일까지 다 깨져버립니다.
하지만:is(#main, :bad-pseudoclass, .validClass) { ... }처럼:is()로 감싸주면 브라우저가 "음, 저 중간 건 에러지만 나머지 유효한 것들은 스타일 적용해 줄게!" 하고 관대하게 처리해 줍니다. 정말 유용하죠?
상대 선택자(relative selector)는 결합자가 앞에 오는 하나 이상의 앵커(기준이 되는) 요소에 상대적인 요소를 나타내는 선택자입니다. 명시적인 결합자(예: >, +, ~)로 시작하지 않는 상대 선택자는 암시적으로 자손 결합자(descendant combinator)를 가집니다.
상대 선택자는 쉼표로 구분하는 선택자 목록 안에서는 단독으로 쓰일 수 없습니다. 대신, :has() 가상 클래스와 같은 특정 문맥 내에서 허용됩니다.
:has(+ div#topic > #reference) {
}
:has சிதம்ப (> .icon) {
}
dt:has(+ img) ~ dd {
}
💡 강사의 실무 팁 4: 혁명적인
:has()선택자
:has()는 CSS 역사상 가장 위대한 업데이트 중 하나로 불리는 '부모 선택자'입니다.
기존 CSS로는 자식을 기준으로 부모의 스타일을 바꿀 방법이 없었는데, 이제div:has(> img)를 쓰면 "자식으로 img 태그를 가진 div"만 콕 집어서 스타일을 줄 수 있게 되었죠. 상대 선택자의 개념은 이:has()를 완벽하게 활용하기 위한 필수 지식입니다!
| Specification (명세) |
|---|
| Selectors Level 4 |
Document.querySelector()Document.querySelectorAll()MDN 향상에 도움 주기 (Help improve MDN)
어떠셨나요? 단순해 보이는 CSS 선택자에도 이렇게 체계적인 구조가 있다는 것을 알면, 개발자 도구를 켜서 DOM 트리를 분석하고 스타일 버그를 잡는 데 훨씬 큰 자신감이 생기실 거예요.
현재 React나 Next.js를 공부하고 계실 텐데, 아무리 좋은 프레임워크나 라이브러리를 쓰더라도 결국 브라우저가 화면을 그리는 기본은 이런 순수 CSS의 원리를 따르게 됩니다. 오늘 배운 내용을 잘 기억해 두셨다가 실무에서, 그리고 포트폴리오를 다듬을 때 유용하게 써먹어 보세요! 더 궁금한 점이 생기면 언제든 질문 환영합니다.