> 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/ui_button_events.md).

# UI 버튼 이벤트

UI 엔티티의 버튼 이벤트를 처리합니다.

UI에 버튼을 만들려면, 다음을 만드세요 `버튼` 다음 속성을 가진 UI 요소:

* `값`: 버튼에 표시할 텍스트가 포함된 문자열.
* `onMouseDown`: 사용자가 엔티티 위에서 포인터 버튼을 누를 때마다 실행되는 콜백 함수.
* `uiTransform`: UI 요소의 위치 지정 속성.

다음 예제는 클릭 가능한 UI 버튼을 만드는 방법을 보여줍니다.

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

```tsx
import { Button } from '@dcl/sdk/react-ecs'

export const uiMenu = () => (
	<Button
		value="클릭하세요"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('UI를 클릭했습니다')
		}}
	/>
)
```

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

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

export function main() {
    ReactEcsRenderer.setUiRenderer(uiMenu)
}
```

{% hint style="warning" %}
**📔 참고**: 이 페이지의 다음 모든 스니펫은 여러분이 `.ts` 와 유사한 `ReactEcsRenderer.setUiRenderer()` 함수를 실행 중이라고 가정합니다.
{% endhint %}

또한 UI 정의 밖에서 실행되는 함수를 작성하고, 이름으로 참조할 수도 있습니다. 이렇게 하면 UI 코드를 더 읽기 쉽게 유지할 수 있고, 여러 개의 클릭 가능한 UI 엔티티가 같은 함수를 호출해야 할 때도 유용합니다.

```tsx
import { Button } from '@dcl/sdk/react-ecs'

function handleClick() {
	// 클릭 시 수행할 작업
	console.log('UI를 클릭했습니다')
}
export const uiMenu = () => (
	<Button
		value="클릭하세요"
		uiTransform={{ width: 100 }}
		onMouseDown={handleClick}
	/>
)
```

다음 필드를 다음에 추가할 수 있습니다 `버튼` UI 요소:

* `onMouseDown`: 사용자가 엔티티 위에서 포인터 버튼을 누를 때마다 실행되는 콜백 함수.
* `onMouseUp`: 엔티티를 가리키고 있을 때 포인터 버튼이 올라갈 때마다 실행되는 콜백 함수.
* `onMouseEnter`: 포인터가 버튼 위로 호버를 시작할 때마다 실행되는 콜백 함수.
* `onMouseLeave`: 포인터가 버튼 위 호버를 멈출 때마다 실행되는 콜백 함수.
* `color`: 버튼 텍스트의 색상.
* `font`: 버튼 텍스트의 글꼴.
* `textAlign`: 버튼 안의 텍스트 정렬
* `uiTransform`: UI 요소의 위치 지정 속성.
* `uiBackground`: UI 요소의 색상 또는 텍스처를 설정합니다.
* `variant`: 이 속성을 사용하여 버튼의 스타일을 기본값 중 하나로 설정하세요. `primary` 및 `secondary` 를 사용할 수 있습니다.
* `disabled`: 버튼을 비활성화하도록 설정하는 불리언입니다.  `disabled` 이 *true*로 설정되면,  `onMouseDown` 및 `onMouseUp` 액션은 더 이상 호출되지 않고, 버튼은 포인터 상호작용을 더 이상 알리지 않습니다. 버튼은 또한 "회색으로 흐려진" 상태로 그려집니다. 텍스트와 배경 모두가 각각의 `alpha` 값의 절반으로 렌더링됩니다. 이는 표시상의 변경일 뿐입니다. 장면이 전달하는 `Color4` 값은 절대 수정되지 않으므로, 여러 요소에서 공유 색상 팔레트 객체를 안전하게 재사용할 수 있습니다.

## 버튼 스타일링

variant를 `primary` 또는 `secondary` 로 설정하여 버튼의 기본 스타일 옵션을 활용하세요. `primary` 는 버튼을 흰색 텍스트의 빨간색으로 만듭니다, `secondary` 는 버튼을 빨간색 텍스트의 흰색으로 만듭니다.

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

export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: 500,
			height: 230,
			margin: '16px 0 8px 270px',
			padding: 4,
			alignSelf: 'center',
		}}
		uiBackground={{ color: Color4.Gray() }}
	>
		<Button
			value="클릭하세요"
			variant="primary"
			uiTransform={{ width: 80, height: 20, margin: 4 }}
			onMouseDown={() => {
				console.log('UI를 클릭했습니다')
			}}
		/>
		<Button
			value="클릭하세요"
			variant="secondary"
			uiTransform={{ width: 80, height: 20, margin: 4 }}
			onMouseDown={() => {
				console.log('UI를 클릭했습니다')
			}}
		/>
	</UiEntity>
)
```

배경의 모든 속성을 자유롭게 사용할 수도 있습니다. variant를 설정한 다음 일부 속성을 덮어쓸 수도 있습니다. 이 예제는 `primary` variant를 사용하지만, 색상은 초록색으로 덮어씁니다:

