---
title: "이름을 붙여야 스타일을 걸 수 있다"
description: "UI Toolkit 에서 모양은 요소에 붙지 않고 선택자에 붙습니다. 타입, 이름, 클래스, 전체 네 가지로 부르는 방법과"
author: "유니얼"
pubDate: 2026-09-18
category: "game-dev/unity/ui"
tags: ["Unity", "UI", "UI Toolkit"]
canonical: https://blog.unialgames.com/posts/uitk-uss-selectors/
---
[화면 하나가 파일 두 개와 오브젝트 하나가 된다](/posts/uitk-uxml-uss-basics/)에서 `.uxml` 은 구조, `.uss` 는 모양이라고 나눴습니다. 파일이 나뉘면 바로 따라오는 질문이 하나 있습니다. **이 모양을 누구에게 걸 것인가** 를 어딘가에 적어야 합니다.

uGUI 에는 이 질문이 없었습니다. 버튼을 클릭하고 인스펙터에서 색을 바꾸면 그 버튼이 바뀌었습니다. 고르는 일과 값을 넣는 일이 한 동작이었습니다. UI Toolkit 에서는 그 둘이 떨어져 있습니다.

## 모양은 요소가 아니라 문장에 붙습니다

uss 에 적는 규칙 하나는 이렇게 생겼습니다.

```css
.primary {
    background-color: #2f6fed;
    color: white;
}
```

앞의 `.primary` 가 **누구에게** 이고, 중괄호 안이 **무엇을** 입니다. 앞부분을 선택자라고 부릅니다.

그래서 순서가 뒤집힙니다. uGUI 는 요소를 고른 다음 값을 넣었고, 여기서는 값을 먼저 적어 두고 그 값을 받을 요소에 표식을 답니다. 표식이 없는 요소는 어느 규칙에도 안 걸리고, 그래서 아무 모양도 받지 못합니다.

## 부르는 방법은 네 가지입니다

<x-uss>
<script type="application/json">
{"label":"선택자 하나가 고르는 것","note":"같은 트리에 선택자만 바꿔 걸어 본 것입니다. 지우기 버튼은 클래스를 두 개 달고 있어서 두 규칙을 모두 받습니다.","default":{"sel":0}}
</script>
</x-uss>

| 선택자 | 적는 법 | 걸리는 것 |
| --- | --- | --- |
| 타입 | `Button { ... }` | 그 타입인 요소 전부 |
| 이름 | `#ok { ... }` | 그 이름을 가진 요소 하나 |
| 클래스 | `.primary { ... }` | 그 클래스를 단 요소 전부 |
| 전체 | `* { ... }` | 전부 |

타입 선택자에는 네임스페이스를 안 적습니다. 문서가 못 박아 둔 부분입니다. "When you write type selectors, specify only the concrete object type. Don't include the namespace in the type name." `UnityEngine.UIElements.Button` 이 아니라 `Button` 입니다.

이어 붙이는 방법도 있습니다. `.row Button` 은 `.row` 안 어딘가에 있는 버튼 전부이고, `.row > Button` 은 바로 아래 칸의 버튼만입니다. `.primary.danger` 처럼 띄우지 않고 붙이면 두 클래스를 다 단 요소만 걸립니다. 쉼표로 늘어놓으면 같은 값을 여럿에게 한 번에 겁니다.

## `#` 와 `.` 은 uss 에만 씁니다

uxml 쪽에는 기호를 쓰지 않습니다.

```xml
<Button name="ok" class="primary" text="확인" />
<Button name="delete" class="primary danger" text="지우기" />
```

클래스를 여럿 달 때는 띄어쓰기로 늘어놓습니다.

문서가 이 부분을 두 번 적어 뒀습니다. 클래스 쪽은 "Only use a period when you write the selector in a USS file. Don't include it when you assign the class to an element in a UXML or C# file." 이고, 이름 쪽은 더 분명합니다. "An element name that includes the number sign is invalid. For example `<Button name="#OK" />` is invalid."

`name="#ok"` 이라고 적어 놓고 왜 안 걸리지 하는 일이 처음에 한 번쯤 있습니다.

코드에서 다는 것도 같습니다.

```csharp
var ok = root.Q<Button>("ok");
ok.AddToClassList("primary");
ok.RemoveFromClassList("ghost");
ok.EnableInClassList("danger", isDangerous);
```

