---
title: "하이어라키 맨 아래가 화면 맨 앞이다"
description: "Figma 레이어 패널과 Unity 하이어라키는 앞뒤를 반대 방향으로 셉니다. 캔버스가 무엇이고, 무엇이 앞에 그려지는지 정하는 규칙을 봅니다."
author: "유니얼"
pubDate: 2026-09-10
category: "game-dev/unity/ui"
tags: ["Unity", "UI", "uGUI"]
canonical: https://blog.unialgames.com/posts/unity-ui-canvas-draw-order/
---
Figma 에서 팝업을 버튼 위로 올리려면 레이어 패널에서 팝업을 위로 끌어 올립니다.
Unity 에서 똑같이 하면 팝업이 버튼 뒤로 숨습니다.

**Unity 는 앞뒤를 반대 방향으로 셉니다.**
이 차이 하나로 시안과 실제 화면이 어긋나는 일이 생각보다 많습니다.
이 글은 Unity UI 가 무엇을 먼저 그리고 무엇을 나중에 그리는지, 그 규칙을 봅니다.

## Canvas 밖의 UI 는 보이지 않는다

버튼, 이미지, 텍스트 같은 UI 요소는 전부 **Canvas** 라는 오브젝트의 자식이어야 화면에 나옵니다.
Canvas 밖으로 꺼내면 오브젝트는 남아 있어도 그려지지 않습니다.

Figma 의 프레임과 비슷하다고 생각하고 출발해도 괜찮습니다. 다만 두 가지가 다릅니다.

- **크기를 직접 정하지 않습니다.** 대부분의 Canvas 는 게임 화면 크기에 저절로 맞춰집니다
- **그리는 단위입니다.** 한 Canvas 안의 요소들은 한 덩어리로 계산되고, 앞뒤 순서도 그 안에서 정해집니다

UI 요소를 처음 만들면 Unity 가 Canvas 와 함께 **EventSystem** 이라는 오브젝트를 만들어 줍니다.
클릭과 터치를 받는 오브젝트입니다. 쓸모없어 보여서 지우면 버튼이 하나도 안 눌립니다.

## Render Mode 세 가지

Canvas 에서 제일 먼저 정하는 것이 **Render Mode** 입니다.
UI 를 게임 화면과 어떤 방식으로 합칠지를 정합니다.