```tsx
import { Button } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

export const uiMenu = () => (
	<Button
		value="내 버튼!"
		variant="primary"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('내 버튼이 클릭되었습니다!')
		}}
		uiBackground={{
			color: Color4.Green(),
		}}
	/>
)
```

## 토글 가능한 버튼

흔한 사용 사례는 버튼을 스위치처럼 두 상태 사이에서 전환되도록 만드는 것입니다. 아래 예제는 버튼을 누를 때마다 두 색상 사이를 전환합니다:

```tsx
import { Button } from '@dcl/sdk/react-ecs'
import { Color4 } from '@dcl/sdk/math'

let buttonEnabled = false

export const uiMenu = () => (
	<Button
		value="내 버튼"
		variant="primary"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			console.log('내 버튼이 클릭되었습니다!')
			buttonEnabled = !buttonEnabled
			if (buttonEnabled) {
				// 작업 수행
			} else {
				// 다른 작업 수행
			}
		}}
		uiBackground={{
			color: buttonEnabled ? Color4.Green() : Color4.Red(),
		}}
	/>
)
```

위 예제에서 색상은  `buttonEnabled` 변수에 따라 달라진다는 점에 유의하세요. 이 변수의 값이 바뀔 때마다 배경색에 즉시 반영됩니다.

## 호버 피드백

또 다른 흔한 사용 사례는 버튼 위에 호버할 때 어떤 시각적 힌트를 표시하여 이것이 상호작용 가능하다는 점을 분명히 하거나, 이 버튼이 무엇을 하는지 설명하는 호버 힌트를 표시하는 것입니다.  `onMouseEnter` 및 `onMouseLeave` 콜백을 사용하여 플레이어의 커서가 버튼 위에 있는지 감지하고, 그에 맞게 반응하세요.

```tsx
import { Button } from '@dcl/sdk/react-ecs'

let buttonEnabled = false

export const uiMenu = () => (
	<Button
		value="내 버튼"
		uiTransform={{ width: 100, height: 100 }}
		onMouseDown={() => {
			// 버튼 함수
		}}
		onMouseEnter={() => {
			// 힌트 표시
		}}
		onMouseLeave={() => {
			// 힌트 숨기기
		}}
	/>
)
```

## 다른 요소를 클릭 가능하게 만들기

UI의 어떤 요소든  `onMouseDown` 속성을 추가하면 클릭 가능하게 만들 수 있으며, 버튼과 동일하게 동작합니다. 다음 예제는 `onMouseDown` 속성을 배경 이미지와 텍스트에 추가합니다.

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