마지막 줄이 실제로 제일 많이 쓰입니다. 값이 참이면 붙이고 거짓이면 뗍니다. 상태에 따라 모양이 바뀌는 자리는 대부분 이 한 줄로 끝납니다.

## 이름은 하나, 클래스는 여럿입니다

문서 표현으로 "To avoid unexpected matches, make element names unique within a panel." 입니다. 이름은 한 요소를 가리키려고 있는 것입니다.

그래서 이름 선택자로 모양을 다 걸면 화면이 하나 늘 때마다 uss 도 같이 늘어납니다. `#ok`, `#shopOk`, `#resultOk` 같은 것이 쌓이고, 나중에 버튼 모서리를 둥글게 바꾸려면 그 줄을 전부 찾아야 합니다.

클래스는 반대입니다. **한 번 적고 여러 군데서 답니다.** uGUI 에서 같은 버튼을 여러 화면에 쓰려고 프리팹을 만들었다면, UI Toolkit 에서 그 자리에 있는 것이 클래스입니다. 다른 점은 프리팹은 구조까지 같이 묶여 오고, 클래스는 모양만 온다는 것입니다.

한 요소가 클래스를 여럿 달 수 있다는 것이 여기서 힘이 됩니다. 문서 표현으로 "When an element has more than one class assigned, a selector only has to match one of them to match the element." 입니다. `class="primary danger"` 는 primary 규칙과 danger 규칙을 둘 다 받습니다. 공통 모양은 primary 에 적어 두고, 다른 부분만 danger 에 적으면 됩니다.

정할 것은 하나입니다. **이 값이 이 요소에만 해당하는가, 같은 종류 전부에 해당하는가.** 앞이면 이름, 뒤면 클래스입니다.

## 상태 이름은 이미 정해져 있습니다

uGUI 에서 버튼 색을 상태별로 넣던 자리가 여기서는 uss 에 있습니다.

```css
.primary:hover {
    background-color: #4a83f0;
}
.primary:disabled {
    opacity: 0.4;
}
```

쓸 수 있는 이름은 이렇습니다.

| 이름 | 언제 |
| --- | --- |
| `:hover` | 커서가 그 요소 위에 있을 때 |
| `:active` | 버튼이나 토글을 누르고 있는 동안 |
| `:inactive` | 누르기를 그만뒀을 때 |
| `:focus` | 그 요소가 포커스를 가졌을 때 |
| `:enabled` / `:disabled` | 켜져 있을 때 / 꺼져 있을 때 |
| `:checked` | 토글이나 라디오가 선택됐을 때 |
| `:root` | 그 스타일시트가 걸린 트리의 맨 위 |

같이 알아 둘 것이 둘 있습니다. 하나는 **새로 만들 수 없다** 는 것입니다. 문서가 "You can't extend pseudo-classes or create custom ones" 라고 적어 뒀습니다. "선택된 아이템" 이나 "새로 들어온 항목" 같은 우리 쪽 상태는 클래스를 직접 붙였다 뗐다 해서 만듭니다. 위의 `EnableInClassList` 가 그 자리입니다.

다른 하나는 CSS 쪽에 있는 `:selected` 가 여기에는 없다는 것입니다. 토글은 `:checked` 를 씁니다.

## 유니티가 만든 컨트롤에도 클래스가 이미 붙어 있습니다

`<Button />` 하나를 놓으면 클래스를 아무것도 안 단 것 같지만 아닙니다. 유니티 소스에 이렇게 적혀 있습니다.

```csharp
// Button.cs
public new static readonly string ussClassName = "unity-button";
public static readonly string iconUssClassName = ussClassName + "--with-icon";
public static readonly string imageUSSClassName = ussClassName + "__image";
// Toggle.cs
public new static readonly string ussClassName = "unity-toggle";
public new static readonly string labelUssClassName = ussClassName + "__label";
public new static readonly string inputUssClassName = ussClassName + "__input";
public static readonly string checkmarkUssClassName = ussClassName + "__checkmark";
```

이름 짓는 규칙이 보입니다.

