도입: CSS를 스타일링 말고 다른 용도로 쓸 수 있다는 발상
CSS라고 하면 보통 뭐가 떠오르세요? 색깔 바꾸고, 여백 주고, 반응형 디자인 만들고. 그런 "꾸미는 언어"라고 생각하잖아요. 그런데 CSS 셀렉터를 조금만 다르게 보면 이게 사실 굉장히 강력한 "DOM 쿼리 언어"라는 걸 알 수 있어요. DOM이 뭐냐면, 브라우저가 HTML을 해석해서 메모리에 만들어둔 문서 구조예요. 트리 모양으로 되어 있고, 자바스크립트로 조작할 수 있는 그 대상이요.
평소에 document.querySelectorAll('div.card') 같은 코드를 써본 적 있을 거예요. 이게 바로 CSS 셀렉터로 DOM을 쿼리하는 거예요. 이 블로그 글이 말하고자 하는 건, 이 방식을 단순 요소 선택을 넘어서 훨씬 더 정교한 "문서 내 정보 검색" 도구로 쓸 수 있다는 거예요. SQL이 데이터베이스를 쿼리하는 언어라면, CSS 셀렉터는 DOM을 쿼리하는 언어라는 관점이에요.
핵심 내용: 생각보다 강력한 CSS 셀렉터
대부분의 개발자는 CSS 셀렉터를 기본적인 것만 써요. 클래스(.btn), 아이디(#header), 자식(ul > li) 정도죠. 근데 CSS 셀렉터는 Level 4까지 오면서 엄청 풍부해졌어요. 예를 들어 :has() 셀렉터는 2023년에 모든 주요 브라우저에서 지원되기 시작했는데, 이게 진짜 판도를 바꿨어요.
:has()가 뭐냐면, "자식 중에 특정 요소를 가진 부모"를 선택할 수 있게 해주는 셀렉터예요. 예전엔 CSS로 부모 선택이 불가능했거든요. 이제 article:has(img)라고 쓰면 "이미지를 포함한 article 태그"만 뽑아낼 수 있어요. 이걸 jQuery나 자바스크립트로 하려면 루프를 돌면서 체크해야 했던 건데 이제 한 줄이면 돼요.
여기에 :not(), :is(), :where(), 속성 셀렉터([data-role="admin"]), 부분 매칭([href^="https://"], [class="card"]) 같은 걸 조합하면 놀라울 정도로 정교한 쿼리를 만들 수 있어요. 예를 들어 "외부 링크인데 이미지를 포함하고, nofollow 속성이 없는 a 태그"를 찾는다면 a[href^="http"]:not([href="mydomain.com"]):has(img):not([rel~="nofollow"]) 같은 식으로 한 줄에 표현이 되거든요.
이게 왜 강력하냐면, 브라우저 개발자 도구의 콘솔에서 $$('selector')를 치면 바로 결과를 확인할 수 있어요. 웹 스크래핑이나 QA 작업, 또는 웹사이트 분석을 할 때 이 방법이 XPath나 복잡한 자바스크립트 루프보다 훨씬 간결하고 빨라요.
실제로 어디에 쓰냐면
실무에서 이게 빛을 발하는 상황이 몇 가지 있어요. 첫째는 웹 스크래핑이에요. Puppeteer, Playwright, Cheerio 같은 도구들은 모두 CSS 셀렉터를 지원하거든요. 복잡한 페이지에서 원하는 정보만 뽑아낼 때 정교한 셀렉터 하나가 수십 줄의 코드를 대체해요.
둘째는 접근성 감사예요. "alt 속성 없는 img 태그"는 img:not([alt]), "label 없이 동동 떠있는 input"은 input:not([id]):not([aria-label]) 같은 식으로 금방 찾을 수 있죠. Lighthouse나 axe 같은 도구 내부에서도 비슷한 원리를 쓰고 있어요.
셋째는 테스트 자동화예요. E2E 테스트를 짤 때 페이지의 특정 상태(예: 체크박스가 선택된 카드)를 찾아야 하는 경우가 많은데, :checked, :disabled, :valid 같은 상태 기반 의사 클래스를 활용하면 테스트 코드가 훨씬 선언적이 돼요.
넷째는 브라우저 콘솔에서 일회성 탐색이에요. 개발하면서 "이 페이지에 이상한 z-index 가진 요소가 몇 개 있지?"를 빠르게 확인하고 싶을 때, 콘솔에서 셀렉터 한 줄로 끝낼 수 있어요.
업계 맥락
CSS를 쿼리 언어로 보는 관점은 새롭진 않아요. jQuery가 2006년에 나오면서 $('selector')로 DOM을 다루는 방식을 대중화했고, 그 이후 document.querySelector가 표준화되면서 이 패턴이 웹 개발의 기본이 됐어요. 흥미로운 건 CSS 셀렉터가 HTML 파싱 라이브러리들에도 스며들었다는 점이에요. Python의 BeautifulSoup, Go의 goquery, Rust의 scraper 같은 라이브러리들이 다 CSS 셀렉터 문법을 지원해요.
경쟁 개념으로는 XPath가 있는데, XPath는 더 강력하지만 문법이 복잡하고 가독성이 떨어져요. 반면 CSS 셀렉터는 프론트엔드 개발자들이 이미 익숙한 문법이라 진입장벽이 거의 없죠. 최근에는 :has() 같은 새 기능이 추가되면서 CSS 셀렉터가 XPath로만 가능했던 영역까지 커버하기 시작했어요.
한국 개발자에게
프론트엔드 개발자라면 CSS 셀렉터를 스타일링 도구로만 보지 말고, DOM 탐색 도구로 한 단계 깊게 공부해보길 권해요. 특히 :has(), :not(), :is(), 속성 셀렉터의 고급 문법을 익히면 개발 속도가 눈에 띄게 빨라져요. 자바스크립트 루프로 필터링하던 코드가 셀렉터 한 줄로 줄어드는 경험을 하면 다시 돌아갈 수가 없거든요.
백엔드나 데이터 엔지니어링을 하는 분들도 웹 크롤링 업무가 생길 때 이 지식이 큰 도움이 돼요. BeautifulSoup에서 .find_all()로 하드코딩된 필터를 쓰는 대신 .select()로 CSS 셀렉터를 쓰면 훨씬 유연해져요.
마무리
CSS의 진정한 힘은 꾸미기가 아니라 "선택하기"에 있다는 관점이 인상적이에요. 여러분은 CSS 셀렉터를 가장 창의적으로 써본 순간이 언제였나요? 혹시 :has() 같은 최신 셀렉터를 프로덕션 코드에 이미 쓰고 계신가요?
🔗 출처: Hacker News
"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"
실제 수강생 후기- 비전공자도 6개월이면 첫 수익
- 20년 경력 개발자 직강
- 자동화 프로그램 + 소스코드 제공