CSS 선택자 총정리, “이 버튼만 바꿔줘”가 사이트 전체를 바꾼 이유

2026년 08월 23일

워드프레스 추가 CSS 창에 딱 세 줄을 넣었는데, 버튼 하나가 아니라 사이트의 모든 글자 색이 바뀌는 일이 있어요. 원인은 대부분 CSS가 어려워서가 아니라, 선택자(Selector)를 잘못 지목했기 때문이에요. 결론부터 말하면 CSS 선택자는 “어떤 요소를 바꿀지” 알려주는 좌표이고, 이 좌표가 넓으면 넓은 만큼 바뀝니다. 태그 선택자 하나(p)는 페이지의 모든 문단을 잡고, 클래스(.btn)는 그 클래스가 붙은 요소만, 아이디(#header)는 딱 하나만 잡아요. Claude Code에게 “이 부분만 바꿔줘”라고 말했을 때 엉뚱한 곳이 바뀌는 이유도 똑같아요. 사람은 화면을 보고 말하지만 AI는 좌표로 움직이거든요. 이 글에서는 선택자 종류를 문법 강의처럼 외우는 대신, 실제로 무엇이 어디까지 잡히는지 눈으로 구분하는 법과, 개발자 도구로 정확한 선택자를 찾아 AI에게 전달하는 순서까지 정리했어요.

“이 버튼만 바꿔줘”가 사이트 전체를 바꾼 이유

"이 버튼만 바꿔줘"가 사이트 전체를 바꾼 이유

📌 핵심 요약

CSS 선택자는 스타일을 적용할 대상을 지목하는 좌표이고, 좌표 범위가 넓을수록(태그 > 클래스 > 아이디 순) 의도하지 않은 곳까지 함께 바뀝니다.

CSS 규칙은 딱 두 덩어리예요. 앞쪽은 선택자(Selector), 뒤쪽 중괄호 안은 선언(속성: 값)이에요.

예를 들어 p { color: red; }라고 쓰면 “이 페이지의 모든 <p> 태그를 빨간색으로”라는 뜻이 돼요. 여기서 문제가 시작돼요. 사람은 화면에서 특정 문단 하나를 보며 말하지만, 브라우저는 p라는 좌표를 받아 페이지 안 모든 문단을 잡아요.

워드프레스에서 흔히 벌어지는 상황이 이거예요. 홈페이지 히어로 영역 문구만 키우려고 p { font-size: 20px; }를 추가 CSS에 넣으면 본문 글, 사이드바 위젯, 푸터 안내문까지 전부 20px이 돼요. 선택자가 잘못된 게 아니라 너무 넓게 지목된 거예요.

AI 코딩 도구에 요청할 때도 원리는 같아요. “이 버튼 색만 바꿔줘”라는 문장에는 좌표가 없어요. 화면을 함께 보고 있지 않은 상대에게 “저기 저거”라고 말하는 셈이라, 도구는 문서에서 가장 그럴듯한 대상을 추측해요. 그 추측이 button 태그 전체였다면 사이트의 모든 버튼이 바뀌어요.

💡 꼭 알아두세요

선택자는 자바스크립트의 document.querySelector()에서도 똑같은 문법을 써요. CSS 선택자를 한 번 익혀두면 스타일 수정뿐 아니라 스크립트로 요소를 잡을 때도 그대로 재활용돼요.

한눈에 보는 CSS 선택자 종류 요약표

선택자는 종류가 많아 보이지만 실제로는 다섯 갈래예요. 기본 선택자, 결합자(Combinator), 가상 클래스(Pseudo-class), 가상 요소(Pseudo-element), 그리고 이들을 이어 붙인 복합 선택자예요.

아래 표를 먼저 훑고, 뒤 섹션에서 헷갈리기 쉬운 것만 골라 파고들면 돼요. 처음부터 전부 외울 필요는 없어요. 실무에서 자주 쓰는 건 클래스, 자식, :hover, :nth-child, ::before 정도로 좁혀져요.

기호 명칭 의미와 예시
* 전체 선택자 모든 요소. * { margin: 0; }
p 태그(요소) 선택자 해당 태그 전부. a { color: #2f6b60; }
.name 클래스 선택자 class 값이 일치. .btn { ... }
#name ID 선택자 문서에 하나뿐인 대상. #site-header { ... }
[type="text"] 속성 선택자 속성값 조건 일치. a[target="_blank"]
공백 자손 선택자 아래 모든 단계. nav a
> 자식 선택자 바로 한 단계 아래만. ul > li
+ 인접 형제 선택자 바로 뒤 하나. h2 + p
~ 일반 형제 선택자 뒤따르는 형제 전부. h2 ~ p
, 그룹 선택자 둘 다 각각 적용. h1, h2
:hover 가상 클래스 특정 상태·순서. li:nth-child(2)
::before 가상 요소 없던 영역을 만들어 냄

기본 선택자 5종 — 전체·태그·클래스·ID·속성

기본 선택자 다섯 개가 전체의 8할이에요. 여기서 중요한 건 문법이 아니라 각각이 몇 개를 잡느냐예요.

*는 페이지의 모든 요소를 잡아요. 여백을 초기화할 때 쓰이지만, 초보자가 추가 CSS 창에서 쓰면 테마가 잡아둔 간격이 통째로 무너져요. 워드프레스 커스터마이징 단계에서는 거의 쓸 일이 없다고 봐도 돼요.

태그 선택자는 h2, a, li처럼 태그 이름을 그대로 써요. 범위가 넓어서 “사이트 전체의 링크 색”처럼 정말 전역으로 바꿀 때만 쓰는 게 안전해요.

클래스 선택자는 점(.)으로 시작하고 여러 요소에 반복해서 붙일 수 있어요. 워드프레스 테마는 대부분 클래스로 스타일을 관리해요. .entry-title, .site-footer, .wp-block-button__link 같은 이름들이 그거예요. 커스터마이징의 90%는 클래스 선택자로 해결돼요.

ID 선택자는 샵(#)으로 시작하고, 한 문서에 같은 ID가 하나만 있어야 해요. 범위가 가장 좁아 정확하지만 우선순위 점수가 높아서 나중에 스타일을 덮어쓰기 까다로워져요. 그래서 실무에서는 앵커 링크용으로만 두고 스타일은 클래스로 거는 경우가 많아요.

속성 선택자와 부분 일치 연산자

속성 선택자는 대괄호를 써요. input[type="email"]처럼 특정 속성값을 가진 요소만 잡아요. 여기에 세 가지 부분 일치 연산자가 붙으면 활용도가 크게 올라가요.

  • [class^="wp-"] — 값이 wp-로 시작하는 것 (^는 시작을 뜻하는 정규식 기호와 같아요)
  • [href$=".pdf"] — 값이 .pdf로 끝나는 것 ($는 끝)
  • [class*="card"] — 값에 card가 포함된 것 (*는 아무 위치)

실전에서 유용한 예가 외부 링크 표시예요. a[href^="http"]:not([href*="내도메인"])로 잡으면 외부로 나가는 링크에만 아이콘을 붙일 수 있어요. PDF 파일 링크에만 아이콘을 다는 a[href$=".pdf"]::after도 같은 원리예요.

공백 하나 차이 — 자손·자식·형제 선택자

공백 하나 차이 — 자손·자식·형제 선택자

초보자가 가장 자주 넘어지는 지점이에요. div p와 div > p는 공백 하나 차이인데 결과가 완전히 달라요.

자손 선택자(공백)는 아래로 몇 단계든 상관없이 전부 잡아요. nav a라고 쓰면 nav 안에 있는 모든 링크가 대상이에요. 서브 메뉴 안쪽 깊은 곳에 있는 링크까지 포함돼요.

자식 선택자(>)는 바로 한 단계 아래만 잡아요. nav > ul > li라고 쓰면 1차 메뉴 항목만 잡히고, 그 안의 드롭다운 하위 항목은 빠져요.

워드프레스에서 “메뉴 글자 크기를 키웠더니 드롭다운 하위 메뉴까지 커졌어요”라는 상황이 여기서 나와요. 자손 선택자로 .main-navigation a { font-size: 18px; }를 걸면 하위 메뉴 링크도 전부 18px이 돼요. 1차 메뉴만 키우려면 .main-navigation > ul > li > a처럼 경로를 한 단계씩 끊어줘야 해요.

형제 선택자 — + 와 ~

형제는 같은 부모 밑에 나란히 있는 요소예요. h2 + p는 h2 바로 다음에 오는 문단 하나만, h2 ~ p는 h2 뒤에 오는 모든 형제 문단을 잡아요.

소제목 바로 아래 첫 문단만 여백을 줄이고 싶을 때 h2 + p { margin-top: 8px; }가 딱 맞아요. 여기서 ~를 쓰면 그 섹션 문단 전부가 붙어버려요.

⚠️ 주의사항

형제 선택자는 뒤쪽만 선택해요. “앞 형제”를 직접 고르는 기호는 없어요. 앞 요소를 잡고 싶으면 :has()를 활용하거나 HTML 구조 자체를 조정하는 편이 빨라요.

.btn.primary와 .btn, .primary의 결정적 차이

점과 쉼표 하나 차이가 결과를 뒤집는 또 다른 지점이에요.

작성 형태 해석
.btn.primary 두 클래스를 모두 가진 요소 (AND)
.btn, .primary 둘 중 하나라도 가진 요소 (OR)
.btn .primary .btn 안쪽에 있는 .primary (자손)
a.btn .btn 클래스가 붙은 a 태그만

HTML에서 class="btn primary"처럼 클래스를 띄어쓰기로 여러 개 붙이는 건 흔한 일이에요. 워드프레스 블록 에디터가 만들어내는 마크업은 class="wp-block-button__link has-background has-text-color"처럼 클래스가 대여섯 개 붙기도 해요.

이때 “배경색이 지정된 버튼만” 바꾸고 싶다면 .wp-block-button__link.has-background처럼 붙여 써야 해요. 중간에 공백을 넣으면 버튼 안쪽에 있는 요소를 찾게 되고, 결국 아무것도 안 잡혀서 “CSS를 넣었는데 아무 변화가 없어요” 상태가 돼요.

변화가 없을 때 원인은 보통 셋 중 하나예요. 선택자 오타, 공백 유무, 그리고 뒤에서 다룰 우선순위 문제예요. 순서대로 확인하면 대부분 잡혀요.

가상 클래스와 가상 요소 — :hover, :nth-child, :not, :has

콜론이 하나면 가상 클래스, 둘이면 가상 요소예요. 이 구분만 잡아도 절반은 끝나요.

가상 클래스는 이미 있는 요소의 특별한 상태나 순서를 가리켜요. 마우스를 올린 상태(:hover), 세 번째 항목(:nth-child(3)), 첫 번째(:first-child), 마지막(:last-child) 같은 것들이에요.

가상 요소는 원래 없던 가짜 요소를 만들어 내요. ::before와 ::after가 대표적이고, content 속성이 있어야 화면에 나타나요. 인용문 앞에 따옴표 장식을 붙이거나 외부 링크 뒤에 화살표를 붙일 때 써요.

nth-child로 짝수·홀수 잡기

표에 줄무늬를 넣을 때 쓰는 게 :nth-child(even)과 :nth-child(odd)예요. 수식으로 2n(짝수), 2n+1(홀수)를 써도 같아요. n은 0부터 올라가는 숫자라고 생각하면 돼요. :nth-child(3n)이면 3, 6, 9번째가 잡혀요.

1

표 줄무늬

tbody tr:nth-child(even) { background: #f7f5f0; } — 짝수 행에만 배경색을 넣어 가독성을 올려요.

2

마지막 항목 구분선 제거

li:last-child { border-bottom: none; } — 목록 맨 아래 선이 남아 어색해 보이는 문제를 한 줄로 해결해요.

3

특정 항목만 제외

.menu li:not(.current) a { opacity: .7; } — 현재 페이지 메뉴만 남기고 나머지를 흐리게 처리해요.

:not, :is, :where, :has

:not()은 제외예요. 최신 문법에서는 :not(.a, .b)처럼 여러 개를 한 번에 넣을 수 있고, 구형 브라우저를 신경 쓴다면 :not(.a):not(.b)로 나눠 쓰면 돼요.

:is()는 여러 선택자를 묶어 코드를 줄여줘요. :is(h1, h2, h3) a는 세 줄을 한 줄로 압축한 셈이에요. :where()는 하는 일이 비슷하지만 우선순위 점수를 0으로 만들어서, 나중에 덮어쓰기 쉬운 기본 스타일을 깔 때 유용해요.

:has()는 오랫동안 없던 “부모 선택자” 역할을 해요. .card:has(img)는 이미지가 들어 있는 카드만 잡아요. h2:has(+ p)처럼 쓰면 뒤에 문단이 따라오는 소제목만 고를 수도 있어요. 다만 지원 범위가 다른 선택자보다 늦게 자리 잡았으니, 실제 사용 전에 브라우저 지원 현황을 확인하고 없어도 무너지지 않는 스타일에 먼저 적용하는 게 안전해요.

우선순위(특이성) 계산법과 !important 이야기

우선순위(특이성) 계산법과 !important 이야기

“추가 CSS에 분명히 넣었는데 적용이 안 돼요”의 8할은 특이성(Specificity) 문제예요. 같은 요소에 서로 다른 규칙이 걸리면 브라우저는 점수가 높은 쪽을 따라요.

구분 점수 예시
!important 모든 점수 위 color: red !important;
인라인 스타일 1000 <p style="...">
ID 100 #header
클래스·속성·가상 클래스 10 .btn, [type="text"], :hover
태그·가상 요소 1 p, ::before
전체 선택자 0 *

직접 계산해 볼까요. .site-header .main-navigation ul li a는 클래스 2개(20) + 태그 3개(3) = 23점이에요. 내가 쓴 .main-navigation a는 클래스 1개(10) + 태그 1개(1) = 11점이라 밀려요. 테마 CSS가 이기는 이유가 이거예요.

해결책은 점수를 억지로 올리는 게 아니라, 테마가 쓰는 것과 같은 수준으로 맞추거나 한 단계만 더 구체적으로 쓰는 거예요. 같은 점수라면 나중에 읽힌 규칙이 이기는데, 워드프레스 추가 CSS는 보통 테마 스타일시트보다 뒤에 로드되니 동점만 만들어도 대개 적용돼요.

⚠️ 주의사항

!important는 당장은 통하지만, 다음에 그 스타일을 바꾸려면 또 !important를 써야 하는 악순환이 시작돼요. 하나가 열이 되는 순간 CSS 파일 전체가 손대기 어려워져요. 정말 급할 때 임시로만 쓰고, 원인이 된 선택자를 찾아 정리하는 편이 나아요.

참고로 선택자를 무한정 길게 늘이는 것도 좋지 않아요. 브라우저는 선택자를 오른쪽에서 왼쪽으로 읽어요. body div ul li a라고 쓰면 먼저 모든 a를 모은 뒤 위로 거슬러 조건을 검사해요. 클래스 하나로 끝낼 수 있으면 그게 가장 빠르고 안전해요.

개발자 도구로 선택자를 찾아 AI에게 전달하기

개발자 도구로 선택자를 찾아 AI에게 전달하기

여기까지 오면 남은 건 “내가 바꾸고 싶은 그 부분의 정확한 좌표를 어떻게 알아내느냐”예요. 답은 브라우저 개발자 도구예요.

1

바꾸고 싶은 부분에서 검사 실행

해당 요소 위에서 마우스 오른쪽 버튼을 눌러 검사를 선택하거나 F12를 눌러요. 오른쪽에 HTML 구조와 적용된 CSS가 함께 뜨는데, 이 화면이 사실상 정답지예요.

2

class 값과 상위 구조를 함께 확인

선택된 태그의 class 목록을 보고, 그 위 부모 요소의 클래스도 한두 단계 같이 적어두세요. 같은 클래스가 여러 곳에 쓰였다면 부모 클래스로 범위를 좁혀야 해요.

3

Styles 패널에서 실시간으로 실험

오른쪽 Styles 패널에서 값을 직접 고쳐보면 화면이 바로 바뀌어요. 새로고침하면 사라지니 사이트를 망가뜨릴 걱정 없이 마음껏 시험해도 돼요. 원하는 결과가 나온 값만 추가 CSS로 옮기면 끝이에요.

4

선택자를 문장에 넣어 AI에게 요청

“이 버튼” 대신 “.wp-block-button__link 클래스가 붙은 요소만”이라고 말해요. 좌표가 문장에 들어가는 순간 엉뚱한 곳이 바뀔 확률이 크게 떨어져요.

Copy → Copy selector 기능을 쓰면 선택자가 클립보드에 복사돼요. 다만 이 값은 body > div:nth-child(3) > main > article처럼 순서 기반으로 길게 나오는 경우가 많아요. 글이 하나 추가되면 깨지기 쉬우니, 복사한 값을 그대로 쓰기보다 그 안에서 의미 있는 클래스 이름을 골라 쓰는 게 좋아요.

AI에게 전달할 때의 프롬프트 뼈대

요청 문장에 네 가지만 담으면 결과가 확 달라져요. 대상 선택자, 바꿀 속성, 원하는 값, 그리고 영향 범위 제한이에요.

예를 들면 이렇게요. “추가 CSS에 넣을 코드를 만들어줘. 대상은 .site-header .main-navigation > ul > li > a이고, 글자 크기를 17px로 키우되 하위 드롭다운 메뉴에는 영향이 없어야 해. !important는 쓰지 말고, 왜 이 선택자를 썼는지 한 줄로 설명해줘.”

여기서 “영향이 없어야 해”와 “왜 이 선택자인지 설명해줘”가 핵심이에요. 설명을 요구하면 잘못된 범위를 잡았을 때 코드를 붙여넣기 전에 알아챌 수 있어요.

📋 CSS가 적용 안 될 때 점검 체크리스트

✓ 클래스 앞 점(.), 아이디 앞 샵(#)을 빠뜨리지 않았나
✓ .a.b로 써야 할 곳에 공백을 넣지 않았나
✓ 중괄호·세미콜론이 빠져 아래 규칙까지 무효가 되진 않았나
✓ 개발자 도구 Styles에서 내 규칙에 취소선이 그어져 있지 않나(우선순위 밀림)
✓ 캐시 플러그인이나 브라우저 캐시를 비웠나
✓ 모바일 화면은 미디어쿼리로 별도 규칙이 걸려 있진 않나

자주 묻는 질문

CSS 선택자는 정확히 무슨 뜻인가요?

HTML 문서에서 스타일을 적용할 대상 요소를 지목하는 패턴이에요. 선택자 { 속성: 값; } 구조에서 중괄호 앞부분 전체가 선택자예요. 영어 Selector를 우리말로 옮긴 말이라 “셀렉터”와 같은 개념이에요.

클래스와 아이디 중 무엇을 써야 하나요?

스타일 목적이라면 클래스를 권해요. 아이디는 한 문서에 하나만 쓸 수 있고 우선순위 점수가 100점이라, 나중에 다른 규칙으로 덮어쓰기가 까다로워져요. 아이디는 앵커 링크 이동이나 스크립트에서 특정 요소를 잡을 때 남겨두는 편이 관리하기 편해요.

CSS에 부모 선택자가 있나요?

오랫동안 없었지만 지금은 :has()로 사실상 부모를 고를 수 있어요. .card:has(img)처럼 특정 자식을 가진 부모만 잡는 식이에요. 다른 기본 선택자보다 지원 시점이 늦었으니, 이게 동작하지 않아도 레이아웃이 무너지지 않는 선에서 쓰는 걸 권해요.

!important를 아예 쓰면 안 되나요?

금지는 아니지만 마지막 수단이에요. 한 번 쓰면 그 규칙을 이기기 위해 또 !important가 필요해지고, 이런 규칙이 쌓이면 어디를 고쳐야 할지 추적하기 어려워져요. 대신 선택자를 한 단계 더 구체적으로 쓰거나, 개발자 도구에서 어떤 규칙에 밀리고 있는지 먼저 확인해 보세요.

선택자를 길게 쓰면 사이트가 느려지나요?

요즘 브라우저 성능에서 체감할 만한 차이는 거의 없어요. 다만 브라우저가 선택자를 오른쪽에서 왼쪽으로 읽는 구조라 body div ul li a처럼 넓은 대상에서 시작하는 형태는 비효율적이에요. 성능보다는 유지보수 측면에서, 의미 있는 클래스 하나로 끝내는 습관이 훨씬 이득이에요.

추가 CSS와 자식 테마 중 어디에 써야 하나요?

몇 줄짜리 색상·여백 조정이라면 워드프레스 관리자의 추가 CSS로 충분해요. 다만 테마를 바꾸면 이 값이 함께 따라가지 않으니, 규칙이 수십 줄로 늘어나거나 템플릿 파일까지 손보게 되면 자식 테마로 옮기는 걸 고려해 보세요.

참고자료

  • MDN Web Docs — CSS 선택자 : 선택자 전체 목록과 문법을 한국어로 정리한 표준 레퍼런스
  • W3C Selectors Level 4 : 선택자 표준 명세 원문, 특이성 계산 규칙의 근거 문서
  • Can I use : :has(), :is() 등 최신 선택자의 브라우저 지원 현황 확인
  • WordPress 공식 문서 — 스타일 개요 : 워드프레스에서 추가 CSS와 테마 스타일이 적용되는 방식 안내

CSS 선택자는 외워야 할 문법 목록이라기보다, 내가 바꾸고 싶은 곳을 정확히 가리키는 좌표 체계예요. 태그는 넓게, 클래스는 알맞게, 아이디는 좁게 잡힌다는 감각 하나만 잡혀도 “왜 엉뚱한 데가 바뀌었지?”라는 상황이 눈에 띄게 줄어들어요. 공백과 꺾쇠(자손 vs 자식), 점과 쉼표(AND vs OR), 콜론 개수(상태 vs 가상 요소) 이 세 쌍만 구분해도 초보 구간의 실수는 대부분 사라져요. 순서는 이렇게 권해요. 개발자 도구로 대상을 검사하고, Styles 패널에서 값을 직접 바꿔 결과를 눈으로 확인한 다음, 그때 확정된 선택자를 문장에 담아 AI에게 요청하는 거예요. 요청에 좌표와 영향 범위 제한을 함께 적으면 결과물의 정확도가 달라져요. 그리고 적용이 안 될 땐 !important부터 찾지 말고 개발자 도구에서 취소선이 그어진 규칙을 먼저 확인해 보세요. 원인을 알고 고친 한 줄이 나중에 사이트를 훨씬 편하게 만들어 줘요.


함께 보면 좋은 글

About the author
VIBE PRESS
코딩을 배운 적 없는 사람이 Claude Code와 워드프레스로 직접 사이트를 만들어가는 기록입니다. 완성된 정답을 가르치는 곳이 아니라, 막히고 헤매고 겨우 해결한 과정을 그대로 남깁니다. 도메인 연결, 호스팅 설정, 테마와 플러그인, 글 발행 자동화까지 — 직접 부딪히며 알게 된 것들을 순서대로 씁니다. 잘못 알고 있던 것을 나중에 고치는 일도 있습니다. 그때는 글을 수정하고 무엇이 틀렸는지 함께 남깁니다.