<figure class="diagram">
<svg viewBox="0 0 680 310" role="img" aria-label="Overlay 는 카메라 바깥 화면 유리에 UI 를 덮고, Camera 는 카메라 앞에 UI 판을 세우고, World Space 는 UI 가 씬 안의 물체 사이에 놓인다는 옆에서 본 그림">
<text x="20" y="51" font-size="12" font-family="var(--font-mono)" fill="var(--text)">Screen Space - Overlay</text>
<text x="20" y="69" font-size="12" fill="var(--text-muted)">게임을 다 그린 뒤 화면에 덮는다</text>
<text x="20" y="151" font-size="12" font-family="var(--font-mono)" fill="var(--text)">Screen Space - Camera</text>
<text x="20" y="169" font-size="12" fill="var(--text-muted)">카메라 앞에 UI 판을 세운다</text>
<text x="20" y="251" font-size="12" font-family="var(--font-mono)" fill="var(--text)">World Space</text>
<text x="20" y="269" font-size="12" fill="var(--text-muted)">씬 안의 물체가 된다</text>
<g stroke="var(--border)" stroke-dasharray="4 4" fill="none">
<line x1="296" y1="45" x2="660" y2="31"></line>
<line x1="296" y1="65" x2="660" y2="79"></line>
<line x1="296" y1="145" x2="660" y2="131"></line>
<line x1="296" y1="165" x2="660" y2="179"></line>
<line x1="296" y1="245" x2="660" y2="231"></line>
<line x1="296" y1="265" x2="660" y2="279"></line>
</g>
<g stroke="var(--text-muted)" fill="var(--bg)">
<rect x="266" y="48" width="16" height="14" rx="2"></rect>
<polygon points="282,51 296,45 296,65 282,59"></polygon>
<rect x="266" y="148" width="16" height="14" rx="2"></rect>
<polygon points="282,151 296,145 296,165 282,159"></polygon>
<rect x="266" y="248" width="16" height="14" rx="2"></rect>
<polygon points="282,251 296,245 296,265 282,259"></polygon>
</g>
<g stroke="var(--border)" fill="var(--bg)">
<rect x="420" y="45" width="20" height="20"></rect>
<rect x="560" y="45" width="20" height="20"></rect>
<rect x="480" y="145" width="20" height="20"></rect>
<rect x="580" y="145" width="20" height="20"></rect>
<rect x="380" y="245" width="20" height="20"></rect>
<rect x="560" y="245" width="20" height="20"></rect>
</g>
<g fill="var(--warn)">
<circle cx="500" cy="55" r="5"></circle>
<circle cx="340" cy="155" r="5"></circle>
</g>
<g stroke="var(--accent)" stroke-width="4">
<line x1="244" y1="22" x2="244" y2="88"></line>
<line x1="380" y1="142" x2="380" y2="168"></line>
<line x1="470" y1="245" x2="470" y2="265"></line>
</g>
<text x="244" y="104" font-size="11" text-anchor="middle" fill="var(--accent)">UI</text>
<text x="380" y="130" font-size="11" text-anchor="middle" fill="var(--accent)">UI</text>
<text x="470" y="232" font-size="11" text-anchor="middle" fill="var(--accent)">UI</text>
<text x="660" y="97" font-size="11" text-anchor="end" fill="var(--text-muted)">이펙트(점)는 UI 앞으로 못 나온다</text>
<text x="660" y="197" font-size="11" text-anchor="end" fill="var(--text-muted)">이펙트(점)를 UI 앞에 둘 수 있다</text>
<text x="660" y="297" font-size="11" text-anchor="end" fill="var(--text-muted)">UI 가 물체를 가리고, 물체에 가려진다</text>
</svg>
<figcaption>옆에서 본 그림입니다. 왼쪽이 화면을 보는 사람 쪽입니다. Overlay 만 UI 가 카메라 바깥에 있습니다.</figcaption>
</figure>

**Screen Space - Overlay.** 기본값입니다.
카메라가 게임 화면을 다 그린 뒤, 그 위에 UI 를 덮습니다. 유리창에 스티커를 붙이는 것과 같습니다.
게임 속 어떤 물체도 이 UI 앞으로 나올 수 없습니다. HUD 와 메뉴는 대부분 이 모드입니다.

**Screen Space - Camera.** 지정한 카메라 앞, 정해 둔 거리(Plane Distance)에 UI 판을 세웁니다.
화면에 꽉 차게 보이는 것은 Overlay 와 같지만, 이제 **UI 도 카메라가 찍는 물체 중 하나** 입니다.
그래서 이펙트나 3D 캐릭터를 UI 앞에 두거나 UI 사이에 끼울 수 있습니다.
대신 카메라 설정의 영향을 받습니다. 원근 카메라라면 UI 를 기울였을 때 원근이 생깁니다.

**World Space.** Canvas 가 씬 안의 평범한 물체가 됩니다.
크기와 위치를 직접 정하고, 앞에 있는 물체에 가려지기도 합니다.
캐릭터 머리 위 이름표, 게임 속 모니터 화면, VR 메뉴가 이 모드입니다.

| | Overlay | Camera | World Space |
| --- | --- | --- | --- |
| 화면 크기에 맞춰지나 | 예 | 예 | 아니오. 직접 정합니다 |
| 게임 물체가 UI 앞으로 나올 수 있나 | **없음** | 있음 | 있음 |
| 캔버스끼리 앞뒤를 정하는 값 | Sort Order | Sorting Layer, Order in Layer | Sorting Layer, Order in Layer, 거리 |
| 주로 쓰는 곳 | HUD, 메뉴, 팝업 | UI 사이에 이펙트나 3D 가 끼는 화면 | 이름표, 체력바, 게임 속 화면 |

