Pseudo-classes and pseudo-elements

김동현·2026년 3월 17일

mdn 학습 번역 - CSS

목록 보기
4/190

가상 클래스란 무엇인가요? (What is a pseudo-class?)

가상 클래스는 요소가 특정 상태(state)에 있을 때만 스타일을 적용해 주는 선택자입니다. 예를 들어, 어떤 요소가 부모 안에서 첫 번째 자식이거나, 마우스 포인터가 그 요소 위에 올라가 있을 때(hover) 등이 있죠. HTML 문서에 여러분이 직접 특별한 클래스를 써넣은 것처럼 동작하기 때문에, 불필요한 클래스 남발을 줄여주고 코드를 훨씬 유연하고 유지 보수하기 쉽게 만들어 줍니다.

가상 클래스는 콜론 한 개(:)로 시작하는 키워드예요. 예를 들어 :hover가 대표적인 가상 클래스입니다.

가상 클래스의 기본 예제 (Basic pseudo-class example)

기본적인 예제를 하나 볼까요? 어떤 글(article) 안에서 첫 번째 문단(paragraph)만 글씨를 키우고 굵게 만들고 싶다고 해봅시다. 가장 단순한 방법은 첫 번째 문단에 직접 클래스(예: .first)를 달고 거기에 CSS를 먹이는 거겠죠.

<article>
  <p class="first">
    Veggies es bonus vobis, proinde vos postulo essum magis kohlrabi welsh onion
    daikon amaranth tatsoi tomatillo melon azuki bean garlic.
  </p>

  <p>
    Gumbo beet greens corn soko endive gumbo gourd. Parsley shallot courgette
    tatsoi pea sprouts fava bean collard greens dandelion okra wakame tomato.
    Dandelion cucumber earthnut pea peanut soko zucchini.
  </p>
</article>
.first {
  font-size: 120%;
  font-weight: bold;
}

하지만 이런 방식은 유지 보수할 때 엄청 피곤해져요. 만약 맨 위에 새로운 문단이 추가되면 어떡하죠? HTML을 열어서 기존 문단에 있던 .first 클래스를 지우고, 새 문단에 다시 적어줘야 합니다. (게다가 CMS 도구로 자동으로 생성되는 글이라면 HTML을 마음대로 수정하지도 못할 수 있어요.)

이럴 때 클래스를 일일이 달아주는 대신 :first-child 가상 클래스 선택자를 사용하면 됩니다! 이 선택자는 어떤 부모 요소(여기서는 <article>) 안에서 무조건 첫 번째 자식 요소만 알아서 타겟팅해 줘요. 덕분에 HTML을 전혀 건드릴 필요가 없어집니다.

<article>
  <p>
    Veggies es bonus vobis, proinde vos postulo essum magis kohlrabi welsh onion
    daikon amaranth tatsoi tomatillo melon azuki bean garlic.
  </p>

  <p>
    Gumbo beet greens corn soko endive gumbo gourd. Parsley shallot courgette
    tatsoi pea sprouts fava bean collard greens dandelion okra wakame tomato.
    Dandelion cucumber earthnut pea peanut soko zucchini.
  </p>
</article>
article p:first-child {
  font-size: 120%;
  font-weight: bold;
}

모든 가상 클래스는 이런 식으로 작동합니다. 문서의 특정 부분이 특정한 상태에 놓였을 때, 마치 HTML에 몰래 클래스를 끼워 넣은 것처럼 동작하죠.

📝 참고사항 (Note):
가상 클래스나 가상 요소를 쓸 때 앞에 꼭 태그 이름(p, a 등)을 붙일 필요는 없습니다. 위 예제에서 article :first-child (띄어쓰기 주의)라고만 써도 동작하거든요. 그러면 <article> 안에 있는 모든 요소 중 부모의 첫 번째 자식인 것들을 전부 타겟팅합니다(*:first-child와 같은 의미). 하지만 실무에서는 내가 의도하지 않은 요소까지 엉뚱하게 선택되는 걸 막기 위해, 가급적 명확하게 대상을 지정해 주는 편이 좋습니다.

