> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-ko/sdk7/2d-ui/onscreen-ui.md).

# 화면 UI

씬에서 플레이어를 위한 UI를 만드는 방법을 알아보세요. 예를 들어 게임 관련 정보를 표시할 때 유용합니다.

장면을 위한 UI를 만들어 3D 월드 공간이 아니라 화면의 고정된 2D 공간에 표시할 수 있습니다.

UI 요소는 플레이어가 장면의 LAND 구역 안에 서 있을 때만 보입니다. 인접한 장면이 자체 UI를 표시할 수 있기 때문입니다. UI의 일부는 월드 공간에서 특정 이벤트가 발생할 때 열리도록 트리거할 수도 있습니다. 예를 들어 플레이어가 특정 위치를 클릭할 때입니다.

중첩된 구조를 정의하여 UI를 만듭니다 `UiEntity` JSX의 객체입니다. UI에 사용되는 구문은 다음과 매우 유사합니다 [React](https://reactjs.org/) (웹 UI 구축을 위한 매우 인기 있는 JavaScript 기반 라이브러리).

{% hint style="warning" %}
**📔 참고**: UI 구문은 다음 확장자를 가진 파일에서만 정의할 수 있습니다 `.tsx` 확장자입니다. `.tsx` 파일은 다음이 지원하는 모든 것을 지원하며 `.ts` 파일은 다음이 지원하는 모든 것을 지원하며, UI 구문도 지원합니다. 우리는 다음을 만드는 것을 권장합니다 `ui.tsx` 파일을 만들고 그곳에 UI를 정의하세요. UI 렌더 메서드는 다음에서 호출하는 것을 잊지 마세요 `index.ts` 와 함께 `ReactEcsRenderer.setUiRenderer(yourUiMethodName)`, 아래 예를 참조하세요.
{% endhint %}

정적인 요소로 이루어진 단순한 UI는 HTML과 매우 비슷해 보일 수 있지만, 상태 변화에 반응하는 동적 요소를 추가하면 훨씬 더 강력한 작업을 할 수 있습니다.

기본 Decentraland 익스플로러 UI에는 채팅 위젯, 지도 및 기타 요소가 포함됩니다. 이러한 UI 요소는 항상 최상위 레이어에 표시되며, 어떤 장면 전용 UI보다 위에 놓입니다. 따라서 장면에 이들과 같은 화면 공간을 차지하는 UI 요소가 있으면 가려집니다.

다음 [UX 가이드라인](/creator/content-creator-ko/sdk7/designing-the-experience/ux-ui-guide.md) UI의 외형과 사용감을 설계하는 팁을 보려면

{% hint style="info" %}
**📱 모바일용 디자인**장치에는 노치, 상태 표시줄, 홈 인디케이터, 둥근 모서리를 위한 화면 공간이 예약되어 있으며, 클라이언트는 캔버스의 일부 위에 자체 컨트롤을 그립니다. 이 둘을 따로 측정할 필요는 없습니다. 대신 UI가 배치될 영역을 다음으로 선택하세요 [`screenInset`](#screen-inset-area) 아래의 설정을 사용하세요. 기본값은 기기 안전 영역이므로 별도 작업 없이 하드웨어 여백을 피할 수 있습니다. 게시하기 전에 다음을 검토하세요 [모바일 안전 영역](/creator/content-creator-ko/build-for-mobile/develop/safe-area.md) 와 [모바일용 UI 모범 사례](/creator/content-creator-ko/build-for-mobile/develop/ui-best-practices.md).
{% endhint %}

플레이어가 다음을 클릭하면 *UI 닫기* 버튼을 화면 오른쪽 아래 모서리에서 누르면 모든 UI 요소가 숨겨집니다.

## UI 렌더링하기

장면에 UI를 표시하려면 다음을 사용하세요 `ReactEcsRenderer.setUiRenderer()` 함수에 유효한 엔티티 구조를 전달합니다. 이는 다음에서 설명합니다 `.tsx` 파일을 추가할 수 있습니다.

각 엔티티는 HTML과 유사한 노드로 정의되며, 각 컴포넌트에 대한 속성을 가집니다.

***ui.tsx 파일:***

```ts
import { UiEntity, ReactEcs } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
  <UiEntity
    uiTransform={{
      width: 700,
      height: 400,
      margin: { top: '35px', left: '500px' },
    }}
    uiBackground={{ color: Color4.Red() }}
  />
)
```

***index.ts 파일:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

엔티티 구조를 정의하고 렌더링하는 작업도 하나의 명령으로 한 번에 수행할 수 있습니다 `.tsx` 파일을 추가할 수 있습니다.

***ui.tsx 파일:***

```tsx
import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export function setupUI() {
  ReactEcsRenderer.setUiRenderer(() => (
    <UiEntity
      uiTransform={{
        width: 700,
        height: 400,
        margin: { top: '35px', left: '500px' },
      }}
      uiBackground={{ color: Color4.Red() }}
    />
  ), { virtualWidth: 1920, virtualHeight: 1080 })
}
```

***index.ts 파일:***

```ts
import { setupUI } from './ui'

export function main() {
    setupUI()
}
```

{% hint style="warning" %}
**📔 참고**: 모든 UI 요소는 하나의 동일한 구조 안에 중첩되어야 하며, 구조의 최상위에는 하나의 단일 부모가 있어야 합니다. 다음은 하나만 호출할 수 있습니다 `ReactEcsRenderer.setUiRenderer()` 장면에서 한 번만.
{% endhint %}

## UI 엔티티

UI의 각 요소는 별도의 `UiEntity`, 이미지, 텍스트, 배경, 보이지 않는 정렬 상자 등 무엇이든 상관없습니다. 장면의 3D 공간에서처럼 각 `UiEntity` 에는 위치, 색상 등을 지정하는 고유한 컴포넌트가 있습니다.

React와 유사한 구문을 사용하면 각 컴포넌트를 다음 내부의 속성으로 지정할 수 있습니다 `UiEntity`, 이렇게 하면 코드가 더 짧고 읽기 쉬워집니다.

다음에서 사용되는 컴포넌트는 `UiEntity` 일반 엔티티에 사용되는 것과 다릅니다. UI 컴포넌트를 일반 엔티티에 적용할 수도 없고, 일반 컴포넌트를 UI 엔티티에 적용할 수도 없습니다.

다음 컴포넌트를 다음에서 사용할 수 있습니다 `UiEntity`:

* `uiTransform`
* `uiBackground`
* `uiText`
* `onMouseDown`
* `onMouseUp`
* `onMouseEnter`
* `onMouseLeave`
* [`uiInputBinding`](/creator/content-creator-ko/sdk7/2d-ui/ui_input_binding.md) — 요소가 눌려 있는 동안 입력 동작을 유지하여 사용자 지정 컨트롤을 만듭니다

HTML 태그처럼 컴포넌트를 자기 닫기 형식으로 정의하거나 서로 중첩할 수 있습니다.

{% hint style="danger" %}
**경고:** 전체 화면 레이아웃 래퍼에는 포인터 핸들러를 두지 마세요. 다음 중 하나라도 추가하면 `onMouse` 핸들러, 또는 `pointerFilter: 'block'`, 다음에 `UiEntity` 크기가 지정된 `100%` 다음인 전체 화면 래퍼에 `100%` 화면의 모든 클릭을 가로채게 되어 다른 모든 UI와 전체 3D 월드를 클릭할 수 없게 됩니다. 대신 필요한 패널이나 버튼에 핸들러를 추가하세요. 다음을 참조하세요 [포인터 차단](/creator/content-creator-ko/sdk7/2d-ui/ui_button_events.md#pointer-blocking).
{% endhint %}

***ui.tsx 파일:***

```tsx
import ReactEcs, { ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
  // 부모 엔티티
  <UiEntity
    uiTransform={{
      width: 200,
      height: 200,
      margin: { top: '250px', left: '500px' },
    }}
    uiBackground={{ color: Color4.Blue() }}
  >
    {/* 자기 닫기 자식 엔티티 */}
    <UiEntity
      uiTransform={{
        width: 400,
        height: 400,
        margin: { top: '35px', left: '500px' },
      }}
      uiText={{ value: `Hello world!`, fontSize: 40 }}
    />
    {/* 부모 엔티티의 닫는 구문 */}
  </UiEntity>
)
```

***index.ts 파일:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { uiMenu } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

UI 모듈의 정의는 부모 수준 엔티티를 하나만 가질 수 있습니다. 다른 엔티티는 원하는 만큼 정의할 수 있지만, 모두 최상단에 하나의 부모가 있는 구조 안에 들어가야 합니다.

## 화면 가상 스케일

UI의 가상 너비와 높이를 설정하세요. 이렇게 하면 실제 픽셀 화면 크기와 상관없이 다른 화면 크기에서도 UI가 동일하게 보입니다.

```ts
export function setupUi() {
    ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
}
```

가상 너비를 1920, 가상 높이를 1080으로 설정하면 UI는 화면 크기에 맞게 조정됩니다. 화면이 1920x1080이면 UI는 가상 크기와 같은 크기로 표시됩니다. 화면이 더 크거나 작으면 모든 픽셀 값이 가상 크기에 맞게 스케일됩니다. 예를 들어 화면이 3840x2160이면 너비가 100픽셀로 정의된 항목은 실제 200픽셀로 표시됩니다.

픽셀 값에 곱해지는 UI 스케일 팩터의 실제 계산식은 [`Math.min(realWidth / virtualWidth, realHeight / virtualHeight)`](https://github.com/decentraland/js-sdk-toolchain/blob/main/packages/%40dcl/react-ecs/src/system.ts).

### 기본 가상 크기

가상 크기는 선택 사항이지만 두 차원은 함께 사용해야 합니다. 둘 다 전달하거나 둘 다 전달하지 않아야 합니다. 하나도 전달하지 않으면 플랫폼 기본값이 적용됩니다:

| 플랫폼      | 기본 가상 크기    |
| -------- | ----------- |
| 모바일      | `1600x720`  |
| 데스크톱 및 웹 | `1920x1080` |

즉, 옵션을 전혀 전달하지 않아도 UI의 픽셀 값은 항상 기준 해상도에 맞춰 스케일됩니다. 이는 장면이 다음만 사용하는 경우에도 적용됩니다 `addUiRenderer()` 그리고 다음을 한 번도 호출하지 않을 때도 `setUiRenderer()`.

세 가지 특별한 경우:

* **스케일링 제외하기**: 잘못된 크기를 전달하세요 — 다음 값이면 됩니다 `0` 또는 그보다 작은 값 — 을 전달하면 가상 화면이 완전히 비활성화됩니다. 그러면 픽셀 값이 스케일링 없이 원시 캔버스 픽셀로 사용됩니다. 이것이 가상 화면을 끄는 문서화된 방법이므로 아무것도 로그에 기록되지 않습니다.

  ```ts
  ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 0, virtualHeight: 0 })
  ```
* **불완전한 크기**: 두 차원 중 하나만 주는 것도 유효하지 않으므로, 위의 경우처럼 가상 화면을 비활성화합니다 — 플랫폼 기본값이 **아닙니다** 대신 적용됩니다. 위의 옵트아웃과 달리 이것은 보고됩니다. 반만 지정한 크기는 의도된 선택이 아니라 실수이므로 SDK가 로그를 남깁니다 `불완전한 가상 화면 크기(…): 두 차원이 모두 필요하므로 가상 화면이 비활성화되고 UI 스케일링이 적용되지 않습니다.` 크기당 한 번씩. 두 차원을 모두 전달하거나 둘 다 전달하지 마세요.

  ```ts
  // 이렇게 하지 마세요 — 스케일링이 전혀 적용되지 않습니다
  ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920 })
  ```

  이는 메인 렌더러에서 특히 다시 확인할 가치가 있습니다. 그곳의 불완전한 크기 또한 장면 전체에 적용되는 스위치이기 때문입니다: `setUiRenderer()` 다음을 언급하는 호출은 *둘 중 하나의* 차원이 다른 어떤 것보다 우선합니다 `addUiRenderer()`, 따라서 위 예시는 장면 전체의 가상 화면을 비활성화하고 추가 렌더러에 전달된 유효한 크기를 버립니다.
* **모바일에서의 16:9 크기**: 휴대폰 화면은 16:9보다 훨씬 더 넓기 때문에 16:9 가상 캔버스는 UI에 레터박스를 적용하게 됩니다. 16:9 크기(`1920x1080`, `1280x720`, …)를 전달하고 장면이 모바일에서 실행되면, 이는 다음으로 덮어씌워집니다 `1600x720`, 그리고 메시지가 콘솔에 한 번 기록됩니다. 모바일에서 16:9가 아닌 크기와 데스크톱 또는 웹에서의 모든 유효한 크기는 그대로 존중됩니다.

{% hint style="info" %}
**📔 참고**: `vw` 과 `vh` 단위는 가상 화면과 무관합니다: `1vw` 는 캔버스 너비의 1%이고 `1vh` 는 캔버스 높이의 1%입니다. CSS와 정확히 같습니다. 또한 다음도 무시합니다 [`screenInset`](#screen-inset-area), 따라서 기본 `'device'` 기본 삽입 영역 안에서는 `width: '100vw'` 는 다음보다 더 넓습니다 `width: '100%'` — 첫 번째는 화면 전체를 차지하고, 두 번째는 삽입 영역을 채웁니다.
{% endhint %}

{% hint style="info" %}
**📱 참고**: 플랫폼 감지는 비동기적으로 해결됩니다. 첫 프레임 동안 SDK는 아직 모바일에서 실행 중인지 알지 못하므로, 가상 화면은 다음 값으로 시작합니다 `1920x1080` 그리고 다음으로 전환됩니다 `1600x720` 감지가 완료되면 — 로드 시 UI가 잠시 다시 스케일되는 것을 볼 수 있으며, 장면이 시작된 직후 콘솔에 16:9 재정의 메시지가 표시됩니다. 첫 프레임부터 하나의 안정적인 기준 해상도를 원한다면 명시적인 16:9가 아닌 가상 크기를 전달하세요.
{% endhint %}

{% hint style="warning" %}
**📔 이 SDK 버전 이전에 작성된 장면에서 변경된 점**

세 가지 변경 사항이 기존 장면에 영향을 줍니다. 이들은 모두 선택 사항이 아니므로, 전혀 손대지 않은 장면도 SDK 버전을 업데이트하면 모습이 달라집니다:

1. **이제 가상 화면이 기본적으로 적용됩니다.** 옵션을 전달하지 않은 장면은 이전에는 픽셀 값을 원시 캔버스 픽셀로 배치했습니다. 이제는 다음을 기준으로 스케일됩니다 `1920x1080` (`1600x720` 모바일에서는). `setUiRenderer(ui, { virtualWidth: 0, virtualHeight: 0 })`.
2. **`screenInset` 기본값은 `'device'`.** 이제 UI는 기기 안전 영역 안에 배치되므로 모바일에서는 안쪽으로 이동하며, 루트 수준의 `100%` 는 더 이상 전체 화면이 아닙니다 — 전체 화면 배경, 디머, 오버레이는 노치와 홈 인디케이터 바로 전에서 멈춥니다. 이전 동작으로 되돌리려면 다음을 전달하세요 `setUiRenderer(ui, { screenInset: 'none' })`. 참고: [화면 인셋 영역](#screen-inset-area).
3. **`devicePixelRatio` UI 레이아웃에 관여하지 않습니다.** 픽셀 크기의 UI는 이제 고밀도(레티나 및 모바일) 화면에서 이전보다 최대 2\~3배 더 크게 표시됩니다. **이것은 비활성화할 수 없습니다** — 수동으로 조정한 크기를 다시 확인하고, 장면이 자체적으로 계산한 스케일 팩터를 제거하세요.
   {% endhint %}

## 화면 인셋 영역

화면은 완전히 사용 가능한 상태가 아닙니다. 휴대폰은 노치, 상태 표시줄, 홈 인디케이터, 둥근 모서리를 위한 공간을 예약하며, 각 익스플로러는 캔버스 일부 위에 자체 HUD(미니맵, 채팅, …)를 그립니다. 선택 가능한 `screenInset` 렌더러 옵션의 속성은 UI가 배치될 화면 영역을 선택합니다:

| 값                  | UI가 배치되는 영역                                                                                                                                     |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `'device'` *(기본값)* | 노치, 상태 표시줄, 둥근 모서리를 제외한 기기 안전 영역입니다. 다음에서 읽습니다 `UiCanvasInformation.screenInsetArea`.                                                           |
| `'interactable'`   | 익스플로러가 자체 HUD(미니맵, 채팅, …)가 없는 영역이라고 보고하는 영역입니다. 다음에서 읽습니다 `UiCanvasInformation.interactableArea`. 무엇을 포함하는지는 각 익스플로러에 달려 있습니다 — 대상 플랫폼에서 확인하세요. |
| `'none'`           | 전체 화면이며, `0,0` 좌측 상단에 위치합니다.                                                                                                                    |

```ts
// UI는 노치, 상태 표시줄, 둥근 모서리에서 떨어져 유지됩니다 — 이것이 기본값입니다
ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })

// UI는 익스플로러 자체 HUD와 겹치지 않게 유지됩니다
ReactEcsRenderer.setUiRenderer(uiComponent, { screenInset: 'interactable' })

// UI가 화면 전체를 덮습니다. 여백은 직접 처리하세요
ReactEcsRenderer.setUiRenderer(uiComponent, { screenInset: 'none' })
```

데스크톱에서는 기기 삽입 영역이 0이므로 `'device'` 그곳에서는 UI가 화면 전체에 배치됩니다 — 다음과 같은 결과입니다 `'none'`. 영역은 매 틱마다 다시 읽히므로, 회전하거나 시스템 바가 나타나고 사라질 때처럼 삽입 영역이 바뀌면 UI도 이를 따라갑니다.

{% hint style="warning" %}
**📔 UI를 좌측 상단에 고정하지 마세요.** 그 모서리와 왼쪽 전체 가장자리는 익스플로러가 미니맵, 채팅, 그리고 모바일에서는 가상 조이스틱을 그리는 곳입니다. 그곳에 배치된 장면 UI는 렌더링되지만, 클라이언트 자체 UI가 그 위에 그려지므로 가려지고 버튼을 클릭할 수 없는 경우가 많습니다. UI를 오른쪽에 고정하거나 중앙에 배치하거나, 다음을 사용하세요 `screenInset: 'interactable'` 그래서 익스플로러가 자체 HUD와 겹치지 않게 배치합니다. 다음을 참조하세요 [UI를 절대 좌측 상단에 고정하지 마세요](/creator/content-creator-ko/sdk7/designing-the-experience/ux-ui-guide.md#never-anchor-your-ui-to-the-top-left).
{% endhint %}

{% hint style="warning" %}
**📔 `'interactable'` 는 데스크톱에서 아무 효과도 없는 것이 아닙니다.** 기기 삽입 영역과 달리, 상호작용 가능 영역은 *아닙니다* 데스크톱 클라이언트에서는 0입니다. 자체 UI를 위해 화면 왼쪽 약 25%를 예약하므로 `screenInset: 'interactable'` 그곳의 남은 75%에 UI를 배치합니다. 이것이 이 옵션의 목적이지만, 데스크톱 레이아웃도 바뀐다는 뜻이므로 다음으로 분기하세요 [`isMobile()`](/creator/content-creator-ko/build-for-mobile/develop/detect-platform.md) 휴대폰에서만 원한다면

**클라이언트 지원**: `'interactable'` 영역을 보고하는 익스플로러가 필요합니다. 데스크톱에서는 지원되며, 모바일에서는 클라이언트 버전 `1.12.1` 이후부터 지원됩니다 — 이전 모바일 클라이언트에서는 값이 0으로 보고되며, UI는 전체 화면을 덮는 방식으로 되돌아갑니다. 같은 `1.12.1` 릴리스는 또한 다음을 정규화합니다 `'device'` Android와 iOS 사이의 영역이므로, 어느 삽입 영역에 의존하는 레이아웃이든 그 최소 기준으로 취급하세요. `'none'` 어디서나 동일하게 동작합니다.
{% endhint %}

{% hint style="warning" %}
**📔 참고**: UI를 다음으로 감싸지 마세요 [`ScreenInsetArea` 또는 `InteractableArea`](/creator/content-creator-ko/build-for-mobile/develop/safe-area.md#wrap-part-of-your-ui-instead) 컴포넌트를 사용하면서 동시에 대응하는 `screenInset` 값을 렌더러에 남겨두면 삽입 영역이 두 번 적용되어 UI가 두 배 여백만큼 안쪽으로 밀립니다. 다음에 의존하거나 `screenInset`, 또는 이를 `'none'` 로 설정하고 래퍼를 직접 배치하세요.
{% endhint %}

### 실제 기기의 세 가지 영역

아래 캡처는 같은 휴대폰에서 같은 장면을 세 번 렌더링한 것으로, 오직 `screenInset` 값만 달라졌습니다. 자홍색 사각형은 UI 자체이며, 윤곽선은 익스플로러가 보고하는 영역입니다 — 시안색은 전체 캔버스, 호박색은 `screenInsetArea`, 녹색은 `interactableArea`.

<figure><img src="https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-78aa0fa3423a3ed51b5889f0150484a354b2dd30%2Fscreeninset-none.png?alt=media" alt="Scene UI covering the whole phone screen, including the notch strip and the areas under the client controls"><figcaption><p><code>screenInset: 'none'</code> — UI가 전체 캔버스를 덮습니다. 모서리는 노치 아래와 클라이언트 자체 컨트롤 뒤에 놓입니다.</p></figcaption></figure>

<figure><img src="https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-94c4b28274282d30cda22c389b375fa30c78dc64%2Fscreeninset-device.png?alt=media" alt="Scene UI inset to the device safe area, clear of the notch"><figcaption><p><code>screenInset: 'device'</code> (기본값) — UI가 기기 안전 영역 안으로 들어와 노치와 둥근 모서리를 피합니다. 다만 이 영역에 포함되지 않는 클라이언트 컨트롤과는 여전히 겹칩니다.</p></figcaption></figure>

<figure><img src="https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-c0c75a80bed756d6b2b1483a7cca0cadef26c77e%2Fscreeninset-interactable.png?alt=media" alt="Scene UI inset to the area the client reports as free of its own HUD"><figcaption><p><code>screenInset: 'interactable'</code> — UI가 클라이언트가 장면 UI용으로 지정한 사각형 안에 배치됩니다. 모바일 클라이언트 <code>1.12.1</code>, 여기에서 캡처된 것은 왼쪽 열(프로필, 채팅, 조이스틱)을 제외합니다. 오른쪽 아래의 액션 버튼은 의도적으로 해당 영역 위에 그려집니다.</p></figcaption></figure>

{% hint style="info" %}
**💡 팁**: `'interactable'` 각 익스플로러가 장면 UI에 지정한 영역을 제공합니다. 이는 의도적으로 "모든 클라이언트 컨트롤이 그 밖에 있다"와 같은 의미는 아닙니다 — 세 번째 캡처가 보여주듯 모바일 액션 버튼은 의도적으로 해당 영역 위에 그려집니다. 그 아래에 배치한 것은 여전히 닿을 수 있지만 같은 탭을 놓고 경쟁하므로, 다음을 염두에 두세요 [혼잡한 모서리](/creator/content-creator-ko/build-for-mobile/develop/safe-area.md#where-the-client-controls-live) 를 염두에 두고, 대상 플랫폼을 확인하세요: 영역이 무엇을 포함하는지는 각 익스플로러의 결정이며 버전마다 달라질 수 있습니다.
{% endhint %}

각 렌더러는 자체 `screenInset`, 따라서 메인 UI와 다음으로 추가된 UI는 `addUiRenderer()` 동시에 서로 다른 영역을 사용할 수 있습니다. 가상 크기와 달리 이 값은 렌더러 간에 공유되지 않습니다.

## 여러 UI 모듈

장면에 각각 자체 UI를 정의하는 여러 시스템이나 모듈이 있다면, 각 UI 모듈을 다음으로 렌더링할 수 있습니다 `ReactEcsRenderer.addUiRenderer()`. 이는 여러 UI 컴포넌트가 있는 복잡한 장면을 작업할 때, 또는 다음을 위한 UI를 정의할 때 특히 유용합니다 [스마트 아이템](https://github.com/decentraland/docs/tree/main/creator/sdk7/smart-items/smart-items.md), 이는 장면의 나머지 코드에 무엇이 있든 독립적으로 사용할 수 있어야 합니다.

이 `ReactEcsRenderer.addUiRenderer()` 함수는 UI의 소유자로 엔티티를 제공할 것을 요구합니다. 이는 아무 엔티티나 될 수 있으며, UI 소유자로만 사용하기 위해 만든 더미 엔티티도 가능합니다.

```ts
export function setupUi() {

    // UI의 소유자가 될 더미 엔티티를 만듭니다
    const dummyEntity = engine.addEntity()

    // UI 모듈을 UI 모듈 배열을 반환하는 함수로 정의합니다
    const uiComponent = () => [
      // UI 모듈을 반환하는 함수,
      // UI 모듈을 반환하는 함수
      // ...
    ]

    // 더미 엔티티를 소유자로 하여 UI 모듈을 렌더링합니다
    ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent)
}
```

이 스니펫은 장면의 다른 어떤 UI 코드와도 독립적으로 존재할 수 있습니다. 장면의 나머지 부분에는 다음이 포함될 수도 있고 `ReactEcsRenderer.setUiRenderer()`, 전혀 없을 수도 있지만, UI는 여전히 렌더링됩니다.

하나의 `addUiRenderer()` 호출에는 다음과 마찬가지로 가상 너비와 높이도 포함할 수 있습니다 `setUiRenderer()`. 그러나 장면에 다음이 있다면 `setUiRenderer()` 호출이 가상 너비와 높이도 정의한다면, 다음의 가상 너비와 높이는 `addUiRenderer()` 호출은 무시됩니다.

```tsx
ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
```

가상 크기는 장면 전체에서 하나의 값이며, 다음과 같이 결정됩니다: 다음의 크기가 `setUiRenderer()` 우선합니다. 그렇지 않으면 최초의 `addUiRenderer()` 호출이 제공한 값이 우선합니다. 그리고 어떤 렌더러도 제공하지 않았다면 [플랫폼 기본값](#default-virtual-size) 이 적용됩니다. 다음만 포함하는 옵션은 `screenInset` 제공된 크기로 간주되지 않습니다.

이 [`screenInset`](#screen-inset-area) 는 반대로 동작합니다 — 렌더러별로 적용되므로 각 UI 모듈은 서로 다른 화면 영역에 있을 수 있습니다:

```tsx
// 이 위젯은 메인 UI가 무엇을 사용하든 익스플로러 HUD와 겹치지 않습니다
ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent, { screenInset: 'interactable' })
```

해당 UI는 다음으로 제거할 수 있습니다 `ReactEcsRenderer.removeUiRenderer(dummyEntity)` , 또한 UI를 소유한 엔티티가 삭제되면 UI도 함께 제거됩니다. 만약 `ReactEcsRenderer.addUiRenderer()` 같은 엔티티에 대해 다른 UiRenderer로 다시 호출하면 이전 것은 정리되고 새 것이 이를 대체하며, 쌓기 순서에서의 위치는 유지됩니다. 이미 렌더링된 UI 모듈의 옵션을 변경하는 방법도 다시 호출하는 것입니다. 예를 들어 그 모듈의 `zIndex`.

### UI 모듈 간의 쌓임 순서

기본적으로 UI 모듈은 처음 나타난 순서대로 쌓이며, 나중에 나타난 것이 앞에 옵니다. 같은 프레임에 처음 나타나는 모듈 — 일반적으로 장면의 첫 프레임 전에 등록된 모든 것 — 은 다음의 메인 UI와 함께 그려집니다 `setUiRenderer()` 가 뒤에 놓이고, 그 뒤에 추가된 순서대로 다른 모듈이 따라옵니다. `zIndex` 렌더러 옵션의 다음 값에 따라: 더 높은 `zIndex` 값을 가진 모듈은 나타난 순서와 상관없이 더 낮은 값을 가진 모듈보다 앞에 렌더링됩니다. 다음 값으로 남겨진 모듈은 `0` 기본 순서를 유지합니다. 이 옵션은 다음에서도 동일하게 작동합니다 `setUiRenderer()`, 따라서 메인 UI도 다른 모듈보다 앞에 배치할 수 있습니다.

```tsx
// 먼저 등록되었지만 항상 다른 UI 모듈보다 앞에 옵니다
ReactEcsRenderer.addUiRenderer(dummyEntity, uiComponent, { zIndex: 10 })

// 메인 UI는 더 낮은 값을 설정하지 않은 모든 모듈 뒤에 놓입니다
ReactEcsRenderer.setUiRenderer(mainUi, { zIndex: -10 })
```

이것은 다음과는 다른 설정입니다 `zIndex` 의 `UiEntity`, 이는 다음만의 순서를 정합니다 [UI 모듈 내부의 형제 요소](/creator/content-creator-ko/sdk7/2d-ui/ui-positioning.md#z-index). 렌더러 옵션은 전체 모듈끼리의 순서를 정하며, 각 모듈 내부의 요소는 자체 `zIndex`.

### 하나의 setUiRenderer 문 공유하기

다음을 호출하는 대신 `ReactEcsRenderer.addUiRenderer()` 각 UI 모듈마다 다음을 호출하는 대신, 다음을 호출할 수 있습니다 `ReactEcsRenderer.setUiRenderer()` 한 번만 UI 모듈 배열로 호출할 수 있으며, 이 모듈들은 서로 다른 파일에 있을 수 있습니다.

```ts
const uiComponent = () => [
  // UI 모듈을 반환하는 함수,
  // UI 모듈을 반환하는 함수
  // ...
]

ReactEcsRenderer.setUiRenderer(uiComponent, { virtualWidth: 1920, virtualHeight: 1080 })
```

더 완전한 예시는 아래와 같습니다:

***ui.tsx 파일:***

```ts
export function UIModule1() {
  return (
    <UiEntity
      uiTransform={{
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        positionType: 'absolute',
        position: { right: '3%', bottom: '3%' },
      }}
    >
      <Label value="Hello World!" fontSize={18} textAlign="middle-center" />
    </UiEntity>
  )
}

export function UIModule2() {
  return (
    <UiEntity
      uiTransform={{
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'space-between',
        positionType: 'absolute',
        position: { right: '3%', top: '3%' },
      }}
    >
      <Label
        value="Here's some more UI!"
        fontSize={18}
        textAlign="middle-center"
      />
    </UiEntity>
  )
}
```

***index.ts 파일:***

```ts
import { ReactEcsRenderer } from '@dcl/sdk/react-ecs'
import { UIModule1, UIModule2 } from './ui'

export function main() {
    ReactEcsRenderer.setUiRenderer(() => [
      UIModule1(),
      UIModule2(),
      // ...
      // 아래 줄은 DCL UI Toolkit 라이브러리를 사용하기 위한 것입니다
      // https://github.com/decentraland-scenes/dcl-ui-toolkit
      ui.render(),
    ], { virtualWidth: 1920, virtualHeight: 1080 })
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-ko/sdk7/2d-ui/onscreen-ui.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