디자이너 쪽에서 기억할 것은 하나입니다.
**"UI 앞에 이펙트나 3D 가 올라오는 연출이 있는가"** 가 Render Mode 를 정합니다.
이게 개발 중간에 나오면 캔버스 설정을 바꿔야 하고, 그러면 이미 만든 화면을 전부 다시 확인해야 합니다.

## 한 캔버스 안에서는 하이어라키 순서

Unity 문서는 이렇게 적고 있습니다.
UI 요소는 **하이어라키에 적힌 순서대로** 그려집니다. 첫 번째 자식을 먼저, 두 번째를 그다음에 그립니다.
겹치면 **나중에 그린 것이 위에** 보입니다.

그림을 칠하는 순서로 생각하면 자연스럽습니다. 먼저 칠한 위를 나중에 칠한 것이 덮습니다.
하이어라키는 위에서 아래로 읽으니 **아래에 있을수록 나중에 칠해지고, 나중에 칠할수록 앞에 보입니다.**

Figma 와 Photoshop 의 레이어 패널은 반대입니다. **목록 맨 위가 맨 앞** 입니다.

<figure class="diagram">
<svg viewBox="0 0 680 240" role="img" aria-label="Figma 레이어 패널은 팝업 버튼 배경 순서로 위가 앞이고, Unity 하이어라키는 배경 버튼 팝업 순서로 아래가 앞인데 화면 결과는 같다는 그림">
<text x="20" y="30" font-size="13" fill="var(--text)">Figma 레이어 패널</text>
<text x="250" y="30" font-size="13" fill="var(--text)">화면</text>
<text x="480" y="30" font-size="13" fill="var(--text)">Unity 하이어라키</text>
<g stroke="var(--border)" fill="var(--bg)">
<rect x="20" y="46" width="180" height="28" rx="3"></rect>
<rect x="20" y="116" width="180" height="28" rx="3"></rect>
<rect x="20" y="151" width="180" height="28" rx="3"></rect>
<rect x="480" y="46" width="180" height="28" rx="3"></rect>
<rect x="480" y="81" width="180" height="28" rx="3"></rect>
<rect x="480" y="116" width="180" height="28" rx="3"></rect>
</g>
<g stroke="var(--accent)" fill="var(--accent-soft)">
<rect x="20" y="81" width="180" height="28" rx="3"></rect>
<rect x="480" y="151" width="180" height="28" rx="3"></rect>
</g>
<text x="34" y="65" font-size="13" fill="var(--text-muted)">Frame</text>
<text x="50" y="100" font-size="13" fill="var(--text)">팝업</text>
<text x="50" y="135" font-size="13" fill="var(--text)">버튼</text>
<text x="50" y="170" font-size="13" fill="var(--text)">배경</text>
<text x="494" y="65" font-size="13" fill="var(--text-muted)">Canvas</text>
<text x="510" y="100" font-size="13" fill="var(--text)">배경</text>
<text x="510" y="135" font-size="13" fill="var(--text)">버튼</text>
<text x="510" y="170" font-size="13" fill="var(--text)">팝업</text>
<rect x="250" y="46" width="180" height="133" rx="3" stroke="var(--border)" fill="var(--bg)"></rect>
<text x="262" y="66" font-size="12" fill="var(--text-muted)">배경</text>
<rect x="266" y="140" width="96" height="28" rx="3" stroke="var(--text-muted)" fill="var(--bg)"></rect>
<text x="280" y="159" font-size="12" fill="var(--text-muted)">버튼</text>
<rect x="318" y="72" width="100" height="84" rx="3" stroke="var(--accent)" fill="var(--accent-soft)"></rect>
<text x="332" y="92" font-size="12" fill="var(--accent)">팝업</text>
<text x="20" y="210" font-size="12" fill="var(--text-muted)">목록 맨 위가 맨 앞</text>
<text x="250" y="210" font-size="12" fill="var(--text-muted)">팝업이 버튼을 가린다</text>
<text x="480" y="210" font-size="12" fill="var(--text-muted)">목록 맨 아래가 맨 앞</text>
</svg>
<figcaption>보이는 결과는 같습니다. 목록을 읽는 방향만 반대입니다.</figcaption>
</figure>