사용자의 행동에 반응하는 가상 클래스 (User-action pseudo classes)

어떤 가상 클래스들은 사용자가 웹 페이지와 상호작용(interaction)할 때만 짠! 하고 나타납니다. 이걸 사용자 동작(User-action) 가상 클래스 혹은 동적(Dynamic) 가상 클래스라고 부르는데요. 대표적으로 이런 것들이 있어요.

  • :hover — 위에서도 언급했죠. 사용자가 요소(주로 링크나 버튼) 위에 마우스 포인터를 올려놓았을 때만 적용됩니다.
  • :focus — 사용자가 마우스로 클릭하거나 키보드(Tab 키 등)를 사용해서 특정 요소에 초점을 맞췄을 때(포커스 상태) 적용됩니다.

💡 강사의 실무 팁!
:focus:focus-visible은 웹 접근성(a11y)에서 정말정말 중요합니다! 마우스를 못 쓰시고 키보드만으로 사이트를 탐색하는 분들을 위해, 현재 어느 버튼에 포커스가 가 있는지 시각적으로 뚜렷하게 보여줘야 하거든요. 아웃라인(outline)을 예쁘게 꾸며서 사용자 경험을 높여보세요.

<p><a href="">Hover over me</a></p>
a:link,
a:visited {
  color: rebeccapurple;
  font-weight: bold;
}

a:hover {
  color: hotpink;
}

가상 클래스로 놀아보기 (Playing with pseudo-classes)

MDN Playground에서 첫 번째 가상 클래스 예제 코드를 이리저리 만져보세요.
1. 문단에 마우스를 올렸을 때(hover) 글자 색상을 파란색(blue)으로 바꾸는 규칙을 추가해 보세요.
2. <article> 안에 있는 여러 문단 중 가장 마지막 문단만 선택해서 배경색(background-color)을 주황색(orange)으로 만들어 보세요. (:last-child 같은 걸 찾아보시면 되겠죠?)

더 많은 가상 클래스 친구들이 궁금하다면 MDN의 가상 클래스(Pseudo-classes) 레퍼런스 페이지를 확인해 보세요!


가상 요소란 무엇인가요? (What is a pseudo-element?)

가상 요소도 가상 클래스와 느낌은 비슷합니다. 하지만 큰 차이가 있어요. 가상 클래스가 기존에 있는 요소에 '클래스를 추가'하는 느낌이라면, 가상 요소는 아예 HTML 마크업에 존재하지 않던 '새로운 요소를 하나 만들어 넣은 것'처럼 행동합니다.

가상 요소는 콜론 두 개(::)로 시작해요. ::before가 대표적인 가상 요소입니다.

📝 참고사항 (Note):
CSS 초기 버전에서는 가상 요소도 콜론을 하나만(:) 썼었어요. 그래서 인터넷에 떠도는 옛날 코드들을 보면 :before처럼 콜론 하나로 쓰인 걸 보실 수 있을 거예요. 현대의 브라우저들은 과거 코드 호환성을 위해 콜론을 하나 쓰든 두 개 쓰든 다 알아먹긴 하지만, 가상 클래스와 가상 요소를 명확히 구분하기 위해 가상 요소에는 꼭 콜론을 두 개(::) 쓰는 것을 습관화하세요!

어떤 상황에서 쓰이는지 볼까요? 만약 문단(<p>)의 '첫 번째 줄'만 딱 골라서 스타일을 주고 싶다고 가정해 봅시다.
단순하게 생각하면 첫 줄이 될 만한 텍스트를 <span> 태그로 감싸고 거기에 스타일을 주면 될 것 같죠? 하지만 안타깝게도 사용자의 화면 크기가 줄어들거나 폰트 크기가 커지면 글자가 아래로 줄바꿈 되면서 <span>으로 감싼 범위를 벗어나 버립니다. 반응형 웹 환경에서 첫 줄에 들어갈 단어 수를 미리 예측해서 HTML 태그를 일일이 달아주는 건 아예 불가능한 일이에요.

