> 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/contributor/contributor-ko/scene-runtime/components/component-creation.md).

# 컴포넌트 생성

새로운 컴포넌트를 만들려면, 렌더러에서 코딩을 시작하기 전에 몇 가지 작업을 해야 합니다. 따라야 할 단계별 가이드가 있습니다. 모든 단계를 먼저 나열한 다음, 각 단계를 더 자세한 설명과 함께 따라가면 됩니다.

1. 다음 위치에서 proto 정의를 생성하세요: [@dcl/protocol](https://github.com/decentraland/protocol)
2. 다음 위치에서 새 proto TypeScript 코드를 생성하세요: [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)
3. 다음에 대한 테스트를 생성하세요: `js-sdk-toolchain`
4. 프로젝트에서 새 proto C# 코드를 생성하세요
5. 새 컴포넌트 코딩하기
6. 컴포넌트가 규칙을 따르도록 하세요

## 다음 위치에서 proto 정의를 생성하세요: [@dcl/protocol](https://github.com/decentraland/protocol)

정의를 만들려면 다음 저장소로 가야 합니다: <https://github.com/decentraland/protocol>

1. 다음 폴더에서 proto 정의를 만드세요: <https://github.com/decentraland/protocol/tree/main/ecs/components>
2. 새 변경 사항으로 PR을 생성하세요

> ***참고:*** PR을 생성한 후 GitHub Bot이 PR을 테스트할 패키지 링크를 댓글로 남깁니다. 다음 단계에서 테스트할 때 그 링크를 사용할 수 있습니다

유의해야 할 사항

* 우리는 proto 3를 사용하므로, proto의 모든 정의는 해당 [문법과 함께 컴파일되어야 합니다](https://developers.google.com/protocol-buffers/docs/proto3/)
* 다시 만들 수 없는 공통 타입이 몇 가지 있습니다
* proto는 기본 정의를 가져야 합니다
* 컴포넌트를 열거하기 위해 다음 코드를 추가해야 합니다

```
import "common/id.proto";
option (ecs_component_id) = 1100;
```

.proto 예시

```
syntax = "proto3";

import "common/id.proto";
option (ecs_component_id) = 1020;

message PBAudioSource {
  optional bool playing = 1;
  optional float volume = 2; // default=1.0f
  optional bool loop = 3;
  optional float pitch = 4; // default=1.0f
  string audio_clip_url = 5;
}
```

## 다음 위치에서 새 proto TypeScript 코드를 생성하세요: [js-sdk-toolchain](https://github.com/decentraland/js-sdk-toolchain)

다음 저장소를 다운로드하세요: <https://github.com/decentraland/js-sdk-toolchain>

그 안에서 다음으로 이동하세요 `packages/dcl/ecs` (`https://github.com/decentraland/js-sdk-toolchain/tree/main/packages/%40dcl/ecs`)

그리고 그곳에서 다음을 실행할 수 있습니다

```
npm install @dcl/protocol@next
```

또는 GitHub Bot이 @dcl/protocol PR에서 생성한 명령어를 사용하세요(이것은 PR 테스트용으로 임시로 사용해야 합니다).

그다음 다음 명령어를 다음의 루트에서 실행하세요 `js-sdk-toolchain` 프로젝트:

```
make install
make build
```

그리고 생성된 코드를 push하세요.

## 프로젝트에서 새 proto C# 코드를 생성하세요

C# 코드를 생성하려면 다음으로 가야 합니다 `protocol-gen` 다음의 루트 경로에서 `@decentraland/unity-renderer` 저장소. 그리고 다음 명령어를 실행하세요:

```
npm install
npm run build-components
```

다음의 최신 버전으로 업그레이드하려면 `@dcl/protocol` (main 브랜치), 다음을 사용하여 업데이트해야 합니다

```
npm install @dcl/protocol@next
npm run build-components
```

PR을 테스트하려면 GitHub Bot이 다음에서 생성한 URL을 사용할 수 있습니다 `@dcl/protocol` PR: 예시:

```
npm install "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-3143233696.commit-45f1290.tgz"
npm run build-components
```

@dcl/protocol PR을 병합한 후에는 다음을 사용해야 합니다 `@dcl/protocol@next` 그리고 코드를 생성하세요.

## 새 컴포넌트 코딩하기

이제 새 컴포넌트의 기능을 구현할 시간입니다.

컴포넌트에는 다섯 가지 필수 요소가 있습니다.

* ComponentID 컴포넌트가 사용할 ID입니다. 고유해야 하며 proto 정의에서 생성되어야 합니다
* Model 이것은 컴포넌트의 모델입니다. 컴포넌트를 처리할 때 사용할 데이터입니다. proto 생성으로 자동 생성되며, proto 파일과 같은 이름에 앞에 PB가 붙습니다. 예를 들어, `BoxShape.proto` 정의가 있다면 모델의 생성된 클래스는 다음이 됩니다 `PBBoxShape`
* Component Handler 컴포넌트 핸들러는 컴포넌트의 모든 기능을 관리합니다. 이 클래스에서는 다음을 구현해야 합니다 `IECSComponentHandler<ModelClass>` (ModelClass는 모델입니다. 이는 proto에서 생성된 클래스이며, 이름은 PB + .proto 파일 이름이 됩니다). 이 인터페이스에는 컴포넌트를 만들기 위해 구현해야 하는 중요한 메서드가 3개 있습니다

```
        void OnComponentCreated(IParcelScene scene, IDCLEntity entity);
        void OnComponentRemoved(IParcelScene scene, IDCLEntity entity);
        void OnComponentModelUpdated(IParcelScene scene, IDCLEntity entity, ModelType model);
```

* Serializer 각 컴포넌트는 컴포넌트의 직렬화와 역직렬화를 구현해야 할 책임이 있습니다. 이 serializer는 바이트 배열로 serialize/deserialize할 수 있어야 합니다
* Register 이것은 컴포넌트를 시스템에 등록하여 시스템에 연결합니다. 이 register는 팩토리와 컴포넌트 writer에 컴포넌트를 등록합니다

컴포넌트 설계는 상속을 피하는 것이므로, 가능한 한 순수 함수를 사용하는 것을 권장합니다

이를 만들기 위해 다음 단계를 따라야 합니다

1. 컴포넌트 폴더와 어셈블리를 만드세요. 우리는 다음 폴더 아래에 모든 컴포넌트를 두고 있습니다 `DCLPlugins/ECS7/ECSComponents`. 컴포넌트를 담을 폴더와 새 어셈블리를 만들어야 합니다
2. 새 어셈블리에서는 다음 것을 참조해야 합니다 `DCL.ECSComponents.Data`. 이것은 방금 업데이트한 컴포넌트의 새 모델을 참조하게 됩니다
3. 모든 로직이 포함된 컴포넌트 핸들러를 만들어야 합니다(예로 다음을 보세요 `ECSBoxShapeComponentHandler.cs` 를 예시로)
4. serializer 클래스를 만들어야 합니다(아마 다른 클래스를 복사해서 여러분의 것으로 맞춰도 됩니다)
5. register 클래스를 만들어야 합니다

```sh
   public static class AudioSourceSerializer
    {
        public static byte[] Serialize(PBAudioSource model)
        {
            int size = model.CalculateSize();
            byte[] buffer = new byte[size];
            CodedOutputStream output = new CodedOutputStream(buffer);
            model.WriteTo(output);
            return buffer;
        }

        public static PBAudioSource Deserialize(object data)
        {
            return PBAudioSource.Parser.ParseFrom((byte[])data);
        }
    }
```

6. 다음에 새 register를 추가하세요 `ECS7ComponentsComposer` 클래스에 해당 ID와 함께

```sh
   public class ECS7ComponentsComposer : IDisposable
    {
        private readonly TransformRegister transformRegister;
        private readonly SphereShapeRegister sphereShapeRegister;
        private readonly BoxShapeRegister boxShapeRegister;
        private readonly PlaneShapeRegister planeShapeRegister;
        private readonly CylinderShapeRegister cylinderShapeRegister;
        private readonly AudioStreamRegister audioStreamRegister;
        private readonly AudioSourceRegister audioSourceRegister;

        public ECS7ComponentsComposer(ECSComponentsFactory componentsFactory, IECSComponentWriter componentsWriter)
        {
            transformRegister = new TransformRegister(ComponentID.TRANSFORM, componentsFactory, componentsWriter);
            sphereShapeRegister = new SphereShapeRegister(ComponentID.SPHERE_SHAPE, componentsFactory, componentsWriter);
            boxShapeRegister = new BoxShapeRegister(ComponentID.BOX_SHAPE, componentsFactory, componentsWriter);
            planeShapeRegister = new PlaneShapeRegister(ComponentID.PLANE_SHAPE, componentsFactory, componentsWriter);
            cylinderShapeRegister = new CylinderShapeRegister(ComponentID.CYLINDER_SHAPE, componentsFactory, componentsWriter);
            audioStreamRegister = new AudioStreamRegister(ComponentID.AUDIO_STREAM, componentsFactory, componentsWriter);
            audioSourceRegister = new AudioSourceRegister(ComponentID.AUDIO_SOURCE, componentsFactory, componentsWriter);
        }

        public void Dispose()
        {
            transformRegister.Dispose();
            sphereShapeRegister.Dispose();
            boxShapeRegister.Dispose();
            planeShapeRegister.Dispose();
            cylinderShapeRegister.Dispose();
            audioStreamRegister.Dispose();
            audioSourceRegister.Dispose();
        }
    }
```

이제 컴포넌트가 추가되어 작동하게 되었습니다!

## 컴포넌트가 규칙을 따르도록 하세요

새 컴포넌트를 개발할 때 고려해야 할 체크리스트가 몇 가지 있습니다. 이 부분은 그것들을 요약하려는 것입니다.

* 단위 테스트 모든 컴포넌트에는 기능, dispose, 그리고 최소한 직렬화/역직렬화를 검증하는 단위 테스트가 포함되어야 하며, 이를 통해 컴포넌트가 작동함을 보장해야 합니다
* 컴포넌트가 씬 안에 없을 때 어떤 일이 일어나는지 고려하세요(자세한 내용은 `SceneBoundariesChecker` 클래스를 확인하세요)
* 컴포넌트가 월드에 무언가를 렌더링한다면, 데이터 저장소에 렌더 가능한 정보를 추가해야 합니다. 이렇게 해야 씬에 렌더러 정보를 추가하여 제한에 반영될 수 있습니다
* 컴포넌트가 월드에 무언가를 렌더링한다면 다음을 추가해야 합니다 `MeshesInfo` 를 엔티티에
* 가능한 한 성능이 좋아야 합니다. 이 코드는 매우 자주 실행되므로 모든 것이 최대한 부드럽게 동작하도록 해야 합니다
* 다음과 함께 작동해야 합니다 `Hot reload` 프리뷰 모드에서. 만약 다음을 올바르게 코딩했다면 `OnComponentRemoved` 올바르게 구현했다면, 이것은 별도 설정 없이 작동하지만 hot reload는 컴포넌트의 dispose가 모든 것을 제대로 처리하는지 테스트하는 방법입니다
* 컴포넌트가 리소스를 사용한다면, 다음을 사용하여 리소스 관리를 구현해야 합니다 `AssetPromiseKeeper`. 컴포넌트는 리소스가 사용될 때와 더 이상 사용되지 않을 때 `AssetPromiseKeeper` 이를 알려야 합니다


---

# 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/contributor/contributor-ko/scene-runtime/components/component-creation.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.