<figure class="diagram">
<svg viewBox="0 0 680 228" role="img" aria-label="unity-toggle__label 과 unity-button--with-icon 을 조각으로 나눈 그림">
<text x="24" y="24" font-size="13" fill="var(--text)">유니티가 붙여 둔 클래스 이름은 조각으로 되어 있습니다</text>
<rect x="28" y="62" width="126" height="36" rx="7" fill="var(--accent2)" fill-opacity="0.14" stroke="var(--accent2)"></rect>
<text x="91" y="85" font-size="12.5" fill="var(--text)" font-family="var(--font-mono)" text-anchor="middle">unity-toggle</text>
<rect x="154" y="62" width="88" height="36" rx="7" fill="var(--accent)" fill-opacity="0.16" stroke="var(--accent)"></rect>
<text x="198" y="85" font-size="12.5" fill="var(--text)" font-family="var(--font-mono)" text-anchor="middle">__label</text>
<text x="91" y="116" font-size="10" fill="var(--text-muted)" text-anchor="middle">컨트롤 하나</text>
<text x="198" y="116" font-size="10" fill="var(--text-muted)" text-anchor="middle">그 안의 부품</text>
<text x="266" y="86" font-size="11.5" fill="var(--text-muted)">Toggle 안의 글자 부분에만 걸립니다</text>
<rect x="28" y="152" width="126" height="36" rx="7" fill="var(--accent2)" fill-opacity="0.14" stroke="var(--accent2)"></rect>
<text x="91" y="175" font-size="12.5" fill="var(--text)" font-family="var(--font-mono)" text-anchor="middle">unity-button</text>
<rect x="154" y="152" width="112" height="36" rx="7" fill="var(--warn)" fill-opacity="0.16" stroke="var(--warn)"></rect>
<text x="210" y="175" font-size="12.5" fill="var(--text)" font-family="var(--font-mono)" text-anchor="middle">--with-icon</text>
<text x="91" y="206" font-size="10" fill="var(--text-muted)" text-anchor="middle">컨트롤 하나</text>
<text x="210" y="206" font-size="10" fill="var(--text-muted)" text-anchor="middle">그 컨트롤의 변형</text>
<text x="290" y="176" font-size="11.5" fill="var(--text-muted)">아이콘이 붙은 버튼일 때만 붙습니다</text>
</svg>
<figcaption>밑줄 두 개는 부품, 하이픈 두 개는 변형입니다.</figcaption>
</figure>

밑줄 두 개는 그 컨트롤 안의 부품이고, 하이픈 두 개는 그 컨트롤의 변형입니다. 웹에서 쓰는 BEM 방식 그대로입니다.

이게 왜 중요하냐면, **컨트롤 속을 건드릴 손잡이가 그 이름들뿐** 이기 때문입니다. 토글의 체크 표시만 키우고 싶으면 `.unity-toggle__checkmark` 에 겁니다. 하이어라키를 열어서 자식 오브젝트를 찾는 대신 이름을 아는 것이 방법입니다.

대신 조심할 것이 있습니다. uss 에 `.unity-button { ... }` 이라고 적으면 그 파일이 걸린 화면의 버튼이 **전부** 바뀝니다. 기본을 갈아 끼우는 셈이라 의도했으면 편하고 아니면 사고입니다. 특정 버튼만 바꿀 것이면 내 클래스를 하나 만들어서 거기에 적습니다.

## 둘 다 맞으면 무엇이 이깁니까

한 요소에 규칙이 여러 개 맞는 일은 드물지 않습니다. `.unity-button` 도 맞고 `.primary` 도 맞고 `#ok` 도 맞습니다.