이럴 때 구세주처럼 등장하는 게 바로 ::first-line 가상 요소입니다! 이 녀석은 화면 크기가 변해서 글자가 늘어나든 줄어들든 상관없이, 언제나 브라우저 화면에 보이는 진짜 첫 번째 줄만 기가 막히게 선택해 줍니다.

<article>
  <p>
    Veggies es bonus vobis, proinde vos postulo essum magis kohlrabi welsh onion
    daikon amaranth tatsoi tomatillo melon azuki bean garlic.
  </p>

  <p>
    Gumbo beet greens corn soko endive gumbo gourd. Parsley shallot courgette
    tatsoi pea sprouts fava bean collard greens dandelion okra wakame tomato.
    Dandelion cucumber earthnut pea peanut soko zucchini.
  </p>
</article>
article p::first-line {
  font-size: 120%;
  font-weight: bold;
}

마치 브라우저가 화면을 그릴 때마다 실시간으로 첫 줄을 계산해서, 안 보이는 투명한 <span> 태그를 마법처럼 씌워주는 것과 같아요. 위 예제를 실행해 보시면 화면 너비를 조절해도 항상 각 문단의 첫 줄만 굵어지는 걸 보실 수 있습니다.

가상 요소로 놀아보기 (Playing with pseudo-elements)

위 예제의 CSS를 직접 수정해 보세요.
1. 사용자가 마우스로 드래그해서 블록 지정(선택)한 텍스트 부분의 배경색(background-color)을 빨간색(red)으로 바꾸는 규칙을 추가해 보세요. (이 기능을 구현하려면 ::selection 가상 요소를 사용해야 해요. 코드 작성 후 직접 글자를 드래그해 보세요!)
2. <article> 안에 있는 각 문단(<p>)의 가장 첫 번째 글자(알파벳 하나)에만 다음 스타일을 줘보세요.
배경색 yellow
테두리 1px solid black
* 폰트 크기 2rem

다른 가상 요소들이 궁금하시다면 MDN의 가상 요소(Pseudo-elements) 레퍼런스 페이지를 둘러보세요.


가상 클래스와 가상 요소 결합하기 (Combining pseudo-classes and pseudo-elements)

원한다면 가상 클래스와 가상 요소를 체인처럼 줄줄이 엮어서 쓸 수도 있어요!
예를 들어, "여러 문단 중에 오직 첫 번째 문단의, 첫 번째 줄만 굵게 만들고 싶어!"라고 한다면 :first-child::first-line을 함께 쓰면 됩니다.

아래 CSS 코드를 이전 예제에 적용해 보세요. 해석하자면 이렇습니다. "<article> 태그 안에 있는 <p> 태그 중에서 가장 첫 번째 자식(:first-child)을 고르고, 그중에서도 화면에 보이는 첫 번째 줄(::first-line)만 선택해라!"

article p:first-child::first-line {
  font-size: 120%;
  font-weight: bold;
}

::before와 ::after로 콘텐츠 생성하기 (Generating content with ::before and ::after)

가상 요소 중에서도 아주아주 특별한 취급을 받는 녀석들이 있습니다. 바로 ::before::after인데요. 이 둘은 content라는 CSS 속성과 찰떡궁합으로 엮여서, HTML 문서에는 아예 존재하지도 않는 텍스트나 이미지를 CSS만으로 화면에 끼워 넣을 때 사용합니다. 이런 기법을 생성된 콘텐츠(generated content)라고 불러요.

아래 예제를 보세요. HTML에는 그냥 "Content in the box..."라는 글만 있지만, CSS를 이용해 그 글자 바로 앞(::before)에 다른 문자열을 강제로 쑤셔 넣었어요. 본문 내용과 헷갈리지 않게 노란색 배경도 칠해줬고요.

<p class="box">Content in the box in my HTML page.</p>
.box::before {
  content: "This should show before the other content. ";
  background-color: yellow;
}

생성된 콘텐츠로 놀아보기 (Playing with generated content)