### 순서는 형제끼리만 바뀐다

하이어라키는 나무 모양이라 순서가 한 줄로 쭉 이어지지 않습니다.
**부모를 먼저 그리고, 그 자식들을 전부 그린 뒤에야 다음 형제로 넘어갑니다.**

<figure class="diagram">
<svg viewBox="0 0 680 210" role="img" aria-label="상점 창 1, 구매 버튼 2, 보상 팝업 3, 확인 버튼 4 순서로 그려져 구매 버튼이 보상 팝업에 가려지는 그림">
<text x="20" y="40" font-size="13" fill="var(--text-muted)">Canvas</text>
<text x="40" y="75" font-size="13" fill="var(--text)">상점 창 (이미지)</text>
<text x="60" y="110" font-size="13" fill="var(--text)">구매 버튼</text>
<text x="40" y="145" font-size="13" fill="var(--text)">보상 팝업 (이미지)</text>
<text x="60" y="180" font-size="13" fill="var(--text)">확인 버튼</text>
<g stroke="var(--accent)" fill="var(--bg)">
<circle cx="250" cy="70" r="11"></circle>
<circle cx="250" cy="105" r="11"></circle>
<circle cx="250" cy="140" r="11"></circle>
<circle cx="250" cy="175" r="11"></circle>
</g>
<g font-size="12" text-anchor="middle" fill="var(--accent)">
<text x="250" y="74">1</text>
<text x="250" y="109">2</text>
<text x="250" y="144">3</text>
<text x="250" y="179">4</text>
</g>
<rect x="360" y="30" width="180" height="166" rx="3" stroke="var(--border)" fill="var(--bg)"></rect>
<text x="372" y="50" font-size="12" fill="var(--text-muted)">1 상점 창</text>
<rect x="400" y="160" width="170" height="30" rx="3" stroke="var(--warn)" fill="var(--bg)"></rect>
<text x="412" y="180" font-size="12" fill="var(--warn)">2 구매 버튼</text>
<rect x="500" y="62" width="150" height="114" rx="3" stroke="var(--accent)" fill="var(--accent-soft)"></rect>
<text x="512" y="82" font-size="12" fill="var(--accent)">3 보상 팝업</text>
<rect x="526" y="126" width="98" height="28" rx="3" stroke="var(--accent)" fill="var(--bg)"></rect>
<text x="538" y="145" font-size="12" fill="var(--accent)">4 확인 버튼</text>
</svg>
<figcaption>번호가 그리는 순서입니다. 구매 버튼이 보상 팝업에 가려집니다.</figcaption>
</figure>

구매 버튼을 상점 창 안에서 맨 아래로 내려도 2 는 3 보다 앞으로 못 나옵니다.
구매 버튼을 보상 팝업보다 앞에 보이게 하려면 방법이 둘뿐입니다.
상점 창과 보상 팝업의 순서를 바꾸거나, 구매 버튼을 상점 창 밖으로 꺼내 부모를 바꾸는 것입니다.
Figma 에서 한 그룹의 레이어를 다른 그룹의 레이어 사이에 끼울 수 없는 것과 같은 규칙입니다.

여기서 하나가 더 따라 나옵니다. **자식은 부모 뒤로 갈 수 없습니다.**
부모 이미지를 먼저 칠하니 자식은 늘 그 위에 있습니다.
창 테두리나 액자처럼 **내용물 위에 겹쳐야 하는 장식** 은 부모 이미지에 같이 그려 두면 안 됩니다.
따로 잘라서 마지막 자식으로 둬야 내용물 위로 올라옵니다.

## 캔버스끼리는 숫자로 정한다

캔버스가 둘 이상이면, 캔버스 사이의 앞뒤는 하이어라키 순서가 아니라 **Canvas 에 적은 숫자** 로 정합니다.
Overlay 모드에서는 **Sort Order** 이고, 숫자가 클수록 앞입니다.
숫자가 같으면 어느 쪽이 앞일지 장담할 수 없으니, 겹칠 수 있는 캔버스는 숫자를 다르게 줍니다.