<figure class="diagram">
<svg viewBox="0 0 680 312" role="img" aria-label="한 요소에 여러 규칙이 맞을 때 이기는 순서를 위에서 아래로 늘어놓은 그림">
<text x="24" y="24" font-size="13" fill="var(--text)">한 요소에 여러 규칙이 맞을 때</text>
<line x1="64" y1="268" x2="64" y2="50" stroke="var(--text-muted)" stroke-width="1.2"></line>
<polygon points="64,42 59,54 69,54" fill="var(--text-muted)"></polygon>
<text transform="rotate(-90 38 158)" x="38" y="158" font-size="10.5" fill="var(--text-muted)" text-anchor="middle">위가 이깁니다</text>
<rect x="96" y="44" width="420" height="32" rx="7" fill="var(--warn)" fill-opacity="0.18" stroke="var(--warn)"></rect>
<text x="112" y="64" font-size="11.5" fill="var(--text)">C# 에서 직접 넣은 값</text>
<text x="500" y="64" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">el.style.color = ...</text>
<rect x="96" y="83" width="420" height="32" rx="7" fill="var(--warn)" fill-opacity="0.13" stroke="var(--warn)"></rect>
<text x="112" y="103" font-size="11.5" fill="var(--text)">uxml 에 직접 적은 인라인</text>
<text x="500" y="103" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">style="color: red"</text>
<rect x="96" y="122" width="420" height="32" rx="7" fill="var(--accent2)" fill-opacity="0.18" stroke="var(--accent2)"></rect>
<text x="112" y="142" font-size="11.5" fill="var(--text)">이름 선택자</text>
<text x="500" y="142" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">#ok</text>
<rect x="96" y="161" width="420" height="32" rx="7" fill="var(--accent2)" fill-opacity="0.13" stroke="var(--accent2)"></rect>
<text x="112" y="181" font-size="11.5" fill="var(--text)">클래스 선택자</text>
<text x="500" y="181" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">.primary</text>
<rect x="96" y="200" width="420" height="32" rx="7" fill="var(--accent)" fill-opacity="0.14" stroke="var(--accent)"></rect>
<text x="112" y="220" font-size="11.5" fill="var(--text)">타입 선택자</text>
<text x="500" y="220" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">Button</text>
<rect x="96" y="239" width="420" height="32" rx="7" fill="var(--accent)" fill-opacity="0.08" stroke="var(--accent)"></rect>
<text x="112" y="259" font-size="11.5" fill="var(--text)">전체 선택자</text>
<text x="500" y="259" font-size="10.5" fill="var(--text-muted)" font-family="var(--font-mono)" text-anchor="end">*</text>
<text x="96" y="294" font-size="11.5" fill="var(--text-muted)">급이 같으면 파일에서 아래쪽에 적힌 것이 이깁니다</text>
</svg>
<figcaption>위쪽이 셉니다. 물려받은 값은 이 사다리와 상관없이 직접 걸린 값에 집니다.</figcaption>
</figure>

문서가 정한 순서가 있습니다.

- 코드에서 직접 넣은 값이 제일 셉니다. "Styles that you set in a C# script override USS styles and inline styles set in a UXML document."
- 그다음이 uxml 태그에 직접 적은 `style="..."` 입니다.
- uss 안에서는 이름, 클래스, 타입, 전체 순입니다.
- 급이 같으면 **파일에서 아래쪽에 적힌 것** 이 이깁니다.

여기에 한 줄이 더 붙는데, 실무에서 이게 자주 걸립니다. **직접 걸린 값이 물려받은 값보다 셉니다.** 문서 표현으로 "Styles that target an element directly take precedence over styles that the element inherits, even if the inherited styles are defined in a selector with higher specificity" 입니다. 글자색처럼 부모에서 물려받는 속성은, 부모 쪽 선택자가 아무리 구체적이어도 자식에 직접 걸린 한 줄에 집니다.

색이 안 바뀔 때 볼 순서는 셋입니다. uxml 에 `style=` 이 박혀 있는지, 더 센 선택자가 어디에 있는지, 같은 급 규칙이 파일 아래쪽에 또 있는지.

## 정리

- uss 규칙은 **선택자 + 값** 입니다. 표식이 없는 요소는 어느 규칙에도 안 걸립니다
- 부르는 방법은 타입 `Button`, 이름 `#ok`, 클래스 `.primary`, 전체 `*` 네 가지이고, 띄어쓰기와 `>` 로 이어 붙일 수 있습니다
- `#` 와 `.` 은 uss 에만 씁니다. uxml 의 `name` 과 `class` 에는 기호를 안 붙입니다
- 이름은 한 판에서 유일해야 하고, 클래스는 한 요소가 여러 개 달 수 있습니다. 재사용되는 쪽은 클래스입니다
- 상태는 `:hover` 처럼 정해진 이름으로 옵니다. 새로 만들 수 없어서 우리 쪽 상태는 클래스를 붙였다 뗍니다
- 유니티 컨트롤에는 `unity-button`, `unity-toggle__label` 같은 클래스가 이미 붙어 있습니다. 속을 건드리는 손잡이가 그 이름입니다
- 둘 다 맞으면 코드, 인라인, 이름, 클래스, 타입, 전체 순으로 이기고, 급이 같으면 파일 아래쪽이 이깁니다