위 예제를 이렇게 수정해 보세요:

  • content 속성 안에 있는 텍스트를 내 맘대로 바꿔보고 화면이 어떻게 변하는지 확인해 보세요.
  • ::before::after로 바꿔보세요. 아까는 글자 맨 앞에 텍스트가 생겼지만, 이제는 글자가 다 끝나고 난 뒤(맨 뒤)에 텍스트가 삽입되는 걸 볼 수 있을 거예요.

아이콘이나 장식 요소로 활용하기 (Generated content icons)

방금 보여드린 예제처럼 긴 문장을 CSS로 집어넣는 코드는 문법적으로는 아무 문제가 없어요. 하지만 실무에서 이런 식으로 텍스트를 우겨넣는 경우는 거의 없습니다. 왜냐하면 시각 장애인용 스크린 리더기가 이 글자를 제대로 읽지 못할 수도 있고, 나중에 문구를 수정해야 할 때 개발자가 "이 텍스트 대체 HTML 어디 숨어있는 거야?" 하고 한참을 헤맬 수 있거든요.

대신 실무에서는 디자인적인 장식 요소나 아이콘을 넣을 때 이 기능을 정말 적극적으로 씁니다. 예를 들어 아래처럼 링크 옆에 자그마한 화살표를 달아주는 식이죠. 이런 화살표는 스크린 리더기가 굳이 소리 내어 읽어줄 필요가 없는 순수한 디자인 요소니까요.

<p class="box">Content in the box in my HTML page.</p>
.box::after {
  content: " ➥";
}

도형 만들어 넣기 (Generated shapes)

생성된 콘텐츠의 또 다른 엄청난 활용법은 content: "" 처럼 내용물을 완전히 비워둔 채로, CSS 스타일링을 이용해 나만의 도형을 만들어 화면에 끼워 넣는 거예요.

아래 예제를 볼까요? ::before를 사용해서 빈 껍데기 문자열("")을 넣었어요. 이 녀석을 display: block으로 만들고 너비(width)와 높이(height)를 줘서 네모반듯한 상자를 하나 뚝딱 만들어냈습니다. HTML 태그를 하나도 늘리지 않고 말이죠!

<p class="box">Content in the box in my HTML page.</p>
.box::before {
  content: "";
  display: block;
  width: 100px;
  height: 100px;
  background-color: rebeccapurple;
  border: 1px solid black;
}

위 코드를 가지고 놀면서 크기를 바꾸거나 border-radius: 50%를 넣어서 동그라미로 만들어 보세요.

💡 강사의 실무 팁!
::before, ::after는 정말 무궁무진하게 쓰입니다.

  • 말풍선(Tooltip) 꼬리 뾰족하게 튀어나온 부분 만들 때
  • 햄버거 메뉴 아이콘(가로줄 3개)을 태그 하나만 써서 CSS로 다 그릴 때
  • 체크박스나 라디오 버튼을 기본 브라우저 스타일이 아니라 커스텀 디자인으로 예쁘게 갈아엎을 때

이런 곳에 아주 단골손님으로 등장하니까, 개념을 꽉 잡아두시면 나중에 UI 구현하실 때 정말 든든하실 겁니다!
CSS Arrow Please 같은 사이트에 가서 화살표 말풍선을 만들어보시면 어떻게 ::before::after가 쓰이는지 그 원리를 명확하게 보실 수 있을 거예요.


요약 (Summary)

이번 문서에서는 CSS 선택자 중에서도 아주 특별한 능력을 가진 가상 클래스가상 요소를 만나보았습니다.

  • 가상 클래스(:)는 요소가 어떤 특정 '상태(마우스 오버 등)'에 있을 때 HTML에 클래스를 달아준 것처럼 반응합니다.
  • 가상 요소(::)는 기존에 없던 새로운 요소를 HTML에 추가한 것처럼 반응하며, 특히 ::before::after는 CSS만으로 화면에 콘텐츠나 예쁜 도형 장식을 덧붙일 수 있게 해줍니다.

자, 이제 이 마법 같은 선택자들을 익혔으니 다음에는 선택자들을 이리저리 조합하는 결합자(Combinators)에 대해 배워보도록 합시다!

profile
프론트에_가까운_풀스택_개발자

0개의 댓글