보통 이런 식으로 층을 나눕니다.

| 캔버스 | Sort Order 예 | 담는 것 |
| --- | --- | --- |
| HUD | 0 | 체력바, 미니맵, 스킬 버튼 |
| 메뉴 | 10 | 인벤토리, 상점 |
| 팝업 | 20 | 확인창, 보상창 |
| 토스트 | 30 | 획득 알림, 네트워크 경고 |
| 시스템 | 100 | 로딩 화면, 화면 전환 페이드 |

하이어라키 순서만으로도 겹치게 만들 수는 있습니다. 그래도 캔버스를 나누는 이유가 있습니다.

- **팝업은 어느 화면에서 뜨든 위에 있어야 합니다.** 화면 안에 넣어 두면 그 화면 안의 순서에 묶입니다
- **숫자 사이를 비워 두면** (0, 10, 20) 나중에 층이 하나 늘어도 기존 캔버스를 안 건드립니다
- **성능 때문이기도 합니다.** 캔버스 안에서 요소 하나가 바뀌면 그 캔버스 전체를 다시 계산합니다.
  매 프레임 숫자가 바뀌는 HUD 와 가만히 있는 메뉴를 한 캔버스에 두면 메뉴까지 매번 다시 계산됩니다

캔버스 안에 캔버스를 넣고 **Override Sorting** 을 켜면, 그 캔버스는 부모와 상관없이 자기 숫자를 가집니다.
튜토리얼에서 화면 전체를 어둡게 덮고 **버튼 하나만 딤 위로 꺼내 보여 줄 때** 이렇게 합니다.
그 버튼을 딤보다 앞으로 옮기려고 부모를 바꾸면 버튼 위치와 레이아웃이 깨지기 때문입니다.

Camera 와 World Space 모드에서는 Sort Order 대신 **Sorting Layer** 와 **Order in Layer** 가 나옵니다.
캔버스가 스프라이트, 파티클과 같은 줄에 서서 순서를 겨루기 때문입니다.
Sorting Layer 를 먼저 비교하고, 같은 레이어 안에서는 Order in Layer 가 큰 쪽이 앞입니다.

## 이펙트를 UI 사이에 끼우기 어려운 이유

이런 연출 요청이 자주 나옵니다.
"보상 팝업 뒤에서 빛이 퍼지고, 그 앞에 아이템 아이콘이 있고, 아이콘 위로 반짝이가 튀었으면 좋겠다."

시안에서는 레이어 넷입니다. Unity 에서는 이게 까다롭습니다. 이유가 둘입니다.

**하나. Overlay 캔버스 위에는 아무것도 못 올라갑니다.**
파티클은 카메라가 찍는 물체이고, Overlay 는 카메라가 다 찍은 뒤에 덮습니다.
캔버스의 Sort Order 를 아무리 낮추고 파티클의 순서를 아무리 올려도, 파티클은 Overlay UI 뒤에 있습니다.

**둘. 한 캔버스 안에는 파티클이 끼어들 틈이 없습니다.**
캔버스는 한 덩어리로 순서를 받습니다. 파티클은 캔버스 전체의 앞이나 뒤에는 설 수 있어도,
캔버스 안의 이미지 두 장 사이에는 설 수 없습니다.

그래서 Camera 모드로 두고, **파티클이 끼어야 하는 경계마다 캔버스를 쪼갭니다.**