export const uiMenu = () => (
	<UiEntity
		onMouseDown={() => {
			console.log('배경이 클릭되었습니다!')
		}}
		uiTransform={{
			width: 400,
			height: 230,
		}}
		uiBackground={{ color: Color4.create(0.5, 0.8, 0.1, 0.6) }}
	>
		<Label
			onMouseDown={() => {
				console.log('레이블이 클릭되었습니다!')
			}}
			value={`Player: ${getPlayerPosition()}`}`
			fontSize={18}
			uiTransform={{ width: '100%', height: 30 }}
		/>
	</UiEntity>
)

function getPlayerPosition() {
	const playerPosition = Transform.getOrNull(engine.PlayerEntity)
	if (!playerPosition) return 'unknown'
	const { x, y, z } = playerPosition.position
	return `{x: ${x.toFixed(2)}, y: ${y.toFixed(2)}, z: ${z.toFixed(2)} }`
}
```

## 포인터 차단

모든 UI 엔티티는 기본적으로 포인터를 차단하지 않으며, 즉 플레이어의 클릭은 이를 통과하여 뒤의 3D 월드 공간에 있는 객체와 상호작용합니다. 엔티티에  `onMouseDown` 콜백이 있으면 포인터를 차단하게 되므로, 플레이어의 클릭이 해당 UI 엔티티 뒤의 것에 영향을 주지 않습니다.

이 기본 동작은  `pointerFilter` 의  `uiTransform` 컴포넌트 값으로 변경할 수 있습니다. 예를 들어,  `onMouseDown` 가 없는 엔티티를 포인터 차단 상태로 설정할 수 있습니다.

다음은  `pointerFilter` 에 대해 지원되는 값입니다:

* `block`: UI 요소가 포인터를 차단하며, 플레이어는 이 UI 요소 뒤의 어떤 것도 클릭할 수 없습니다.
* `none`: UI 요소가 포인터를 차단하지 않습니다. 이 요소는 클릭할 수 없으며, 뒤에 있는 것은 무엇이든 클릭할 수 있습니다.

아래는  `onMouseDown`가 없는 간단한 UI이지만,  `pointerFilter` 를 `block`.

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

// UI 그리기
export const uiMenu = () => (
	<UiEntity
		uiTransform={{
			width: '100%',
			height: '100px',
			pointerFilter: `block`,
		}}
		uiText={{ value: `이 요소는 포인터를 차단합니다`, fontSize: 40 }}
		uiBackground={{ color: Color4.create(0.5, 0.8, 0.1, 0.6) }}
	/>
)
```

### 차단은 보이는 픽셀이 아니라 레이아웃 박스를 따릅니다

차단 요소는 그  **전체 사각형**에 걸쳐 클릭을 잡아냅니다. 그곳에 무엇이 그려져 있든 상관없습니다. 완전히 투명한 배경은 차이를 만들지 않습니다.

{% hint style="danger" %}
**경고:** 포인터 핸들러나  `pointerFilter: 'block'` 을 전체 화면 래퍼에  `100%` 크기가 `100%`.

그 사각형은 화면 전체이므로, 레이아웃 루트의 잘못된 단 하나의  `onMouseDown` 만으로도 다른 모든 UI 요소와 3D 월드의 모든 것이 클릭 불가능해집니다. 볼 수 있는 패널은 화면의 작은 부분만 덮기 때문에 UI는 여전히 완벽하게 올바르게 보이며, 그래서 이를 알아차리기 매우 어렵습니다.
{% endhint %}

핸들러는 필요한 가장 작은 요소에만 연결하세요: 패널, 버튼, 행(row) 같은 곳입니다. 레이아웃 래퍼에는 핸들러를 두지 마세요.

전체 화면 차단 요소가 올바른 경우는 두 가지뿐이며, 둘 다 의도적인 경우입니다:

* 하나의 **모달 배경**, 클릭을 흡수하도록 되어 있으며, 모달이 열려 있는 동안에만 렌더링됩니다.
* 하나의 **드래그 해제 포착기**, 드래그가 진행 중일 때만 존재합니다.  [드래그 상호작용](#drag-interactions) 를 참조하세요.

장면의 어느 곳에서든 클릭이 작동하지 않으면, 가장 먼저 이것을 확인하세요.

## 드래그 상호작용

UI 포인터 핸들러(`onMouseDown`, `onMouseUp`, `onMouseEnter`, `onMouseLeave`)는 매개변수가 없습니다. 위치나 좌표 데이터 없이 단순한  `() => void`  콜백으로 실행됩니다.  `onMouseDrag` 또는 `onMouseMove` 핸들러는 UI 시스템에 없습니다.

드래그 기반 UI(슬라이더, 스크럽 바, 드래그 핸들)를 만들려면  `PrimaryPointerInfo.screenDelta` 를 `@dcl/sdk/ecs`에서 사용하세요. 이렇게 하면 커서가 무엇 위에 있든 상관없이 매 프레임 업데이트되는, 마지막 프레임 이후의 마우스 이동 픽셀을 얻을 수 있습니다.

패턴은 다음과 같이 작동합니다:

1. `onMouseDown` 드래그 대상에서 드래그를 시작하고 초기 값을 기록합니다.
2. 시스템이  `screenDelta` 를 매 프레임 읽어 드래그가 활성화되어 있는 동안 값을 누적합니다.
3. 와 함께하는 전체 화면의 보이지 않는 오버레이가  `pointerFilter: 'block'` 마우스 해제를 감지하므로, 좁은 대상 밖에서 놓아도 드래그가 끝납니다.

```ts
import { engine, PrimaryPointerInfo, UiCanvasInformation } from '@dcl/sdk/ecs'

let dragging = false
let sliderValue = 0.5

// 드래그 이동을 누적하려면 이 시스템을 매 프레임 호출하세요
engine.addSystem(() => {
	if (!dragging) return

	const delta = PrimaryPointerInfo.getOrNull(engine.RootEntity)?.screenDelta
	if (!delta || delta.x === 0) return

	// 화면 픽셀을 0~1 범위로 변환
	// 드래그 속도가 커서와 맞도록 UI 스케일 계수로 나눕니다
	const canvas = UiCanvasInformation.getOrNull(engine.RootEntity)
	const scale = canvas ? Math.min(canvas.width / 1920, canvas.height / 1080) : 1
	const trackWidth = 200 // 슬라이더 트랙의 가상 px 너비

	sliderValue = Math.max(0, Math.min(1, sliderValue + delta.x / scale / trackWidth))
})
```

{% hint style="warning" %}
**참고:** 모바일에서는,  `screenDelta` 항상 0을 반환합니다(자유롭게 움직이는 커서가 없기 때문입니다). 모바일 호환 슬라이더의 경우 드래그 트랙 옆에 스테퍼 버튼(`-` / `+`)을 추가하세요.  [`isMobile()`](/creator/content-creator-ko/build-for-mobile/develop/detect-platform.md) 를 `@dcl/sdk/platform` 을 사용해 UI 분기를 처리하세요.
{% endhint %}

{% hint style="info" %}
**팁:** 항상  `screenDelta` 를 UI 스케일 계수로 나누세요. 그렇지 않으면 드래그가 가상 크기와 다른 해상도의 화면에서 과도하게 이동하거나 덜 이동하게 됩니다.
{% endhint %}


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