<figure class="diagram">
<svg viewBox="0 0 680 196" role="img" aria-label="팝업 캔버스 순서 10, 빛 파티클 11, 아이콘 캔버스 12, 반짝이 파티클 13 으로 캔버스와 파티클이 번갈아 쌓이는 그림">
<g stroke="var(--border)" fill="var(--bg)">
<rect x="40" y="30" width="300" height="30" rx="2"></rect>
<rect x="40" y="110" width="300" height="30" rx="2"></rect>
</g>
<g stroke="var(--warn)" fill="var(--bg)" stroke-dasharray="4 3">
<rect x="40" y="70" width="300" height="30" rx="2"></rect>
<rect x="40" y="150" width="300" height="30" rx="2"></rect>
</g>
<text x="54" y="50" font-size="12" fill="var(--text)">팝업 캔버스: 팝업 배경</text>
<text x="54" y="90" font-size="12" fill="var(--warn)">파티클: 뒤에서 퍼지는 빛</text>
<text x="54" y="130" font-size="12" fill="var(--text)">아이콘 캔버스: 아이템 아이콘</text>
<text x="54" y="170" font-size="12" fill="var(--warn)">파티클: 반짝이</text>
<g font-size="12" font-family="var(--font-mono)" fill="var(--accent)">
<text x="360" y="50">Order 10</text>
<text x="360" y="90">Order 11</text>
<text x="360" y="130">Order 12</text>
<text x="360" y="170">Order 13</text>
</g>
<text x="470" y="50" font-size="12" fill="var(--text-muted)">뒤</text>
<line x1="476" y1="60" x2="476" y2="150" stroke="var(--text-muted)"></line>
<polygon points="471,150 481,150 476,160" fill="var(--text-muted)"></polygon>
<text x="470" y="178" font-size="12" fill="var(--text-muted)">앞</text>
<text x="510" y="90" font-size="12" fill="var(--text-muted)">파티클이 한 번 끼면</text>
<text x="510" y="108" font-size="12" fill="var(--text-muted)">캔버스가 한 번 쪼개집니다</text>
</svg>
<figcaption>레이어 넷이면 캔버스 둘, 파티클 둘입니다. 끼어드는 이펙트가 늘수록 캔버스가 늘어납니다.</figcaption>
</figure>

아이콘 캔버스는 팝업 캔버스 안에 넣고 **Override Sorting** 을 켭니다.
위치는 팝업을 따라가고, 순서만 따로 갖습니다.

캔버스가 늘어나는 게 부담스러우면 다른 길도 있습니다.
**파티클을 UI 요소처럼 그려 주는 플러그인** 을 쓰면 파티클이 하이어라키 순서를 따릅니다.
이펙트를 **스프라이트 시퀀스 애니메이션** 으로 만들어 UI 이미지로 넣는 방법도 있습니다.
어느 쪽이든 개발 중간에 바꾸기는 번거로우니 연출 시안이 일찍 나올수록 좋습니다.

## 시안을 넘길 때 적어 주면 좋은 것

앞의 규칙들을 거꾸로 보면, 개발자가 시안에서 찾아야 하는 정보가 됩니다.

| 적어 줄 것 | 무엇이 정해지나 |
| --- | --- |
| HUD, 메뉴, 팝업, 토스트처럼 **화면 위에 뜨는 층** | 캔버스를 몇 개로 나눌지 |
| **동시에 뜰 때 무엇이 위인지** (팝업 위 팝업, 팝업을 가리는 토스트) | 캔버스의 Sort Order |
| 이펙트나 3D 캐릭터가 **UI 앞에, 뒤에, 사이에** 오는지 | Render Mode, 캔버스 쪼개기 |
| 테두리처럼 **내용물 위에 겹치는 장식** | 이미지를 따로 자를지 |
| 튜토리얼에서 **딤 위로 꺼내 보여 줄 요소** | Override Sorting 캔버스 |

## 정리

- UI 는 Canvas 안에 있어야 보이고, Canvas 는 한 덩어리로 그려지는 단위입니다
- Render Mode 는 UI 를 게임 화면과 합치는 방식입니다. **UI 앞에 이펙트나 3D 가 와야 하면 Overlay 로는 안 됩니다**
- 한 캔버스 안에서는 **하이어라키 아래쪽이 앞** 입니다. Figma 와 Photoshop 과 반대입니다
- 순서는 형제끼리만 바뀌고, 자식은 부모 뒤로 가지 못합니다
- 캔버스끼리는 숫자로 정합니다. Overlay 는 Sort Order, 나머지는 Sorting Layer 와 Order in Layer 입니다
- 파티클을 UI 사이에 끼우려면 **끼어드는 경계마다 캔버스를 쪼개야** 합니다