

지원 종료 공지: 2026 AWS 년 5월 20일에에 대한 지원이 종료됩니다 AWS SimSpace Weaver. 2026년 5월 20일 이후에는 SimSpace Weaver 콘솔 또는 SimSpace Weaver 리소스에 더 이상 액세스할 수 없습니다. 자세한 내용은 [AWS SimSpace Weaver 지원 종료를 참조하세요](https://docs.aws.amazon.com/simspaceweaver/latest/userguide/simspaceweaver-end-of-support.html).

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 작업 SimSpace Weaver
<a name="working-with"></a>

이 장에서는 SimSpace Weaver에서 자체 애플리케이션을 구축하는 데 도움이 되는 정보와 지침을 제공합니다.

**Topics**
+ [시뮬레이션 구성](working-with_configuring-simulation.md)
+ [시뮬레이션의 최대 지속 시간](working-with_max-duration.md)
+ [앱 개발](working-with_developing-apps.md)
+ [클라이언트 애플리케이션 개발](working-with_developing-client-applications.md)
+ [사용자 지정 앱의 IP 주소 및 포트 번호 가져오기](working-with_get-ip.md)
+ [Unreal Engine 뷰 클라이언트 시작](working-with_unreal-client.md)
+ [의 로컬 개발 SimSpace Weaver](working-with_local-development.md)
+ [AWS SimSpace Weaver 앱 SDK](working-with_app-sdk.md)
+ [AWS SimSpace Weaver 데모 프레임워크](working-with_demo-framework.md)
+ [Service Quotas 작업](working-with_quotas.md)
+ [디버깅 시뮬레이션](working-with_debugging.md)
+ [사용자 지정 컨테이너](working-with_custom-containers.md)
+ [Python 작업](working-with_python.md)
+ [기타 엔진 지원](working-with_engines.md)
+ [에서 라이선스 소프트웨어 사용 AWS SimSpace Weaver](working-with_byol.md)
+ [를 사용하여 리소스 관리 AWS CloudFormation](working-with_cloudformation.md)
+ [스냅샷](working-with_snapshots.md)
+ [메시징](working-with_messaging.md)

# 시뮬레이션 구성
<a name="working-with_configuring-simulation"></a>

**시뮬레이션 스키마**(또는 **스키마**)는 시뮬레이션 구성을 지정하는 YAML 형식이 지정된 텍스트 파일입니다. 동일한 스키마를 사용하여 여러 시뮬레이션을 시작할 수 있습니다. 스키마 파일은 시뮬레이션을 위한 프로젝트 폴더에 있습니다. 텍스트 편집기를 사용하여 파일을 편집할 수 있습니다.는 시뮬레이션을 시작할 때 SimSpace Weaver 만 스키마를 읽습니다. 스키마 파일을 편집하면 편집 후에 시작하는 새 시뮬레이션에만 영향을 줍니다.

시뮬레이션을 구성하려면 시뮬레이션 스키마 파일을 편집합니다(운영 체제에 적합한 경로 구분자 사용).

```
project-folder\tools\project-name-schema.yaml
```

새 시뮬레이션을 생성할 때 시뮬레이션 스키마를 업로드합니다. 프로젝트의 빠른 시작 도우미 스크립트는 시뮬레이션을 빌드하는 프로세스의 일부로 스키마를 업로드합니다.

```
project-folder\tools\windows\quick-start.py
```

빠른 시작 스크립트 실행에 대한 자세한 내용은이 가이드의 [시작](getting-started.md) 장[상세 자습서](getting-started_detailed.md)에 있는 단원을 참조하십시오.

## 시뮬레이션 구성 파라미터
<a name="working-with_configuring-simulation_config-parameters"></a>

시뮬레이션 스키마에는 다음과 같은 자동 구성 정보가 포함됩니다.
+ **시뮬레이션 속성** - SDK 버전 및 컴퓨팅 구성([작업자](w2aac51.md#glossary_worker) 유형 및 수)
+ **클럭** - 틱 속도 및 허용오차
+ **공간 분할 전략** - 공간 토폴로지(예: 그리드), 경계 및 배치 그룹(작업자에 대한 공간 분할 그룹화)
+ **도메인 및 해당 앱** - 앱 버킷, 경로, 실행 명령

SimSpace Weaver 는 스키마 구성을 사용하여 공간 파티션을 구성 및 정렬하고, 앱을 시작하고, 지정된 틱 속도로 시뮬레이션을 진행합니다.

**참고**  
 SimSpace Weaver 앱 SDK의 create-project 스크립트는 샘플 애플리케이션을 기반으로 시뮬레이션 스키마를 자동으로 생성합니다.

다음 주제에서는 시뮬레이션 스키마의 파라미터에 대해 설명합니다. 시뮬레이션 스키마에 대한 전체 설명은 [SimSpace Weaver 시뮬레이션 스키마 참조](schema-reference.md) 섹션을 참조하세요.

**Topics**
+ [시뮬레이션 구성 파라미터](#working-with_configuring-simulation_config-parameters)
+ [SDK 버전](working-with_configuring-simulation_sdk-version.md)
+ [시뮬레이션 속성](working-with_configuring-simulation_simulation-properties.md)
+ [작업자](working-with_configuring-simulation_workers.md)
+ [클럭](working-with_configuring-simulation_clock.md)
+ [파티셔닝 전략](working-with_configuring-simulation_partitioning-strategies.md)
+ [도메인](working-with_configuring-simulation_domains.md)

# SDK 버전
<a name="working-with_configuring-simulation_sdk-version"></a>

`sdk_version` 필드는 스키마의 형식 SimSpace Weaver 이 지정된 버전을 지정합니다. 유효한 값: `1.17`, `1.16`, `1.15`, `1.14`, `1.13`, `1.12` 

**중요**  
`sdk_version`의 값에는 메이저 버전 번호와 첫 번째 마이너 버전 번호만 포함됩니다. 예를 들어, `1.12` 값은 `1.12.0`, `1.12.1`, `1.12.2` 등의 모든 버전 `1.12.x`를 지정합니다.

# 시뮬레이션 속성
<a name="working-with_configuring-simulation_simulation-properties"></a>

스키마 `simulation_properties` 섹션은 엔터티의 인덱스 필드(일반적으로 공간 위치)에 대한 로깅 구성 및 데이터 유형을 지정합니다.

```
simulation_properties:
  log_destination_service: "logs"
  log_destination_resource_name: "MySimulationLogs"
  default_entity_index_key_type: "Vector3<f32>"
```

`log_destination_service`의 값에 따라 `log_destination_resource_name` 값의 해석이 결정됩니다. 현재 지원되는 값은 `logs`입니다. 즉, `log_destination_resource_name`의 값은 Amazon CloudWatch Logs의 로그 그룹 이름입니다.

**참고**  
로깅은 선택 사항입니다. 로그 대상 속성을 구성하지 않으면 시뮬레이션에서 로그가 생성되지 않습니다.

`default_entity_index_key_type` 속성은 필수입니다. 유일한 유효 값은 `Vector3<f32>`입니다.

# 작업자
<a name="working-with_configuring-simulation_workers"></a>

`workers` 섹션은 시뮬레이션에 사용할 작업자의 유형과 수를 지정합니다.는 Amazon EC2 인스턴스 유형에 매핑되는 자체 작업자 유형을 SimSpace Weaver 사용합니다.

```
workers:
  MyComputeWorkers:
    type: "sim.c5.24xlarge"
    desired: 1
```

## 다중 작업자 시뮬레이션 지원
<a name="working-with_configuring-simulation_workers_multi-worker"></a>

하나 이상의 작업자를 사용하는 시뮬레이션을 생성할 수 있습니다. 기본적으로 시뮬레이션에서는 하나의 작업자를 사용합니다. 시뮬레이션을 시작하기 전에 시뮬레이션 스키마를 수정해야 합니다.

**참고**  
이미 시작된 시뮬레이션은 변경할 수 없습니다. 실행 중인 시뮬레이션에 대해 다중 작업자를 활성화하려면 먼저 시뮬레이션을 중지하고 삭제해야 합니다.

둘 이상의 작업자를 사용하려면 컴퓨팅 인스턴스의 `desired` 수를 1보다 큰 값으로 설정합니다. 각 작업자에는 최대 앱 수가 있습니다. 자세한 내용은 단원을 참조하십시오[SimSpace Weaver 엔드포인트 및 할당량](service-quotas.md). SimSpace Weaver 는 작업자의 앱 수가이 제한을 초과할 때 1명 이상의 작업자만 사용합니다.는 사용 가능한 작업자 중 하나에 앱을 배치할 SimSpace Weaver 수 있습니다. 특정 작업자에 대한 앱 배치는 보장되지 않습니다.

다음 스키마 스니펫은 작업자 둘을 요청하는 시뮬레이션의 구성을 보여줍니다. SimSpace Weaver 는 앱 수가 한 작업자의 최대 앱 수를 초과하는 경우 두 번째 작업자 할당을 시도합니다.

```
workers:
  MyComputeWorkers:
    type: "sim.c5.24xlarge"
    desired: 2
```

# 클럭
<a name="working-with_configuring-simulation_clock"></a>

이 `clock` 섹션에서는 시뮬레이션 클럭의 속성을 지정합니다. 현재는 **틱 속도**(클럭이 앱으로 보내는 초당 틱 수)만 구성할 수 있습니다. 틱 속도는 최대 속도입니다. 틱에 대한 모든 작업(예: 엔터티 업데이트)이 완료되어야 다음 틱이 시작되기 때문에 유효 틱 속도는 더 낮을 수 있습니다. 틱 속도는 **클럭 속도**라고도 합니다.

`tick_rate`의 유효한 값은 스키마에 지정된 `sdk_version`에 따라 달라집니다.

**틱 속도의 유효 값**
+ `"1.14"` 이전 버전:
  + `10`
  + `15`
  + `30`
+ `"1.14"` 이후 이상:
  + `"10"`
  + `"15"`
  + `"30"`
  + `"unlimited"`

    자세한 내용은 [무제한 틱 속도](#working-with_configuring-simulation_clock_unlimited) 단원을 참조하십시오.

**중요**  
`"1.14"` 이전 `sdk_version`의 스키마의 경우 `tick_rate` 값은 `30`과 같은 **정수**입니다.
`"1.14"` 이후 `sdk_version`의 스키마의 경우 `tick_rate` 값은 `"30"`과 같은 **문자열**입니다. 값에는 **큰따옴표가 포함되어야 합니다**.  
버전 `"1.12"` 또는 `"1.13"` 스키마를 `"1.14"` 버전 이상으로 변환하는 경우 `tick_rate`의 값을 큰따옴표로 묶어야 합니다.

## 무제한 틱 속도
<a name="working-with_configuring-simulation_clock_unlimited"></a>

`tick_rate`를 `"unlimited"`로 설정하여 코드를 실행할 수 있는 속도만큼 빠르게 시뮬레이션을 실행할 수 있습니다. 무제한 틱 속도를 사용하면 모든 앱이 현재 틱에 대한 커밋을 완료한 직후 다음 틱을 SimSpace Weaver 보냅니다.

**중요**  
1.14.0 이전 SimSpace Weaver 버전에서는 무제한 틱 속도가 지원되지 않습니다. 스키마 `sdk_version`의 최소값은 `"1.14"`입니다.

**SimSpace Weaver Local의 무제한 틱 속도**  
SimSpace Weaver Local은 스키마가 10kHz(10000)의 틱 속도를 지정한 것처럼 `"unlimited"`를 구현합니다. 효과는 AWS 클라우드에서 무제한 틱 속도를 적용한 것과 같습니다. 여전히 스키마에 `tick_rate: "unlimited"`를 지정합니다. SimSpace Weaver Local에 대한 자세한 정보는 [의 로컬 개발 SimSpace Weaver](working-with_local-development.md) 섹션을 참조하세요.

## 클럭에 대한 FAQ
<a name="working-with_configuring-simulation_clock_faq"></a>

### Q1. 다른 틱 속도를 사용하도록 STARTED 시뮬레이션을 변경할 수 있나요?
<a name="working-with_configuring-simulation_clock_faq_q1"></a>

수명 주기의 어떤 단계에서도 AWS 클라우드 에 이미 존재하는 시뮬레이션의 틱 속도를 변경할 수 없습니다. 또한 SimSpace Weaver Local에서 실행 중인 시뮬레이션의 틱 속도도 변경할 수 없습니다. 스키마에서 `tick_rate`를 설정하고 해당 스키마에서 새 시뮬레이션을 시작할 수 있습니다.

### Q2. 1.14 이전 버전에서 무제한 틱 속도로 시뮬레이션을 실행할 수 있나요?
<a name="working-with_configuring-simulation_clock_faq_q2"></a>

아니요, 1.14.0 이전 버전에서는 무제한 틱 속도가 지원되지 않습니다.

## 클럭 오류 해결
<a name="working-with_configuring-simulation_clock_troubleshooting"></a>

시뮬레이션이 시작되지 않는 경우 **DescribeSimulation** API의 출력에서 `"StartError"` 값을 확인할 수 있습니다. 스키마의 `tick_rate` 값이 유효하지 않으면 다음과 같은 오류가 발생합니다.

**참고**  
여기에 표시된 오류 출력은 가독성을 높이기 위해 여러 줄에 표시됩니다. 실제 오류 출력은 한 줄입니다.
+ `sdk_version`은 `"1.14"` 이전이고 `tick_rate` 값은 유효하지 않은 정수입니다. 유효한 값: `10`, `15`, `30` 

  ```
  "[{\"errorType\":\"SchemaFormatInvalid\",\"errorMessage\":
      \"$.clock.tick_rate: does not have a value in the enumeration [10, 15, 30]\"}]"
  ```
+ `sdk_version`은 `"1.14"` 이전이고 `tick_rate` 값은 문자열입니다. 유효한 값: `10`, `15`, `30` 

  ```
  "[{\"errorType\":\"SchemaFormatInvalid\",\"errorMessage\":
      \"$.clock.tick_rate: does not have a value in the enumeration [10, 15, 30]\"},
      {\"errorType\":\"SchemaFormatInvalid\",
      \"errorMessage\":\"$.clock.tick_rate: string found, integer expected\"}]"
  ```
+ `sdk_version`은 `"1.14"` 이후이고 `tick_rate` 값은 유효하지 않은 문자열입니다. 유효값: `"10"`, `"15"`, `"30"`, `"unlimited"` 

  ```
  "[{\"errorType\":\"SchemaFormatInvalid\",\"errorMessage\":
      \"$.clock.tick_rate: does not have a value in the enumeration [10, 15, 30, unlimited]\"}]"
  ```
+ `sdk_version`은 `"1.14"` 이후이고 `tick_rate` 값은 정수입니다. 유효값: `"10"`, `"15"`, `"30"`, `"unlimited"` 

  ```
  "[{\"errorType\":\"SchemaFormatInvalid\",\"errorMessage\":
      \"$.clock.tick_rate: does not have a value in the enumeration [10, 15, 30, unlimited]\"},
      {\"errorType\":\"SchemaFormatInvalid\",
      \"errorMessage\":\"$.clock.tick_rate: integer found, string expected\"}]"
  ```

# 파티셔닝 전략
<a name="working-with_configuring-simulation_partitioning-strategies"></a>

`partitioning_strategies` 섹션에서는 공간 앱 파티션의 구성 속성을 지정합니다. 파티셔닝 전략에 고유한 이름(이 섹션의 속성 세트)을 입력하고 공간 앱 구성에 해당 이름을 사용합니다.

```
partitioning_strategies:
  MyGridPartitioning:
    topology: "Grid"
    aabb_bounds:
      x: [0, 1000]
      y: [0, 1000]
    grid_placement_groups:
      x: 1
      y: 1
```

`topology` 속성은 시뮬레이션에서 사용하는 좌표계의 유형을 지정합니다. `Grid` 값은 2차원(2D) 그리드를 지정합니다.

`Grid` 토폴로지의 경우 시뮬레이션 공간은 축으로 정렬된 경계 상자 (AABB)로 모델링됩니다. `aabb_bounds` 속성에서 AABB의 각 축에 대한 좌표 경계를 지정합니다. 시뮬레이션에서 공간적으로 존재하는 모든 엔터티는 AABB 내에 위치해야 합니다.

## 그리드 배치 그룹
<a name="working-with_configuring-simulation_partitioning-strategies_placement-groups"></a>

**배치 그룹은** 동일한 작업자에 배치 SimSpace Weaver 하려는 공간 앱 파티션의 모음입니다. `grid_placement_groups` 속성에서 배치 그룹(그리드 내)의 수와 배열을 지정합니다. SimSpace Weaver 는 배치 그룹 전체에 파티션을 균등하게 분산하려고 시도합니다. 동일한 배치 그룹에 파티션이 있는 공간 앱의 소유권 영역은 공간적으로 인접합니다.

x\$1 y는 원하는 작업자 수와 같게 설정하는 것이 좋습니다. 같지 않은 경우 SimSpace Weaver 는 사용 가능한 작업자 간에 배치 그룹의 균형을 맞추려고 시도합니다.

배치 그룹 구성을 지정하지 않으면가 자동으로 배치 그룹 구성을 SimSpace Weaver 계산합니다.

# 도메인
<a name="working-with_configuring-simulation_domains"></a>

도메인에 대한 구성 속성 집합의 이름을 제공합니다. 도메인의 앱 시작 설정에 따라 도메인 유형이 결정됩니다.
+ **`launch_apps_via_start_app_call`** - 사용자 지정 도메인
+ **`launch_apps_by_partitioning_strategy`** - 공간 도메인
+ **`launch_apps_per_worker`**(샘플 애플리케이션에 포함되지 않음) - 서비스 도메인

**중요**  
SimSpace Weaver 는 각 시뮬레이션에 대해 최대 5개의 도메인을 지원합니다. 여기에는 모든 공간, 사용자 지정 및 서비스 도메인이 포함됩니다.

```
domains:
  MyViewDomain:
    launch_apps_via_start_app_call: {}
    app_config:
      package: "s3://weaver-myproject-111122223333-us-west-2/MyViewApp.zip"
      launch_command: ["MyViewApp"]
      required_resource_units:
        compute: 1
      endpoint_config:
        ingress_ports:
          - 7000
  MySpatialDomain:
    launch_apps_by_partitioning_strategy:
      partitioning_strategy: "MyGridPartitioning"
      grid_partition:
        x: 2
        y: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-us-west-2/MySpatialApp.zip"
      launch_command: ["MySpatialApp"]
      required_resource_units:
        compute: 1
```

**참고**  
SimSpace Weaver 앱 SDK 버전 1.12.x 프로젝트는 앱 .zip 파일 및 스키마에 대해 별도의 버킷을 사용합니다.  
weaver-*lowercase-project-name*-*account-number*-app-zips-*region*
weaver-*lowercase-project-name*-*account-number*-schemas-*region*

**Topics**
+ [앱 구성](working-with_configuring-simulation_domains_app-config.md)
+ [공간 도메인 구성](working-with_configuring-simulation_domains_spatial.md)
+ [네트워크 엔드포인트](working-with_configuring-simulation_domains_endpoints.md)
+ [서비스 도메인 구성](working-with_configuring-simulation_domains_service-domains.md)

# 앱 구성
<a name="working-with_configuring-simulation_domains_app-config"></a>

앱(`app_config`)의 구성을 해당 도메인 구성의 일부로 지정합니다. 모든 유형의 도메인은 이와 동일한 앱 구성 속성을 사용합니다.

```
    app_config:
      package: "s3://weaver-myproject-111122223333-us-west-2/MyViewApp.zip"
      launch_command: ["MyViewApp"]
      required_resource_units:
        compute: 1
```

**참고**  
SimSpace Weaver 앱 SDK 버전 1.12.x 프로젝트는 앱 .zip 파일 및 스키마에 대해 별도의 버킷을 사용합니다.  
weaver-*lowercase-project-name*-*account-number*-app-zips-*region*
weaver-*lowercase-project-name*-*account-number*-schemas-*region*

`package` 속성은 S3 버킷의 zip 파일 S3 URI을 지정합니다. zip 파일에는 앱 실행 파일(*바이너리*라고도 함) 및 필요한 기타 리소스(예: 라이브러리)가 들어 있습니다. 앱 실행 파일의 각 인스턴스는 작업자의 Docker 컨테이너에서 실행됩니다.

`launch_command` 속성은 실행 파일의 이름과 앱을 실행하는 데 필요한 모든 명령줄 옵션을 지정합니다. `launch_command`의 값은 배열입니다. 전체 시작 명령 문자열의 각 토큰은 배열의 한 요소입니다.

**예제**
+ 시작 명령의 경우: `MyTestApp --option1 value1`
+ `launch_command: ["MyTestApp", "-option1", "value1"]`를 지정합니다.

`required_resource_units` 속성은이 앱에 할당 SimSpace Weaver 해야 하는 컴퓨팅 리소스 단위 수를 지정합니다. 컴퓨팅 리소스 단위는 작업자의 고정된 처리 용량 (vCPU) 및 메모리 (RAM) 양입니다. 이 값을 늘려 앱이 작업자에서 실행될 때 사용할 수 있는 컴퓨팅 성능의 양을 늘릴 수 있습니다. 각 작업자에는 제한된 수의 컴퓨팅 리소스 단위가 있습니다. 자세한 내용은 [SimSpace Weaver 엔드포인트 및 할당량](service-quotas.md) 단원을 참조하십시오.

# 공간 도메인 구성
<a name="working-with_configuring-simulation_domains_spatial"></a>

공간 도메인의 경우 `partitioning_strategy`를 지정해야 합니다. 이 속성의 값은 스키마의 다른 부분에서 정의한 분할 전략에 지정한 이름입니다.

```
  MySpatialDomain:
    launch_apps_by_partitioning_strategy:
      partitioning_strategy: "MyGridPartitioning"
      grid_partition:
        x: 2
        y: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-us-west-2/MySpatialApp.zip"
      launch_command: ["MySpatialApp"]
      required_resource_units:
        compute: 1
```

**참고**  
SimSpace Weaver 앱 SDK 버전 1.12.x 프로젝트는 앱 .zip 파일 및 스키마에 대해 별도의 버킷을 사용합니다.  
weaver-*lowercase-project-name*-*account-number*-app-zips-*region*
weaver-*lowercase-project-name*-*account-number*-schemas-*region*

`Grid` 토폴로지(이 릴리스에서 지원되는 유일한 토폴로지)가 있는 파티셔닝 전략은 그리드에서이 도메인의 공간 앱 파티션을 정렬 SimSpace Weaver 하도록 지시합니다. `grid_partition` 속성은 파티션 그리드의 행과 열 수를 지정합니다.

SimSpace Weaver 는 파티션 그리드의 각 셀에 대해 공간 앱의 인스턴스 1개를 시작합니다. 예를 들어 공간 도메인에 `x: 2` 및 `grid_partition` 값이 있는 경우 공간 도메인에는 2 \$1 2 = 4개의 파티션이 `y: 2`있습니다. SimSpace Weaver 는 공간 도메인에 구성된 앱 인스턴스 4개를 시작하고 각 앱 인스턴스에 파티션 1개를 할당합니다.

**주제**
+ [공간 도메인의 리소스 요구 사항](#working-with_configuring-simulation_domains_spatial_resources)
+ [공간 도메인 정보](#working-with_configuring-simulation_domains_spatial_multiple)
+ [공간 도메인에 대한 FAQ](#working-with_configuring-simulation_domains_spatial_faq)
+ [공간 도메인 문제 해결](#working-with_configuring-simulation_domains_spatial_troubleshooting)

## 공간 도메인의 리소스 요구 사항
<a name="working-with_configuring-simulation_domains_spatial_resources"></a>

각 작업자에 최대 17개의 컴퓨팅 리소스 단위를 할당할 수 있습니다. 공간 도메인 `app_config` 섹션에서 각 공간 앱이 사용하는 컴퓨팅 리소스 단위 수를 지정합니다.

**Example 공간 앱의 컴퓨팅 리소스 단위를 보여주는 스키마 스니펫**  

```
  MySpatialDomain:
    launch_apps_by_partitioning_strategy:
      partitioning_strategy: "MyGridPartitioning"
      grid_partition:
        x: 2
        y: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-artifacts-us-west-2/MySpatialApp.zip"
      launch_command: ["MySpatialApp"]
      required_resource_units:
        compute: 1
```

도메인에 필요한 컴퓨팅 리소스 단위의 수를 계산하려면 그리드의 셀 수(`grid_partition`, `x` \$1`y`)에 공간 앱에 할당된 컴퓨팅 리소스 단위의 수를 곱합니다.

이전 예제에서 도메인 `MySpatialDomain`는 다음을 지정했습니다.
+ `x`: `2`
+ `y`: `2`
+ `compute`: `1`

`MySpatialDomain` 그리드는 2\$1 2 = 4셀입니다. 공간 도메인에는 4\$1 1 = 4 컴퓨팅 리소스 단위가 필요합니다.

스키마에 지정된 모든 도메인의 총 컴퓨팅 리소스 단위 수는 작업자의 `desired` 수에 각 작업자의 최대 컴퓨팅 리소스 단위 수(17)를 곱한 값보다 작거나 같아야 합니다.

## 공간 도메인 정보
<a name="working-with_configuring-simulation_domains_spatial_multiple"></a>

둘 이상의 공간 도메인을 사용하도록 시뮬레이션을 구성할 수 있습니다. 예를 들어, 하나의 공간 도메인을 사용하여 시뮬레이션의 주요 작업자(예: 사람, 자동차)를 제어하고 다른 공간 도메인을 사용하여 환경을 제어할 수 있습니다.

또한 여러 공간 도메인을 사용하여 시뮬레이션의 각 부분에 서로 다른 리소스를 할당할 수 있습니다. 예를 들어 시뮬레이션에 다른 유형보다 10배 더 많은 엔터티 인스턴스가 있는 엔터티 유형이 있는 경우 각 엔터티 유형을 처리하는 다른 도메인을 만들고 엔터티가 더 많은 도메인에 더 많은 리소스를 할당할 수 있습니다.

**중요**  
SimSpace Weaver 1.14.0 이전 버전은 여러 공간 도메인을 지원하지 않습니다.

**중요**  
AWS SimSpace Weaver Local는 현재 여러 공간 도메인을 지원하지 않습니다. SimSpace Weaver Local에 대한 자세한 정보는 [의 로컬 개발 SimSpace Weaver](working-with_local-development.md) 섹션을 참조하세요.

**중요**  
SimSpace Weaver 는 각 시뮬레이션에 대해 최대 5개의 도메인을 지원합니다. 여기에는 모든 공간, 사용자 지정 및 서비스 도메인이 포함됩니다.

### 여러 공간 도메인 구성
<a name="working-with_configuring-simulation_domains_spatial_multiple_configure"></a>

두 개 이상의 공간 도메인을 구성하려면 다른 공간 도메인 정의를 스키마에 별도의 명명된 섹션으로 추가합니다. 각 도메인은 `launch_apps_by_partitioning_strategy` 키를 지정해야 합니다. 다음의 스키마 예제를 참조하세요.

```
sdk_version: "1.14"
workers:
  MyComputeWorkers:
    type: "sim.c5.24xlarge"
    desired: 1
clock:
  tick_rate: "30"
partitioning_strategies:
  MyGridPartitioning:
    topology: Grid
    aabb_bounds:
      x: [0, 1000]
      y: [0, 1000]
domains:
  MySpatialDomain:
    launch_apps_by_partitioning_strategy:
      partitioning_strategy: "MyGridPartitioning"
      grid_partition:
        x: 2
        y: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-artifacts-us-west-2/MySpatialApp.zip"
      launch_command: ["MySpatialApp"]
      required_resource_units:
        compute: 1
  MySecondSpatialDomain:
    launch_apps_by_partitioning_strategy:
      partitioning_strategy: "MyGridPartitioning"
      grid_partition:
        x: 2
        y: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-artifacts-us-west-2/MySpatialApp2.zip"
      launch_command: ["MySpatialApp2"]
      required_resource_units:
        compute: 1
```

### 공간 도메인을 함께 배치
<a name="working-with_configuring-simulation_domains_spatial_multiple_placement"></a>

일부 시나리오에서는 작업자의 공간 도메인용 파티션을 다른 도메인의 파티션 옆에 배치할 수 있습니다. 그러면 파티션이 서로에 대한 크로스 도메인 구독을 생성하는 경우 성능 특성이 향상될 수 있습니다.

`placement_constraints` 스키마에 최상위 키를 추가하여 함께 배치 SimSpace Weaver 할 도메인을 지정합니다. 필수 `on_workers` 키는 스키마의 명명된 `workers` 구성을 참조해야 합니다.

**Example 함께 배치된 공간 도메인을 보여주는 스키마 스니펫**  

```
workers:
  MyComputeWorkers:
    type: "sim.c5.24xlarge"
    desired: 2
placement_constraints:
  - placed_together: ["MySpatialDomain", "MySecondSpatialDomain"]
    on_workers: ["MyComputeWorkers"]
```

**중요**  
배치 그룹을 사용하는 경우 다음을 수행합니다.  
x \$1 y가 작업자 수의 배수인지 확인합니다.
배치 그룹 값이 함께 배치하는 도메인의 그리드 크기를 나타내는 공약수인지 확인합니다.
배치 그룹을 **사용하지 않는** 경우 다음을 수행합니다.  
공간 도메인 그리드의 한 축에 작업자 수와 동일한 공약수가 있는지 확인합니다.
배치 그룹에 대한 자세한 내용은 [파티셔닝 전략](working-with_configuring-simulation_partitioning-strategies.md#working-with_configuring-simulation_partitioning-strategies_placement-groups) 섹션을 참조하세요.

## 공간 도메인에 대한 FAQ
<a name="working-with_configuring-simulation_domains_spatial_faq"></a>

### Q1. 기존 시뮬레이션에 다른 공간 도메인을 추가하려면 어떻게 해야 하나요?
<a name="working-with_configuring-simulation_domains_spatial_faq_q1"></a>
+ **실행 중인 시뮬레이션의 경우** - 실행 중인 시뮬레이션의 구성을 변경할 수 없습니다. 스키마에서 도메인 구성을 변경하고, 스키마와 앱 zip을 업로드하고, 새 시뮬레이션을 시작합니다.
+ **새 시뮬레이션의 경우** - 스키마에서 도메인 구성을 추가하고, 스키마와 앱 zip을 업로드하고, 새 시뮬레이션을 시작합니다.

## 공간 도메인 문제 해결
<a name="working-with_configuring-simulation_domains_spatial_troubleshooting"></a>

시뮬레이션을 시작하려고 했지만 도메인 구성이 유효하지 않은 경우 다음의 오류가 발생할 수 있습니다.

```
"StartError": "[{\"errorType\":\"SchemaFormatInvalid\",\"errorMessage\":
    \"We were unable to determine an arrangement of your domains that would fit 
    within the provided set of workers. This can generally be resolved by 
    increasing the number of workers if able, decreasing your domains\u0027 
    [\u0027\u0027grid_partition\u0027\u0027] values, or adjusting the 
    dimensions of your [\u0027\u0027grid_placement_groups\u0027\u0027].\"}]"
```

**예상 원인**
+ 스키마는 작업자가 사용할 수 있는 것보다 더 많은 컴퓨팅 리소스 단위를 앱에 할당합니다.
+ SimSpace Weaver 는 작업자에 도메인을 함께 배치하는 배열을 결정할 수 없습니다. 이는 공간 도메인을 여러 개 지정했지만 도메인 그리드 사이에 공약수나 배수가 없는 경우(예: 2x4 그리드와 3x5 그리드 사이)에 발생합니다.

# 네트워크 엔드포인트
<a name="working-with_configuring-simulation_domains_endpoints"></a>

사용자 지정 및 서비스 앱에는 외부 클라이언트가 연결할 수 있는 네트워크 엔드포인트가 있을 수 있습니다. 포트 번호 목록을 `endpoint_config`의 `ingress_ports` 값으로 지정합니다. 이러한 포트 번호는 모두 TCP와 UDP입니다. 사용자 지정 또는 서비스 앱은에서 지정한 포트 번호에 바인딩되어야 합니다`ingress_ports`.는 런타임 시 포트 번호를 SimSpace Weaver 동적으로 할당하고 이러한 포트를 동적 포트에 매핑합니다. 앱이 동적(실제) 포트 번호를 찾기 시작한 후 **describe-app** API를 호출할 수 있습니다. 자세한 내용은 빠른 시작 자습서의 [사용자 지정 앱의 IP 주소 및 포트 번호 가져오기IP 주소 및 포트 번호 가져오기](working-with_get-ip.md) 섹션을 참조하세요.

```
domains:
  MyViewDomain:
    launch_apps_via_start_app_call: {}
    app_config:
      package: "s3://weaver-myproject-111122223333-us-west-2/MyViewApp.zip"
      launch_command: ["MyViewApp"]
      required_resource_units:
        compute: 1
      endpoint_config:
        ingress_ports:
          - 7000
```

**참고**  
SimSpace Weaver 앱 SDK 버전 1.12.x 프로젝트는 앱 .zip 파일 및 스키마에 대해 별도의 버킷을 사용합니다.  
weaver-*lowercase-project-name*-*account-number*-app-zips-*region*
weaver-*lowercase-project-name*-*account-number*-schemas-*region*

**참고**  
`endpoint_config`는 사용자 지정 앱 및 서비스 앱의 선택적 속성입니다. `endpoint_config`를 지정하지 않으면 앱에 네트워크 엔드포인트가 없습니다.

# 서비스 도메인 구성
<a name="working-with_configuring-simulation_domains_service-domains"></a>

도메인 구성`launch_apps_per_worker:`에가 있으면 서비스 앱이 있는 서비스 도메인임을 나타냅니다.는 서비스 앱을 SimSpace Weaver 시작하고 중지합니다. 가 앱을 SimSpace Weaver 시작하고 중지하면 앱에 *관리형 수명 주기*가 있는 것으로 간주됩니다. SimSpace Weaver 현재는 각 작업자에서 1개 또는 2개의 서비스 앱 시작을 지원합니다.

**Example 각 작업자에서 1개의 서비스 앱을 실행하도록 구성된 도메인의 예제**  

```
domains:
  MyServiceDomain:
    launch_apps_per_worker:
      count: 1
    app_config:
      package: "s3://weaver-myproject-111122223333-app-zips-us-west-2/PlayerConnectionServiceApp.zip"
      launch_command: ["PlayerConnectionServiceApp"]
      required_resource_units:
        compute: 1
      endpoint_config:
        ingress_ports:
          - 9000
          - 9001
```

**Example 각 작업자에서 2개의 서비스 앱을 실행하도록 구성된 도메인의 예제**  

```
domains:
  MyServiceDomain:
    launch_apps_per_worker:
      count: 2
    app_config:
      package: "s3://weaver-myproject-111122223333-app-zips-us-west-2/PlayerConnectionServiceApp.zip"
      launch_command: ["PlayerConnectionServiceApp"]
      required_resource_units:
        compute: 1
      endpoint_config:
        ingress_ports:
          - 9000
          - 9001
```

# 시뮬레이션의 최대 지속 시간
<a name="working-with_max-duration"></a>

의 각 시뮬레이션 AWS SimSpace Weaver 에는 시뮬레이션을 실행할 수 있는 최대 시간을 지정하는 최대 *기간* 설정이 있습니다. 시뮬레이션을 시작할 때 최대 지속 시간을 파라미터로 제공합니다. [`StartSimulation` 애플리케이션 프로그래밍 인터페이스(API)](https://docs.aws.amazon.com/simspaceweaver/latest/APIReference/API_StartSimulation.html)에는 선택적 파라미터 `MaximumDuration`이 있습니다. 파라미터 값은 분(m 또는 M), 시간(h 또는 H) 또는 일(d 또는 D)입니다. 예를 들어, `1h` 또는 `1H`는 1시간을 의미합니다. SimSpace Weaver 은 이 제한에 도달하면 시뮬레이션을 중지합니다.

## 최대값
<a name="working-with_max-duration_max-value"></a>

`MaximumDuration`에 대한 가장 높은 유효 값은 `14D` 또는 이에 상응하는 시간(`336H`) 또는 분(`20160M`)입니다.

## 기본값
<a name="working-with_max-duration_default-value"></a>

`MaximumDuration` 파라미터는 선택 항목입니다. 값을 제공하지 않으면는 값을 SimSpace Weaver 사용합니다`14D`.

## 최소값
<a name="working-wtih_max-duration_min-value"></a>

`MaximumDuration`에 대한 가장 낮은 유효 값은 수치적으로 `0`에 상응하는 값입니다. 예를 들어,`0M`, `0H`, `0D` 값은 모두 수치적으로 `0`에 상응합니다.

최대 지속 시간에 대한 최소값을 제공하면 시뮬레이션이 `STOPPING` 상태에 도달하는 즉시 `STARTED` 상태로 전환됩니다.

## 콘솔을 사용하여 시뮬레이션 시작
<a name="working-with_max-duration_console"></a>

[SimSpace Weaver 콘솔에서](https://console.aws.amazon.com/simspaceweaver) 시뮬레이션을 시작할 때 **최대 지속 시간** 값을 제공할 수 있습니다. **시뮬레이션 시작**을 선택할 때 **시뮬레이션 설정** 양식의 **최대 지속 시간** 필드에 값을 입력합니다.

**중요**  
**최대 지속 시간**에 값을 입력하지 않으면 SimSpace Weaver 에서 [기본값](#working-with_max-duration_default-value)(`14D`)을 사용합니다.

## 최대 지속 시간에 도달한 시뮬레이션의 상태
<a name="working-with_max-duration_sim-state"></a>

가 최대 지속 시간에 도달한 시뮬레이션을 SimSpace Weaver 자동으로 중지하면 시뮬레이션의 **상태는** `STOPPING` (진행 중인 경우) 또는 입니다`STOPPED`. [SimSpace Weaver 콘솔](https://console.aws.amazon.com/simspaceweaver)에서 시뮬레이션의 **대상 상태**가 계속 `STARTED` 상태로 표시되는 이유는 사용자가 요청한 마지막 상태이기 때문입니다.

# 앱 개발
<a name="working-with_developing-apps"></a>

SimSpace Weaver 개발에는 Amazon Linux에서 시뮬레이션이 실행되기 때문에 앱을 빌드하는 Amazon Linux 2 (AL2) 환경이 필요합니다AWS Cloud. 를 사용하는 경우 SimSpace Weaver 앱 SDK의 스크립트를 사용하여 SimSpace Weaver 앱을 빌드하는 데 필요한 종속성AL2으로 실행되는 Docker 컨테이너를 생성하고 시작할 Windows수 있습니다. Windows Subsystem for Linux (WSL)를 사용하여 AL2 환경을 시작하거나 네이티브 AL2 시스템을 사용할 수도 있습니다. 자세한 내용은 [에 대한 로컬 환경 설정 SimSpace Weaver](setting-up_local.md) 단원을 참조하십시오.

**참고**  
로컬 개발 환경을 구성하는 방법에 관계없이 AWS 클라우드에서 실행되도록 앱을 업로드하면 앱이 Docker 컨테이너에서 실행됩니다. **앱은 호스트 운영 체제에 직접 액세스할 수 없습니다**.

**SimSpace Weaver 앱의 일반 흐름**

1. 애플리케이션을 생성합니다.

1. 루프:

   1. `Transaction`을 생성하여 업데이트를 시작합니다.

      1. 시뮬레이션이 종료되면 루프를 종료합니다.

   1. 구독 및 소유권 엔터티 이벤트를 처리합니다.

   1. 시뮬레이션을 업데이트합니다.

   1. 업데이트를 종료하려면 `Transaction`을 커밋합니다.

1. 애플리케이션을 삭제합니다.

## 공간 앱
<a name="working-with_developing-apps_spatial-apps"></a>

각 공간 앱에는 시뮬레이션 세계의 공간 영역인 소유권 영역이 있습니다. 공간 앱의 소유권 영역에 있는 엔터티는 앱에 할당된 파티션에 저장됩니다. 단일 공간 앱은 할당된 파티션 내의 모든 엔터티에 대한 완전한 소유권(읽기 및 쓰기 권한)을 갖습니다. 다른 앱은 이러한 엔터티에 쓸 수 없습니다. 공간 앱은 해당 엔터티의 상태를 발전시킵니다. 각 공간 앱은 1개의 파티션만 소유합니다. SimSpace Weaver 는 엔터티의 공간적 위치를 사용하여 엔터티를 인덱싱하고 공간 앱 파티션에 할당합니다.

 SimSpace Weaver 앱 SDK는 샘플 애플리케이션을 제공합니다. 다음 폴더에서 샘플 애플리케이션의 공간 앱에 대한 소스 코드를 찾을 수 있습니다(운영 체제에 적합한 경로 구분자 사용).

```
sdk-folder\Samples\PathfindingSample\src\SpatialApp
```

## 사용자 지정 앱
<a name="working-with_developing-apps_custom-apps"></a>

사용자 지정 앱을 만들고 사용하여 시뮬레이션과 상호 작용합니다.

**사용자 지정 앱은 다음을 수행할 수 있습니다.**
+ 엔터티 생성
+ 다른 파티션 구독
+ 변경 사항 커밋

**사용자 지정 앱의 일반적인 흐름**

1. 애플리케이션을 생성합니다.

1. 다음과 같이 시뮬레이션에서 특정 리전을 구독합니다.

   1. `Transaction`을 생성하여 첫 번째 업데이트를 시작합니다.

   1. 특정 리전에 대한 구독을 생성합니다.

   1. 첫 번째 업데이트를 종료하려면 `Transaction`을 커밋합니다.

1. 루프:

   1. `Transaction`을 생성하여 업데이트를 시작합니다.

      1. 시뮬레이션이 종료되면 루프를 종료합니다.

   1. 프로세스 상태가 변경됩니다.

   1. 업데이트를 종료하려면 `Transaction`을 커밋합니다.

1. 애플리케이션을 삭제합니다.

사용자 지정 앱이 개체를 생성한 후 개체가 시뮬레이션 내에 공간적으로 존재하도록 하려면 개체를 공간 도메인으로 전송해야 합니다.는 개체의 공간 위치를 SimSpace Weaver 사용하여 개체를 적절한 공간 앱 파티션에 배치합니다. 엔터티를 만든 사용자 지정 앱은 엔터티를 공간 도메인으로 전송한 후에는 엔터티를 업데이트하거나 삭제할 수 없습니다.

 SimSpace Weaver 앱 SDK는 샘플 애플리케이션을 제공합니다. 샘플 애플리케이션에 포함된 사용자 지정 앱을 자체 사용자 지정 앱의 모델로 사용할 수 있습니다. 다음 폴더에서 샘플 애플리케이션의 보기 앱(사용자 지정 앱)에 대한 소스 코드를 찾을 수 있습니다(운영 체제에 올바른 경로 구분 기호 사용).

```
sdk-folder\Samples\PathfindingSample\src\ViewApp
```

# 클라이언트 애플리케이션 개발
<a name="working-with_developing-client-applications"></a>

클라이언트를 시뮬레이션에 연결해야 하는 몇 가지 이유는 다음과 같습니다.
+ 도시 규모의 시뮬레이션에 실시간 교통 정보를 주입합니다.
+ 인간 운영자가 시뮬레이션의 일부 측면을 제어하는 *human-in-the-loop* 시뮬레이션을 만듭니다.
+ 훈련 시뮬레이션과 같이 사용자가 시뮬레이션과 상호 작용할 수 있도록 합니다.

이 예제의 사용자 지정 앱은 시뮬레이션 상태와 외부 세계 사이의 인터페이스 역할을 합니다. 클라이언트는 사용자 지정 앱에 연결하여 시뮬레이션과 상호 작용합니다.

SimSpace Weaver 는 클라이언트 애플리케이션과 사용자 지정 앱과의 통신을 처리하지 않습니다. 클라이언트 애플리케이션의 설계, 생성, 운영, 보안 및 사용자 지정 앱과의 커뮤니케이션은 사용자의 책임입니다. SimSpace Weaver 는 클라이언트가 연결할 수 있도록 각 사용자 지정 앱의 IP 주소 및 포트 번호만 노출합니다.

 SimSpace Weaver 앱 SDK는 샘플 애플리케이션을 위한 클라이언트를 제공합니다. 이러한 클라이언트를 자체 클라이언트 애플리케이션의 모델로 사용할 수 있습니다. 다음 폴더에서 샘플 애플리케이션 클라이언트의 소스 코드를 찾을 수 있습니다.

------
#### [ Docker ]

```
sdk-folder\packaging-tools\clients\PathfindingSampleClients
```

------
#### [ WSL ]

**중요**  
편의를 위해 이 지침을 제공합니다. 이 지침은 Windows Subsystem for Linux (WSL)와 함께 사용할 수 있도록 지원되지 않습니다. 자세한 내용은 [에 대한 로컬 환경 설정 SimSpace Weaver](setting-up_local.md) 단원을 참조하십시오.

```
sdk-folder/packaging-tools/clients/PathfindingSampleClients
```

------

샘플 애플리케이션 클라이언트를 빌드하고 사용하는 방법에 대한 자세한 내용은의 자습서를 참조하세요[시작하기 SimSpace Weaver](getting-started.md).

# 사용자 지정 앱의 IP 주소 및 포트 번호 가져오기
<a name="working-with_get-ip"></a>

시뮬레이션을 보려면 사용자 지정 앱을 생성하고 클라이언트를 사용하여 연결합니다. 자세한 내용은의 자습서를 참조하세요[시작하기 SimSpace Weaver](getting-started.md). 다음 절차에 따라 사용자 지정 앱의 IP 주소와 포트 번호를 가져올 수 있습니다. 운영 체제에 적합한 경로 구분자를 사용합니다(예: `\` Windows의 경우 , Linux`/`의 경우 ).

**IP 주소 및 포트 번호 가져오기**

1. **ListSimulation** API를 사용하여 시뮬레이션 이름을 가져올 수 있습니다.

   ```
   aws simspaceweaver list-simulations
   ```

   출력 예시:

   ```
   {
       "Simulations": [
           {
               "Status": "STARTED",
               "CreationTime": 1664921418.09,
               "Name": "MyProjectSimulation_22-10-04_22_10_15",
               "Arn": "arn:aws:simspaceweaver:us-west-2: 111122223333:simulation/MyProjectSimulation_22-10-04_22_10_15",
               "TargetStatus": "STARTED"
           }
       ]
   
   }
   ```

1. **DescribeSimulation** API를 사용하여 시뮬레이션의 도메인 목록을 가져옵니다.

   ```
   aws simspaceweaver describe-simulation --simulation simulation-name
   ```

   출력의 `LiveSimulationState` 섹션에서 `Domains` 섹션을 찾습니다.

   출력 예시:

   ```
       "LiveSimulationState": {
           "Domains": [
               {
                   "Type": "",
                   "Name": "MySpatialSimulation",
                   "Lifecycle": "Unknown"
               },
               {
                   "Type": "",
                   "Name": "MyViewDomain",
                   "Lifecycle": "ByRequest"
               }
           ],
   ```

1. **ListApps** API를 사용하여 도메인의 사용자 지정 앱 목록을 가져옵니다. 예를 들어 샘플 프로젝트에서 뷰(사용자 지정) 앱의 도메인 이름은 입니다`MyViewDomain`. 출력에서 앱 이름을 찾습니다.

   ```
   aws simspaceweaver list-apps --simulation simulation-name --domain domain-name
   ```

   출력 예시:

   ```
    
   {
       "Apps": [
           {
               "Status": "STARTED",
               "Domain": "MyViewDomain",
               "TargetStatus": "STARTED",
               "Name": "ViewApp",
               "Simulation": "MyProjectSimulation_22-10-04_22_10_15"
           }
       ]
   }
   ```

1. **DescribeApp** API를 사용하여 IP 주소와 포트 번호를 가져옵니다. 샘플 프로젝트의 경우 도메인 이름은 `MyViewDomain`이고 앱 이름은 `ViewApp`입니다.

   ```
   aws simspaceweaver describe-app --simulation simulation-name --domain domain-name --app app-name
   ```

   IP 주소와 포트 번호는 출력의 `EndpointInfo` 블록에 있습니다. IP 주소는 `Address`의 값이고 포트 번호는 `Actual`의 값입니다.

   출력 예시:

   ```
   {
       "Status": "STARTED",
       "Domain": "MyViewDomain",
       "TargetStatus": "STARTED",
       "Simulation": "MyProjectSimulation_22-10-04_22_10_15",
       "LaunchOverrides": {
           "LaunchCommands": []
       },
       "EndpointInfo": {
           "IngressPortMappings": [
               {
                   "Declared": 7000,
                   "Actual": 4321
               }
           ],
           "Address": "198.51.100.135"
       },
       "Name": "ViewApp"
   }
   ```
**참고**  
`Declared`의 값은 앱 코드가 바인딩되어야 하는 포트 번호입니다. 의 값은가 클라이언트에 SimSpace Weaver 노출하여 앱에 연결하는 포트 번호`Actual`입니다.는 `Declared`포트를 `Actual` 포트에 SimSpace Weaver 매핑합니다.

# Unreal Engine 뷰 클라이언트 시작
<a name="working-with_unreal-client"></a>

 다음으로 이동합니다.

```
sdk-folder/Samples/PathfindingSample/tools/cloud
```

1. 다음 명령 중 하나를 실행합니다.
   + 도커: `python quick-start.py`
   + WSL: `python quick-start.py --al2`

1. IP 주소와 “실제” 포트 번호를 가져옵니다. 이는 quick-start.py 실행하여 콘솔 출력에 있거나의 절차에 따라 가져옵니다[사용자 지정 앱의 IP 주소 및 포트 번호 가져오기IP 주소 및 포트 번호 가져오기](working-with_get-ip.md).

1.  다음으로 이동합니다.

   ```
   sdk-folder/Clients/TCP/UnrealClient/lib
   ```

1.  다음 명령을 실행하여 NNG 라이브러리를 빌드합니다.

   ```
   cmake -S . -B build 
   cmake --build build --config RelWithDebInfo 
   cmake --install build
   ```

1.  **텍스트 편집기**에서 `view_app_url.txt`를 엽니다.

1.  URL을 뷰 앱의 IP 주소 및 포트 번호로 업데이트합니다. `tcp://ip-address:actual-port-number`(`tcp://198.51.100.135:1234`와 같아야 함).

1.  **Unreal 편집기**에서 **재생**을 선택합니다.

## 문제 해결
<a name="working-with_unreal-client_troubleshooting"></a>
+  **NNG CMake 설치 단계가 실패하고 "관리 권한이 필요할 수 있음":** 

  ```
  CMake Error at build/_deps/nng-build/src/cmake_install.cmake:39 (file):
    file cannot create directory: C:/Program Files
    (x86)/ThirdPartyNngBuild/lib.  Maybe need administrative privileges.
  Call Stack (most recent call first):
    build/_deps/nng-build/cmake_install.cmake:37 (include)
    build/cmake_install.cmake:73 (include)
  ```
  +  **해결 방법:** UnrealClient/lib 디렉터리에 `nng.lib` 또는가 `nng.so` 있는 경우이 오류를 무시해도 됩니다. 그렇지 않은 경우 관리자 권한이 있는 터미널에서 cmake 빌드 명령을 실행해 보세요.
+  **“CMake를 사용하여 nng에서 제공하는 패키지 구성 파일 찾기”:** 

  ```
  CMake Error at CMakeLists.txt:23 (find_package):
  By not providing "Findnng.cmake" in CMAKE_MODULE_PATH this project has
   asked CMake to find a package configuration file provided by "nng", but
   CMake did not find one.
  ```
  +  **해결 방법:** CMake에서 `Findnng.cmake` 파일을 찾는 데 문제가 있습니다. CMake로 빌드할 때 인수를 추가합니다`-DTHIRD_PARTY_LIB_PATH sdk-folder/ThirdParty`. CMake 빌드를 다시 실행하기 전에 `Findnng.cmake` 파일이 `ThirdParty` 디렉터리에 여전히 있는지 확인합니다.

    ```
    cmake -S . -B build -DTHIRD_PARTY_LIB_PATH sdk-folder/ThirdParty
    cmake --build build --config RelWithDebInfo 
    cmake --install build
    ```

# 의 로컬 개발 SimSpace Weaver
<a name="working-with_local-development"></a>

신속한 테스트 및 디버깅을 위해 SimSpace Weaver 애플리케이션을 로컬에 배포할 수 있습니다.

**요구 사항**
+ [에 대한 설정 SimSpace Weaver](setting-up.md)의 단계를 수행하세요.

**Topics**
+ [1단계: 로컬 시뮬레이션 시작](working-with_local_launch.md)
+ [2단계: 로컬 시뮬레이션 보기](working-with_local-development_view.md)
+ [3단계: 로컬 시뮬레이션 중지(Windows에서는 선택 사항)](working-with_local-development_stop-sim.md)
+ [에서 로컬 개발 문제 해결 SimSpace Weaver](working-with_local-development_troubleshooting.md)

# 1단계: 로컬 시뮬레이션 시작
<a name="working-with_local_launch"></a>

1. 로 이동

   ```
   cd sdk-folder/Samples/sample-name/tools/local
   ```

1. 다음 명령을 실행하여 시뮬레이션을 로컬로 빌드하고 시작합니다.

   ```
   python quick-start.py
   ```

   이 스크립트는 다음을 수행합니다.

   1. 프로젝트를 빌드합니다.
      +  `quick-start.py`는 build.py 정의된 `build_project` 함수를 호출합니다. 이 단계는 프로젝트에 따라 달라집니다. PathfindingSample의 경우 CMake가 사용됩니다. build.py 찾을 수 있는 CMake 및 Docker 명령입니다.

   1. 로컬 시뮬레이션 시작
      + 스크립트는 스키마에 정의된 각 공간 파티션에 대해 하나의 로컬 프로세스를 시작합니다.
      + 스크립트는 스키마에 정의된 각 사용자 지정 앱에 대해 하나의 프로세스를 시작합니다.
      + 공간 앱이 먼저 시작된 다음 사용자 지정 앱이 시작됩니다. 각 앱은 스키마에 나타나는 순서대로 시작됩니다.

**중요**  
콘솔 SSH 세션과 같이 GUI를 지원하지 않는 환경에서 시작할 때는 `--noappwindow` 옵션을 사용하여 모든 출력을 현재 터미널로 리디렉션합니다.

**중요**  
Linux 사용자의 경우 스크립트는 시스템에 `xterm` 명령이 있다고 가정합니다. Linux 배포에 `xterm` 명령이 없는 경우 `--noappwindow` 옵션을 사용하여 모든 출력을 현재 터미널로 리디렉션합니다.
+  -h, --help 
  +  이러한 파라미터를 나열합니다.
+  --정리 
  +  빌드하기 전에 빌드 디렉터리의 내용을 삭제합니다.
+  --nobuild 
  +  프로젝트 재구축을 건너뜁니다.
+  --noappwindow 
  +  각 앱에 대해 새 창을 열지 마세요. 대신 stdout을 현재 터미널로 리디렉션합니다.
+  --logfile 
  +  로그 파일에 콘솔 출력을 씁니다.
+  --콘솔리언트 
  +  구성에 나열된 콘솔 클라이언트를 자동으로 연결합니다.
+  --스키마 SCHEMA
  + 이 호출에서 사용할 스키마입니다. config.py://https://https://https://https://https://https://https://https://https://https://https://https://https 

# 2단계: 로컬 시뮬레이션 보기
<a name="working-with_local-development_view"></a>

로컬 시뮬레이션을 보기 위해 SimSpaceWeaverAppSdkDistributable에 포함된 모든 클라이언트를 사용할 수 있습니다. 샘플 클라이언트를 빌드하고 사용하는 방법에 대한 자세한 내용은의 자습서를 참조하세요[시작하기 SimSpace Weaver](getting-started.md).

로컬 시뮬레이션을 위해 보기 앱에 연결하려면 클라이언트의 IP 주소와 포트 번호를 업데이트해야 합니다. 항상 다음 값을 SimSpace Weaver Local과 함께 사용합니다.

```
tcp://127.0.0.1:7000
```

선택한 클라이언트에 따라 다음과 같이 IP 주소 및 포트 번호를 업데이트할 수 있습니다.
+ **Unreal** - `view_app_url.txt`의 첫 번째 줄에서 URL을 변경합니다.
+ **콘솔** - IP 주소와 포트 번호(URL 를 파라미터로 사용하여 클라이언트를 시작합니다.

# 3단계: 로컬 시뮬레이션 중지(Windows에서는 선택 사항)
<a name="working-with_local-development_stop-sim"></a>

**참고**  
이 단계는 Linux에서는 필요하지만 Windows에서는 선택 사항입니다.

1.  다음으로 이동합니다.

   ```
   sdk-folder/Samples/sample-name/tools/local
   ```

1.  다음 명령을 실행하여 로컬 시뮬레이션을 중지하고 공유 메모리 리소스를 삭제합니다.

   ```
   python stop-and-delete.py
   ```

    이 스크립트는 다음을 수행합니다.
   +  로컬 프로세스를 중지합니다.
   +  공유 메모리 객체를 삭제합니다(Linux에서만 필요).

**stop-and-delete.py 파라미터**
+  -h, --help 
  +  이러한 파라미터를 나열합니다.
+  --중지 
  +  프로세스만 중지하려고 시도합니다.
+  - 삭제 
  +  공유 메모리 리소스만 삭제하려고 시도합니다.
+  --프로세스 
  +  중지할 프로세스의 이름입니다. 프로세스 이름이 스키마의 패키지 이름과 일치하지 않는 경우 이를 사용합니다.
+  --스키마 SCHEMA 
  +  이 호출에서 사용할 스키마입니다. 기본값은 config.pySCHEMA'입니다.

# 에서 로컬 개발 문제 해결 SimSpace Weaver
<a name="working-with_local-development_troubleshooting"></a>
+  **Linux: xterm 명령을 찾을 수 없음/ 열 수 없음** 
  + 로컬 스크립트는 Linux에서 실행할 때 xterm 명령이 존재한다고 가정합니다. xterm 명령이 없거나 GUI를 지원하지 않는 환경에서 실행 중인 경우 빠른 시작 스크립트를 실행할 때 `--noappwindow` 옵션을 사용합니다.
+  ** 열려 있는 앱 창이 없습니다\$1 ** 
  +  이는 로컬 시뮬레이션이 즉시 충돌할 때 발생합니다. 충돌 후 콘솔 출력을 보려면 빠른 시작 스크립트를 실행할 때 `--noappwindow` 또는 `--logfile` 옵션을 사용합니다.
+  ** 뷰 앱이 시작되거나 뷰 클라이언트가 연결된 후 시뮬레이션이 틱하지 않습니다\$1 ** 
  +  `—noappwindow` 옵션을 사용하여를 실행하면 일반적으로 이러한 종류의 문제가 해결됩니다. 그렇지 않으면 몇 번 다시 시작해도 성공합니다(비록 훨씬 낮은 속도임).

# AWS SimSpace Weaver 앱 SDK
<a name="working-with_app-sdk"></a>

 SimSpace Weaver 앱 SDK는 시뮬레이션에서 개체를 제어하고 SimSpace Weaver 이벤트에 응답하는 데 사용할 수 있는 APIs를 제공합니다. 여기에는 다음 네임스페이스가 포함됩니다.
+ **API** - API의 핵심 정의 및 해당 사용

다음 라이브러리와 연결합니다.
+ `libweaver_app_sdk_cxx_v1_full.so`

**중요**  
라이브러리는 AWS 클라우드에서 앱을 실행할 때 동적 연결에 사용할 수 있습니다. 앱과 함께 업로드할 필요가 없습니다.

**참고**  
 SimSpace Weaver 앱 SDK APIs는 시뮬레이션 내에서 데이터를 제어합니다. 이러한 APIs는 SimSpace Weaver 서비스 리소스(예: 시뮬레이션, 앱 및 클럭)를 제어하는 SimSpace Weaver 서비스 APIs와는 별개입니다 AWS. 자세한 내용은 [SimSpace Weaver API 참조](api-reference.md) 단원을 참조하십시오.

**Topics**
+ [API 메서드는 Result를 반환합니다.](working-with_app-sdk_return-result.md)
+ [최상위의 앱 SDK와 상호 작용](working-with_app-sdk_top-level.md)
+ [시뮬레이션 관리](working-with_app-sdk_sim.md)
+ [구독](working-with_app-sdk_sub.md)
+ [개체](working-with_app-sdk_ent.md)
+ [엔터티 이벤트](working-with_app-sdk_events.md)
+ [Result 및 오류 처리](working-with_app-sdk_result.md)
+ [일반 및 도메인 유형](working-with_app-sdk_generics.md)
+ [기타 앱 SDK 작업](working-with_app-sdk_misc.md)

# API 메서드는 Result를 반환합니다.
<a name="working-with_app-sdk_return-result"></a>

대부분의 SimSpace Weaver API 함수에는 반환 유형이 있습니다`Aws::WeaverRuntime::Result<T>`. 함수가 성공적으로 실행되면 `Result`에 `T`가 포함됩니다. 그렇지 않으면 `Result`에 Rust App SDK의 오류 코드를 나타내는 `Aws::WeaverRuntime::ErrorCode`가 포함됩니다.

**Example 예제**  

```
Result<Transaction> BeginUpdate(Application& app)
```

이 메서드는 다음을 수행합니다.
+ `BeginUpdate()` 실행이 성공하면 `Transaction`을 반환합니다.
+ `BeginUpdate()` 실행이 실패하면 `Aws::WeaverRuntime::ErrorCode`를 반환합니다.

# 최상위의 앱 SDK와 상호 작용
<a name="working-with_app-sdk_top-level"></a>

**수명 주기**
+  SimSpace Weaver 앱 SDK는 앱 수명 주기를 관리합니다. 앱의 수명 주기 상태를 읽거나 쓸 필요가 없습니다.

**파티션**
+ `Result <PartitionSet> AssignedPartitions(Transaction& txn);`를 사용하여 소유한 파티션을 가져옵니다.
+ `Result <PartitionSet> AllPartitions(Transaction& txn);`를 사용하여 시뮬레이션의 모든 파티션을 가져옵니다.

# 시뮬레이션 관리
<a name="working-with_app-sdk_sim"></a>

이 섹션에서는 일반적인 시뮬레이션 관리 작업에 대한 솔루션을 설명합니다.

**Topics**
+ [시뮬레이션 시작](working-with_app-sdk_sim_start.md)
+ [시뮬레이션 업데이트](working-with_app-sdk_sim_update.md)
+ [시뮬레이션 종료](working-with_app-sdk_sim_terminate.md)

# 시뮬레이션 시작
<a name="working-with_app-sdk_sim_start"></a>

`CreateApplication()`을 사용하여 앱을 생성합니다.

**Example 예제**  

```
Result<Application> applicationResult = Api::CreateApplication();

if (!applicationResult)
{
    ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(applicationResult);

    std::cout << "Failed to create application. Error code " <<
        static_cast<std::underlying_type_t<ErrorCode>>(errorCode) <<
        " Last error message "<< Api::LastErrorMessage() << ".";

    return 1;
}

/**
* Run simulation
*/
RunSimulation(std::move(applicationResult.assume_value()));
```

# 시뮬레이션 업데이트
<a name="working-with_app-sdk_sim_update"></a>

다음 `BeginUpdate` 함수를 사용하여 앱을 업데이트합니다.
+ `Result<Transaction> BeginUpdate(Application& app)`
+ `Result<bool> BeginUpdateWillBlock(Application& app)` - `BeginUpdate()`의 차단 여부를 알려줍니다.

`Result<void> Commit(Transaction& txn)`을 사용하여 변경 내용을 커밋합니다.

**Example 예제**  

```
Result<void> AppDriver::RunSimulation(Api::Application app) noexcept
{
    while (true)
    {
        {
            bool willBlock;

            do
            {
                WEAVERRUNTIME_TRY(willBlock, Api::BeginUpdateWillBlock(m_app));
            } while (willBlock);
        }

        WEAVERRUNTIME_TRY(Transaction transaction, Api::BeginUpdate(app));

        /**
         * Simulate app.
         */
        WEAVERRUNTIME_TRY(Simulate(transaction));
        WEAVERRUNTIME_TRY(Api::Commit(std::move(transaction)));
    }

    return Success();
}
```

# 시뮬레이션 종료
<a name="working-with_app-sdk_sim_terminate"></a>

`Result<void> DestroyApplication(Application&& app)`을 사용하여 앱과 시뮬레이션을 종료합니다.

다른 앱은 `BeginUpdateWillBlock()` 또는 `BeginUpdate()`에 대한 호출에서 `ErrorCode::ShuttingDown`이 수신되는 경우 시뮬레이션이 종료된다는 사실을 알게 됩니다. 앱이 `ErrorCode::ShuttingDown`을 수신하면 `Result<void> DestroyApplication(Application&& app)` 호출을 자체적으로 종료할 수 있습니다.

**Example 예제**  

```
Result<void> AppDriver::EncounteredAppError(Application&& application) noexcept
{
    const ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(runAppResult);

    switch (errorCode)
    {
    case ErrorCode::ShuttingDown:
        {
            // insert custom shutdown process here.

            WEAVERRUNTIME_TRY(Api::DestroyApplication(std::move(application)));
            return Success();
        }
    default:
        {
            OnAppError(errorCode);
            return errorCode;
        }
    }
}
```

**중요**  
`Api::Commit()` 다음에 `Result<void> DestroyApplication(Application&& app)`을 호출합니다. 업데이트 중에 애플리케이션을 삭제하면 정의되지 않은 동작이 발생할 수 있습니다.

**중요**  
프로그램이 종료되기 전에 `DestroyApplication()`을 호출하여 애플리케이션이 성공적으로 종료되었다고 보고하는지 확인해야 합니다.  
프로그램이 종료될 때 `DestroyApplication()`을 호출하지 않으면 `FATAL` 상태로 간주됩니다.

# 구독
<a name="working-with_app-sdk_sub"></a>

구독 영역과 도메인 ID를 사용하여 구독을 생성합니다. 도메인 ID는 해당 구독 영역을 소유한 도메인을 나타냅니다. `BoundingBox2F32`는 구독 영역을 설명합니다. 다음 함수를 사용하여 구독을 생성합니다.

```
Result<SubscriptionHandle> CreateSubscriptionBoundingBox2F32(Transaction& txn, DomainId id, const BoundingBox2F32& boundingBox)
```

**Example 예제**  

```
Result<void> CreateSubscriptionInSpatialDomain(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(Api::PartitionSet partitionSet, Api::AllPartitions(transaction)); 
    
    Api::DomainId spatialDomainId;

    for (const Api::Partition& partition : partitionSet.partitions)
    {
        if (partition.domain_type == Api::DomainType::Spatial)
        {
            /**
            * Get the spatial domain ID.
            */
            spatialDomainId = partition.domain_id;
            break;
        }
    }
    
    constexpr Api::BoundingBox2F32 subscriptionBounds { 
        /* min */ { /* x */ 0, /* y */ 0 }, 
        /* max */ { /* x */ 1000, /* y */ 1000 } }

    WEAVERRUNTIME_TRY(
        Api::SubscriptionHandle subscriptionHandle,
        Api::CreateSubscriptionBoundingBox2F32(
        transaction,
        spatialDomainId,
        subscriptionBounds));
        
    return Success();
}
```

`CreateSubscriptionBoundingBox2F32()`에서 반환된 `Api::SubscriptionHandle`을 사용하여 구독을 수정할 수 있습니다. 다음 함수에 인수로 전달합니다.

```
Result<void> ModifySubscriptionBoundingBox2F32(Transaction& txn, SubscriptionHandle handle, const BoundingBox2F32& boundingBox)
```

```
Result<void> DeleteSubscription(Transaction& txn, SubscriptionHandle handle)
```

# 개체
<a name="working-with_app-sdk_ent"></a>

`CreateEntity()`에서 반환된 `Result<Api::Entity>`의 `Api:Entity`을 사용하거나 엔터티가 앱의 구독 영역에 진입할 때 소유권 변경 이벤트에서 `Store` 및 `Load` API를 호출합니다(자세한 내용은 [엔터티 이벤트](working-with_app-sdk_events.md) 섹션 참조). 이러한 API와 함께 사용할 수 있도록 `Api::Entity` 객체를 추적하는 것이 좋습니다.

**Topics**
+ [엔터티 생성](working-with_app-sdk_ent_create.md)
+ [공간 도메인으로 엔터티 이전](working-with_app-sdk_ent_transfer.md)
+ [엔터티에 필드 데이터 쓰기 및 읽기](working-with_app-sdk_ent_readwrite.md)
+ [엔터티에 대한 위치 저장](working-with_app-sdk_ent_store-position.md)
+ [엔터티에 대한 위치 로드](working-with_app-sdk_ent_load-position.md)

# 엔터티 생성
<a name="working-with_app-sdk_ent_create"></a>

`CreateEntity()`를 사용하여 엔터티를 생성합니다. 이 함수에 전달하는 `Api::TypeId`의 의미를 정의합니다.

```
Namespace
{
    constexpr Api::TypeId k_entityTypeId { /* value */ 512 };
}

Result<void> CreateEntity(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(
        Api::Entity entity,
        Api::CreateEntity(
            transaction, Api::BuiltinTypeIdToTypeId(k_entityTypeId )));
}
```

**참고**  
`Api::BuiltinTypeId`에 0\$1511의 값이 예약되어 있습니다. 엔터티 TypeID(이 예제에서는 `k_entityTypeId`)의 값은 512 이상이어야 합니다.

# 공간 도메인으로 엔터티 이전
<a name="working-with_app-sdk_ent_transfer"></a>

사용자 지정 앱 또는 서비스 앱이 엔터티를 생성한 후 엔터티가 시뮬레이션에서 공간적으로 존재하도록 하려면 앱이 엔터티를 공간 도메인으로 전송해야 합니다. 공간 도메인의 엔터티는 다른 앱에서 읽을 수 있고 공간 앱에서 업데이트할 수 있습니다. `ModifyEntityDomain()` API를 사용하여 엔터티를 공간 도메인으로 전송할 수 있습니다.

```
AWS_WEAVERRUNTIME_API Result<void> ModifyEntityDomain(Transaction& txn, const Entity& entity, DomainId domainId) noexcept;
```

`DomainId`가 호출 앱에 할당된 `Partition`과 일치하지 않는 경우 `DomainId`는 `DomainType::Spatial` `Domain`에 대한 ID이어야 합니다. 새 `Domain`으로의 소유권 전송은 `Commit(Transaction&&)` 중에 이루어집니다.파라미터

`txn`  
현재의 `Transaction`입니다.

`entity`  
`Domain`를 변경할 대상 `Entity`입니다.

`domainId`  
`Entity`에 대한 대상 `Domain`의 `DomainId`입니다.

이 API는 엔터티 도메인이 성공적으로 변경되면 `Success`를 반환합니다.

# 엔터티에 필드 데이터 쓰기 및 읽기
<a name="working-with_app-sdk_ent_readwrite"></a>

모든 엔터티 데이터 필드는 blob 유형입니다. 엔터티에 최대 1,024바이트의 데이터를 쓸 수 있습니다. Blob 크기가 클수록 성능이 저하되므로 blob을 최대한 작게 유지하는 것이 좋습니다. Blob에 쓸 때 포인터 SimSpace Weaver 를 데이터 및 길이에 전달합니다. Blob에서 읽을 때 SimSpace Weaver 는 포인터 및 읽을 길이를 제공합니다. 앱이 `Commit()`을 호출하기 전에 모든 읽기가 완료되어야 합니다. 읽기 호출에서 반환된 포인터는 앱이 `Commit()`을 호출할 때 무효화됩니다.

**중요**  
`Commit()` 이후에 캐시된 blob 포인터에서의 읽기는 지원되지 않으므로 시뮬레이션이 실패할 수 있습니다.
읽기 호출에서 반환된 blob 포인터에서의 쓰기는 지원되지 않으므로 시뮬레이션이 실패할 수 있습니다.

**Topics**
+ [엔터티의 필드 데이터 저장](working-with_app-sdk_ent_readwrite_store.md)
+ [엔터티의 필드 데이터 불러오기](working-with_app-sdk_ent_readwrite_load.md)
+ [제거된 엔터티의 필드 데이터 로드](working-with_app-sdk_ent_readwrite_load-removed.md)

# 엔터티의 필드 데이터 저장
<a name="working-with_app-sdk_ent_readwrite_store"></a>

다음 예제는 앱이 소유한 엔터티의 필드 데이터를 저장(상태 패브릭에 쓰기)하는 방법을 보여줍니다. 해당 예제에는 다음 함수가 사용됩니다.

```
AWS_WEAVERRUNTIME_API Result<void> StoreEntityField(
    Transaction& txn,
    const Entity& entity,
    TypeId keyTypeId,
    FieldIndex index,
    std::int8_t* src,
    std::size_t length) noexcept;
```

`Api::TypeId keyTypeId` 파라미터는 전달된 데이터의 데이터 유형을 나타냅니다.

`Api::TypeId keyTypeId` 파라미터는 `Api::BuiltinTypeId`에서 해당 `Api::TypeId`를 수신해야 합니다. 적절한 변환이 없는 경우 `Api::BuiltinTypeId::Dynamic`을 사용할 수 있습니다.

복잡한 데이터 유형의 경우 `Api::BuiltInTypeId::Dynamic`을 사용합니다.

**참고**  
`FieldIndex index` 값은 0보다 커야 합니다. 0 값은 인덱스 키용으로 예약되어 있습니다(`StoreEntityIndexKey()` 참조).

**Example 프리미티브 데이터 유형을 사용한 예제**  

```
namespace
{
    constexpr Api::FieldIndex k_isTrueFieldId { /* value */ 1 };
}

Result<void> SetEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    bool value = true;
    
    auto* src = reinterpret_cast<std::int8_t*>(value);
    size_t length = sizeof(*value);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Bool),
        k_isTrueFieldId,
        src,
        length));
}
```

**Example struct를 사용한 데이터 보관 예제**  

```
namespace
{
    constexpr Api::FieldIndex k_dataFieldId { /* value */ 1 };
}

struct Data
{
    bool boolData;
    float floatData;
};

Result<void> SetEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    Data data = { /* boolData */ false, /* floatData */ -25.93 };
    
    auto* src = reinterpret_cast<std::int8_t*>(data);
    size_t length = sizeof(*data);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Dynamic),
        k_dataFieldId,
        src,
        length));
}
```

# 엔터티의 필드 데이터 불러오기
<a name="working-with_app-sdk_ent_readwrite_load"></a>

다음 예제는 엔터티의 필드 데이터를 로드(상태 패브릭에서 읽기)하는 방법을 보여줍니다. 해당 예제에는 다음 함수가 사용됩니다.

```
Result<std::size_t> LoadEntityField(
    Transaction& txn,
    const Entity& entity,
    TypeId keyTypeId,
    FieldIndex index,
    std::int8_t** dest) noexcept;
```

`Api::TypeId keyTypeId` 파라미터는 `Api::BuiltinTypeId`에서 해당 `Api::TypeId`를 수신해야 합니다. 적절한 변환이 없는 경우 `Api::BuiltinTypeId::Dynamic`을 사용할 수 있습니다.

**참고**  
`FieldIndex` 인덱스 값은 0보다 커야 합니다. 0 값은 인덱스 키용으로 예약되어 있습니다(`StoreEntityIndexKey()` 참조).

**Example 프리미티브 데이터 유형을 사용한 예제**  

```
namespace
{
    constexpr Api::FieldIndex k_isTrueFieldId { /* value */ 1 };
}

Result<void> LoadEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Api::LoadEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Bool),
        k_isTrueFieldId,
        &dest));
    
    bool isTrueValue = *reinterpret_cast<bool*>(dest);
}
```

**Example struct를 사용한 데이터 보관 예제**  

```
namespace
{
    constexpr Api::FieldIndex k_dataFieldId { /* value */ 1 };
}

struct Data
{
    bool boolData;
    float floatData;
};

Result<void> LoadEntityFields(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Api::LoadEntityField(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Dynamic),
        k_dataFieldId,
        &dest));
    
    Data dataValue = *reinterpret_cast<Data*>(dest);
}
```

# 제거된 엔터티의 필드 데이터 로드
<a name="working-with_app-sdk_ent_readwrite_load-removed"></a>

앱의 소유권 및 구독 영역에서 제거된 엔터티의 엔터티 필드 데이터는 로드(상태 패브릭에서 읽기)할 수 없습니다. 다음 예제에서는 `Api::ChangeListAction::Remove`의 결과로 엔터티에 대한 `Api::LoadIndexKey()` 호출로 인해 오류가 발생합니다. 두 번째 예제는 앱에서 직접 항목 데이터를 저장하고 로드하는 올바른 방법을 보여줍니다.

**Example 잘못된 코드의 예제**  

```
Result<void> ProcessSubscriptionChanges(Transaction& transaction)
{
    /* ... */
    
    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, 
        Api::AllSubscriptionEvents(transaction));
    
    for (const Api::SubscriptionEvent& event : 
        subscriptionChangeList.changes)
    {
        switch (event.action)
        {
        case Api::ChangeListAction::Remove:
            {
                std::int8_t* dest = nullptr;
    
                /**
                 * Error!
                 * This calls LoadEntityIndexKey on an entity that
                 * has been removed from the subscription area.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));
    
                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                break;
            }
        }
 
    }

    /* ... */
}
```

**Example 앱에서 항목 데이터를 저장하고 로드하는 올바른 방법의 예제**  

```
Result<void> ReadAndSaveSubscribedEntityPositions(Transaction& transaction)
{
    static std::unordered_map<Api::EntityId, AZ::Vector3> 
        positionsBySubscribedEntity;

    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, 
        Api::AllSubscriptionEvents(transaction));

    for (const Api::SubscriptionEvent& event : 
        subscriptionChangeList.changes)
    {
        switch (event.action)
        {
        case Api::ChangeListAction::Add:
            {
                std::int8_t* dest = nullptr;

                /**
                 * Add the position when the entity is added.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));

                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                positionsBySubscribedEntity.emplace(
                    event.entity.descriptor->id, position);

                break;
            }
        case Api::ChangeListAction::Update:
            {
                std::int8_t* dest = nullptr;

                /**
                 * Update the position when the entity is updated.
                 */
                WEAVERRUNTIME_TRY(Api::LoadEntityIndexKey(
                    transaction,
                    event.entity,
                    Api::BuiltinTypeIdToTypeId(
                        Api::BuiltinTypeId::Vector3F32),
                    &dest));

                AZ::Vector3 position = 
                    *reinterpret_cast<AZ::Vector3*>(dest);
                positionsBySubscribedEntity[event.entity.descriptor->id] = 
                    position;

                break;
            }
        case Api::ChangeListAction::Remove:
            {
                /**
                 * Load the position when the entity is removed.
                 */
                AZ::Vector3 position = positionsBySubscribedEntity[
                    event.entity.descriptor->id];

                /**
                 * Do something with position...
                 */
                break;
            }
        }
    }
    
    /* ... */
}
```

# 엔터티에 대한 위치 저장
<a name="working-with_app-sdk_ent_store-position"></a>

정수 데이터 구조를 사용하여 엔터티의 위치를 저장(상태 패브릭에 쓰기)할 수 있습니다. 해당 예제에는 다음 함수가 사용됩니다.

```
Result<void> StoreEntityIndexKey(
    Transaction& txn, 
    const Entity& entity, 
    TypeId keyTypeId, 
    std::int8_t* src, 
    std::size_t length)
```

**참고**  
다음 예제에서와 같이 `Api::BuiltinTypeId::Vector3F32`를 `Api::StoreEntityIndexKey()`에 제공해야 합니다.

**Example 배열을 사용하여 위치를 나타내는 예제**  

```
Result<void> SetEntityPositionByFloatArray(
    Api::Entity& entity, 
    Transaction& transaction)
{
    std::array<float, 3> position = { /* x */ 25, /* y */ 21, /* z */ 0 };
    
    auto* src = reinterpret_cast<std::int8_t*>(position.data());
    std::size_t length = sizeof(position);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(Api::BuiltinTypeId::Vector3F32),
        src,
        length));
}
```

**Example struct을 사용하여 위치를 나타내는 예제**  

```
struct Position 
{
   float x;
   float y;
   float z;
};

Result<void> SetEntityPositionByStruct(
    Api::Entity& entity, 
    Transaction& transaction)
{
    Position position = { /* x */ 25, /* y */ 21, /* z */ 0 };
    
    auto* src = reinterpret_cast<std::int8_t*>(&position);
    std::size_t length = sizeof(position);
    
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(Api::BuiltinTypeId::Vector3F32),
        src,
        length));
}
```

# 엔터티에 대한 위치 로드
<a name="working-with_app-sdk_ent_load-position"></a>

정수 데이터 구조를 사용하여 엔터티의 위치를 로드(상태 패브릭에서 읽기)할 수 있습니다. 해당 예제에는 다음 함수가 사용됩니다.

**참고**  
다음 예제에서와 같이 `Api::BuiltinTypeId::Vector3F32`를 `Api::LoadEntityIndexKey()`에 제공해야 합니다.

**Example 배열을 사용하여 위치를 나타내는 예제**  

```
Result<void> GetEntityPosition(Api::Entity& entity, 
    Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Aws::WeaverRuntime::Api::LoadEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Vector3F32),
        &dest));
        
    std::array<float, 3> position = 
        *reinterpret_cast<std::array<float, 3>*>(dest);
}
```

**Example struct을 사용하여 위치를 나타내는 예제**  

```
struct Position 
{struct
   float x;
   float y;
   float z;
};

Result<void> GetEntityPosition(Api::Entity& entity, Transaction& transaction)
{
    std::int8_t* dest = nullptr;
    
    WEAVERRUNTIME_TRY(Aws::WeaverRuntime::Api::LoadEntityIndexKey(
        transaction,
        entity,
        Api::BuiltinTypeIdToTypeId(
            Aws::WeaverRuntime::Api::BuiltinTypeId::Vector3F32),
        &dest));
        
    Position position = *reinterpret_cast<Position*>(dest);
}
```

# 엔터티 이벤트
<a name="working-with_app-sdk_events"></a>

 SimSpace Weaver 앱 SDK에서 다음 함수를 사용하여 모든 소유권 및 구독 이벤트를 가져올 수 있습니다.
+ `Result<OwnershipChangeList> OwnershipChanges(Transaction& txn) `
+ `Result<SubscriptionChangeList> AllSubscriptionEvents(Transaction& txn) `

콜백 기반 엔터티 이벤트 처리가 필요한 경우 SimSpace Weaver 데모 프레임워크를 사용할 수 있습니다. 자세한 내용은 다음 헤더 파일을 참조하세요.
+ `sdk-folder/packaging-tools/samples/ext/DemoFramework/include/DemoFramework/EntityEventProcessor.h`

고유한 엔터티 이벤트 처리를 만들 수도 있습니다.

**Topics**
+ [소유한 엔터티에 대한 이벤트 반복 실행](working-with_app-sdk_events_own.md)
+ [구독한 엔터티의 이벤트 반복 실행](working-with_app-sdk_events_sub.md)
+ [엔터티에 대한 소유권 변경 이벤트 반복 실행](working-with_app-sdk_events_change.md)

# 소유한 엔터티에 대한 이벤트 반복 실행
<a name="working-with_app-sdk_events_own"></a>

`OwnershipChanges()`를 사용하여 소유한 엔터티(앱 소유권 영역에 있는 엔터티)에 대한 이벤트 목록을 가져옵니다. 함수의 서명은 다음과 같습니다.

```
Result<OwnershipChangeList> OwnershipChanges(Transaction& txn)
```

그런 후 다음 예제와 같이 루프를 사용하여 엔터티를 반복합니다.

**Example 예제**  

```
WEAVERRUNTIME_TRY(Result<Api::OwnershipChangeList> ownershipChangesResult, Api::OwnershipChanges(transaction));

for (const Api::OwnershipChange& event : ownershipChangeList.changes)
{
    Api::Entity entity = event.entity;
    Api::ChangeListAction action = event.action;

    switch (action)
    {
    case Api::ChangeListAction::None:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Remove:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Add:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Update:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Reject:
        // insert code to handle the event
        break;
    }
}
```

**이벤트 유형**
+ `None` - 엔터티가 해당 영역에 있고 위치 및 필드 데이터가 수정되지 않았습니다.
+ `Remove` - 엔터티가 영역에서 제거되었습니다.
+ `Add` - 엔터티가 영역에 추가되었습니다.
+ `Update` - 엔터티가 해당 영역에 있으며 수정되었습니다.
+ `Reject` - 앱이 해당 영역에서 엔터티를 제거하지 못했습니다.

**참고**  
`Reject` 이벤트가 발생한 경우 앱은 다음 틱에서 전송을 다시 시도합니다.

# 구독한 엔터티의 이벤트 반복 실행
<a name="working-with_app-sdk_events_sub"></a>

`AllSubscriptionEvents()`를 사용하여 구독한 엔터티(앱 구독 영역에 있는 엔터티)에 대한 이벤트 목록을 가져옵니다. 함수의 서명은 다음과 같습니다.

```
Result<SubscriptionChangeList> AllSubscriptionEvents(Transaction& txn)
```

그런 후 다음 예제와 같이 루프를 사용하여 엔터티를 반복합니다.

**Example 예제**  

```
WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionChangeList, Api::AllSubscriptionEvents(transaction));

for (const Api::SubscriptionEvent& event : subscriptionChangeList.changes)
{
    Api::Entity entity = event.entity;
    Api::ChangeListAction action = event.action;

    switch (action)
    {
    case Api::ChangeListAction::None:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Remove:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Add:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Update:
        // insert code to handle the event
        break;
    case Api::ChangeListAction::Reject:
        // insert code to handle the event
        break;
    }
}
```

**이벤트 유형**
+ `None` - 엔터티가 해당 영역에 있고 위치 및 필드 데이터가 수정되지 않았습니다.
+ `Remove` - 엔터티가 영역에서 제거되었습니다.
+ `Add` - 엔터티가 영역에 추가되었습니다.
+ `Update` - 엔터티가 해당 영역에 있으며 수정되었습니다.
+ `Reject` - 앱이 해당 영역에서 엔터티를 제거하지 못했습니다.

**참고**  
`Reject` 이벤트가 발생한 경우 앱은 다음 틱에서 전송을 다시 시도합니다.

# 엔터티에 대한 소유권 변경 이벤트 반복 실행
<a name="working-with_app-sdk_events_change"></a>

엔터티가 소유권 영역과 구독 영역 사이를 이동하는 이벤트를 가져오려면 현재 및 이전 엔터티 소유권 및 구독 이벤트 간의 변경 사항을 비교합니다.

다음을 읽으면 이러한 이벤트를 처리할 수 있습니다.
+ `Api::SubscriptionChangeList`
+ `Api::OwnershipEvents`

그런 다음 변경 내용을 이전에 저장된 데이터와 비교할 수 있습니다.

다음 예제에서는 엔터티 소유권 변경 이벤트를 처리하는 방법을 보여줍니다. 이 예제에서는 구독 엔터티와 소유 엔터티 사이를 전환하는 엔터티의 경우 (어느 방향으로든) 소유권 제거/추가 이벤트가 먼저 발생하고 다음 틱에서 구독 제거/추가 이벤트가 발생한다고 가정합니다.

**Example 예제**  

```
Result<void> ProcessOwnershipEvents(Transaction& transaction)
{
    using EntityIdsByAction =
        std::unordered_map<Api::ChangeListAction, 
        std::vector<Api::EntityId>>;
    using EntityIdSetByAction =
        std::unordered_map<Api::ChangeListAction, 
        std::unordered_set<Api::EntityId>>;
   
    static EntityIdsByAction m_entityIdsByPreviousOwnershipAction;
    
    EntityIdSetByAction entityIdSetByAction;
   
    /**
     * Enumerate Api::SubscriptionChangeList items 
     * and store Add and Remove events.
     */ 
    WEAVERRUNTIME_TRY(Api::SubscriptionChangeList subscriptionEvents, 
        Api::AllSubscriptionEvents(transaction));
   
    for (const Api::SubscriptionEvent& event : subscriptionEvents.changes)
    {
        const Api::ChangeListAction action = event.action;
    
        switch (action)
        {
        case Api::ChangeListAction::Add:
        case Api::ChangeListAction::Remove:
    
            {
                entityIdSetByAction[action].insert(
                    event.entity.descriptor->id);
                break;
            }
        case Api::ChangeListAction::None:
        case Api::ChangeListAction::Update:
        case Api::ChangeListAction::Reject:
            {
                break;
            }
        }
    }
    
    EntityIdsByAction entityIdsByAction;
    
    /**
     * Enumerate Api::OwnershipChangeList items 
     * and store Add and Remove events.
     */
    
    WEAVERRUNTIME_TRY(Api::OwnershipChangeList ownershipChangeList, 
        Api::OwnershipChanges(transaction));
   
    for (const Api::OwnershipChange& event : ownershipChangeList.changes)
    {
        const Api::ChangeListAction action = event.action;
    
        switch (action)
        {
        case Api::ChangeListAction::Add:
        case Api::ChangeListAction::Remove:
            {
                entityIdsByAction[action].push_back(
                    event.entity.descriptor->id);
                break;
            }
        case Api::ChangeListAction::None:
        case Api::ChangeListAction::Update:
        case Api::ChangeListAction::Reject:
            {
                break;
            }
        }
    
    }
      
    std::vector<Api::EntityId> fromSubscribedToOwnedEntities;
    std::vector<Api::EntityId> fromOwnedToSubscribedEntities;
   
    /**
     * Enumerate the *previous* Api::OwnershipChangeList Remove items
     * and check if they are now in 
     * the *current* Api::SubscriptionChangeList Add items.
     *
     * If true, then that means 
     * OnEntityOwnershipChanged(bool isOwned = false)
     */ 
    for (const Api::EntityId& id : m_entityIdsByPreviousOwnershipAction[
        Api::ChangeListAction::Remove])
    {
        if (entityIdSetBySubscriptionAction[
            Api::ChangeListAction::Add].find(id) !=
                entityIdSetBySubscriptionAction[
                Api::ChangeListAction::Add].end())
        {
            fromOwnedToSubscribedEntities.push_back(id);
        }
    }
    
   
    /**
     * Enumerate the *previous* Api::OwnershipChangeList Add items
     * and check if they are now in 
     * the *current* Api::SubscriptionChangeList Remove items.
     *
     * If true, then that means 
     * OnEntityOwnershipChanged(bool isOwned = true)
     */ 
    for (const Api::EntityId& id : m_entityIdsByPreviousOwnershipAction[
        Api::ChangeListAction::Add])
    {
        if (entityIdSetBySubscriptionAction[
            Api::ChangeListAction::Remove].find(id) !=
            
                entityIdSetBySubscriptionAction[
                Api::ChangeListAction::Remove].end())
        {
            fromSubscribedToOwnedEntities.push_back(id);
        }
    }
    
    m_entityIdsByPreviousOwnershipAction = entityIdsByOwnershipAction;
    
    return Success();
}
```

# Result 및 오류 처리
<a name="working-with_app-sdk_result"></a>

`Aws::WeaverRuntime::Result<T>` 클래스는 타사 `Outcome` 라이브러리를 사용합니다. 다음 패턴을 사용하여 `Result`를 확인하고 API 직접 호출에서 반환된 오류를 캐치할 수 있습니다.

```
void DoBeginUpdate(Application& app)
{
    Result<Transaction> transactionResult = Api::BeginUpdate(app);
    
    if (transactionResult)
    {
        Transaction transaction = 
            std::move(transactionResult).assume_value();
        
        /**
         * Do things with transaction ...
         */
    }
    else
    {     
        ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(transactionResult);
        /**
         * Macro compiles to:
         * ErrorCode errorCode = transactionResult.assume_error();
         */
    }
}
```

## Result 제어문 매크로
<a name="working-with_app-sdk_result_macro"></a>

`Aws::WeaverRuntime::Result<T>` 반환 유형이 있는 함수 내에서 이전 코드 패턴 대신 `WEAVERRUNTIME_TRY` 매크로를 사용할 수 있습니다. 매크로는 전달된 함수를 실행합니다. 전달된 함수가 실패하면 매크로는 둘러싸는 함수가 오류를 반환하도록 합니다. 전달된 함수가 성공하면 실행은 다음 줄로 진행됩니다. 다음 예제에서는 이전 `DoBeginUpdate()` 함수의 재작성을 보여줍니다. 이 버전에서는 if-else 제어 구조 대신 `WEAVERRUNTIME_TRY` 매크로를 사용합니다. 함수의 반환 형식은 `Aws::WeaverRuntime::Result<void>`입니다.

```
Aws::WeaverRuntime::Result<void> DoBeginUpdate(Application& app)
{
    /**
     * Execute Api::BeginUpdate() 
     * and return from DoBeginUpdate() if BeginUpdate() fails.
     * The error is available as part of the Result.
     */
    WEAVERRUNTIME_TRY(Transaction transaction, Api::BeginUpdate(m_app));
    
    /**
     * Api::BeginUpdate executed successfully.
     *
     * Do things here.
     */
    
    return Aws::Success();
}
```

`BeginUpdate()`가 실패하면 매크로는 실패와 `DoBeginUpdate()`를 조기에 반환합니다. `WEAVERRUNTIME_EXPECT_ERROR` 매크로를 사용하여 `BeginUpdate()`에서 `Aws::WeaverRuntime::ErrorCode`를 가져올 수 있습니다. 다음 예제는 `Update()` 함수의 `DoBeginUpdate()` 호출 및 실패 시 오류 코드를 가져오는 방법을 보여줍니다.

```
void Update(Application& app)
{
    Result<void> doBeginUpdateResult = DoBeginUpdate(app);
    
    if (doBeginUpdateResult)
    {
        /**
         * Successful.
         */
    }
    else
    {    
        /**
         * Get the error from Api::BeginUpdate().
         */ 
        ErrorCode errorCode = WEAVERRUNTIME_EXPECT_ERROR(doBeginUpdateResult);

    }
}
```

`Update()`의 반환 유형을 `Aws::WeaverRuntime::Result<void>`로 변경하여 `Update()`를 호출하는 함수에서 `BeginUpdate()`의 오류 코드를 사용할 수 있도록 할 수 있습니다. 이 프로세스를 반복하여 호출 스택 아래쪽으로 오류 코드를 계속 보낼 수 있습니다.

# 일반 및 도메인 유형
<a name="working-with_app-sdk_generics"></a>

 SimSpace Weaver 앱 SDK는 단일 정밀도 데이터 형식 `Api::Vector2F32` 및 `Api::BoundingBox2F32`, 이중 정밀도 `Api::Vector2F64` 및를 제공합니다`Api::BoundingBox2F64`. 이러한 데이터 유형은 편리한 방법이 없는 수동적 데이터 구조입니다. 참고로 API는 `Api::Vector2F32` 및 `Api::BoundingBox2F32`만 사용합니다. 이러한 데이터 유형을 사용하여 구독을 만들고 수정할 수 있습니다.

 SimSpace Weaver 데모 프레임워크는 `Vector3` 및 `Aabb`를 포함하는 수AzCore학 라이브러리의 최소 버전을 제공합니다. 자세한 내용은 다음의 파일 헤더를 참조하세요.
+ `sdk-folder/packaging-tools/samples/ext/DemoFramework/include/AzCore/Math`

# 기타 앱 SDK 작업
<a name="working-with_app-sdk_misc"></a>

**Topics**
+ [AllSubscriptionEvents 및 OwnershipChanges에는 마지막 호출의 이벤트가 포함됨](working-with_app-sdk_misc_events-from-last-call.md)
+ [SubscriptionChangeList 처리 후 읽기 잠금 해제](working-with_app-sdk_misc_release-locks.md)
+ [테스트용 독립 실행형 앱 인스턴스 생성](working-with_app-sdk_misc_testing-app.md)

# AllSubscriptionEvents 및 OwnershipChanges에는 마지막 호출의 이벤트가 포함됨
<a name="working-with_app-sdk_misc_events-from-last-call"></a>

`Api::AllSubscriptionEvents()` 및 `Api::OwnershipChanges()`에 대한 호출의 반환 값에는 **마지막 틱이 아닌** 마지막 호출의 이벤트가 포함됩니다. 다음 예제에서는 첫 번째 호출 후 해당 함수가 즉시 호출되기 때문에 `secondSubscriptionEvents` 및 `secondOwnershipChangeList`가 비어 있습니다.

10개의 틱을 기다린 다음 `Api::AllSubscriptionEvents()` 및 `Api::OwnershipChanges()`를 호출하면 해당 결과에 마지막 틱이 아닌 최근 10개 틱의 이벤트와 변경 내용이 포함됩니다.

**Example 예제**  

```
Result<void> ProcessOwnershipChanges(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(
        Api::SubscriptionChangeList firstSubscriptionEvents,
        Api::AllSubscriptionEvents(transaction));
    WEAVERRUNTIME_TRY(
        Api::OwnershipChangeList firstOwnershipChangeList,
        Api::OwnershipChanges(transaction));
    
    WEAVERRUNTIME_TRY(
        Api::SubscriptionChangeList secondSubscriptionEvents,
        Api::AllSubscriptionEvents(transaction));
    WEAVERRUNTIME_TRY(
        Api::OwnershipChangeList secondOwnershipChangeList,
        Api::OwnershipChanges(transaction));
    
    /**
     * secondSubscriptionEvents and secondOwnershipChangeList are 
     * both empty because there are no changes since the last call.
     */
}
```

**참고**  
`AllSubscriptionEvents()` 함수는 구현되었지만 `SubscriptionEvents()` 함수는 **구현되지 않았습니다**.

# SubscriptionChangeList 처리 후 읽기 잠금 해제
<a name="working-with_app-sdk_misc_release-locks"></a>

업데이트를 시작하면 이전 틱의 다른 파티션에 커밋된 데이터에 대한 공유 메모리 세그먼트가 생깁니다. 독자는 이러한 공유 메모리 세그먼트를 잠글 수 있습니다. 모든 독자가 잠금을 해제하기 전까지는 앱을 완전히 커밋할 수 없습니다. 최적화를 위해 앱은 `Api::SubscriptionChangelist` 항목을 처리한 후 잠금을 해제하도록 `Api::ReleaseReadLeases()`를 호출해야 합니다. 그러면 커밋 시 경합이 줄어듭니다. `Api::Commit()`은 기본적으로 읽기 임대를 해제하지만 구독 업데이트를 처리한 후 읽기 임대를 수동으로 릴리스하는 것이 가장 좋습니다.

**Example 예제**  

```
Result<void> ProcessSubscriptionChanges(Transaction& transaction)
{
    WEAVERRUNTIME_TRY(ProcessSubscriptionChanges(transaction));
    
    /**
     * Done processing Api::SubscriptionChangeList items.
     * Release read locks. 
     */
        
    WEAVERRUNTIME_EXPECT(Api::ReleaseReadLeases(transaction));
    
    ...
}
```

# 테스트용 독립 실행형 앱 인스턴스 생성
<a name="working-with_app-sdk_misc_testing-app"></a>

`Api::CreateStandaloneApplication()`을 통해 실제 시뮬레이션에서 코드를 실행하기 전에 독립 실행형 앱을 생성하여 앱 로직을 테스트할 수 있습니다.

**Example 예제**  

```
int main(int argc, char* argv[])
{
    Api::StandaloneRuntimeConfig config = { 
        /* run_for_seconds (the lifetime of the app) */ 3,
        /* tick_hertz (the app clock rate) */ 10 };
    
    Result<Application> applicationResult =
        Api::CreateStandaloneApplication(config);

    ...
}
```

# AWS SimSpace Weaver 데모 프레임워크
<a name="working-with_demo-framework"></a>

 AWS SimSpace Weaver 데모 프레임워크(데모 프레임워크)는 SimSpace Weaver 앱을 개발하는 데 사용할 수 있는 유틸리티 라이브러리입니다.

**데모 프레임워크는 다음을 제공합니다.**
+ 사용 및 검토할 수 있는 코드 샘플 및 프로그래밍 패턴
+ 간단한 앱 개발을 간소화하는 추상화 및 유틸리티 함수
+  SimSpace Weaver 앱 SDK의 실험 기능을 테스트하는 더 간단한 방법

더 높은 성능을 제공하기 위해 SimSpace Weaver APIs에 대한 낮은 수준의 액세스를 갖춘 SimSpace Weaver 앱 SDK를 설계했습니다. 반대로 데모 프레임워크는 더 높은 수준의 추상화와 SimSpace Weaver 가 사용하기 쉬운 API에 대한 액세스를 제공하도록 설계했습니다. 사용 편의성 비용은 SimSpace Weaver 앱 SDK를 직접 사용할 때보다 성능이 낮습니다. 낮은 성능을 견딜 수 있는 시뮬레이션(예: 실시간 성능 요구 사항이 없는 시뮬레이션)은 데모 프레임워크를 사용하기에 좋은 후보일 수 있습니다. 데모 프레임워크는 완전한 툴킷이 아니므로 복잡한 애플리케이션에는 SimSpace Weaver 앱 SDK의 네이티브 기능을 사용하는 것이 좋습니다.

**데모 프레임워크에는 다음이 포함되어 있습니다.**
+ 다음을 지원하고 시연하는 작업 코드 샘플:
  + 앱 흐름 관리
  + 콜백 기반 엔터티 이벤트 처리
+ 다음의 타사 유틸리티 라이브러리 세트:
  + spdlog(로깅 라이브러리)
  + 다음만 포함하는 AZCore(수학 라이브러리)의 최소 버전
    + Vector3
    + Aabb
  + cxxopts(명령줄 옵션 구문 분석 라이브러리)
+ 관련 유틸리티 함수 SimSpace Weaver

데모 프레임워크는 라이브러리, 소스 파일, CMakeLists 등으로 구성되어 있습니다. 파일은 SimSpace Weaver 앱 SDK 배포 가능 패키지에 포함되어 있습니다.

# Service Quotas 작업
<a name="working-with_quotas"></a>

이 섹션에서는에 대한 서비스 할당량으로 작업하는 방법을 설명합니다 SimSpace Weaver. **할당량**은 **제한**이라고도 합니다. Service Quotas 목록은 [SimSpace Weaver 엔드포인트 및 할당량](service-quotas.md) 섹션을 참조하세요. 이 섹션의 API는 **앱 API** 세트에서 가져온 것입니다. 앱 API는 서비스 API와 다릅니다. 앱 APIs입니다. SimSpace Weaver 로컬 시스템의 앱 SDK 폴더에서 앱 API에 대한 설명서를 찾을 수 있습니다.

```
sdk-folder\SimSpaceWeaverAppSdk-sdk-version\documentation\index.html
```

**Topics**
+ [앱 제한 확인하기](#working-with_quotas_get-app-limits)
+ [앱에서 사용하는 리소스의 양 가져오기](#working-with_quotas_get-app-resources)
+ [지표 재설정](#working-with_quotas_reset-metrics)
+ [제한 초과됨](#working-with_quotas_exceed-limit)
+ [메모리 부족](#working-with_quotas_out-of-memory)
+ [모범 사례](#working-with_quotas_best-practices)

## 앱 제한 확인하기
<a name="working-with_quotas_get-app-limits"></a>

**RuntimeLimits**앱 API를 사용하여 앱의 제한을 쿼리할 수 있습니다.

```
Result<Limit> RuntimeLimit(Application& app, LimitType type)
```파라미터

**Application 및 앱**  
앱에 대한 참조입니다.

**LimitType 유형**  
다음과 같은 제한 유형을 가진 열거형입니다.  

```
enum LimitType {
    Unset = 0,
    EntitiesPerPartition = 1,
    RemoteEntityTransfers = 2,
    LocalEntityTransfers = 3
};
```

다음 예제에서는 엔터티 수 제한을 쿼리합니다.

```
WEAVERRUNTIME_TRY(auto entity_limit,
    Api::RuntimeLimit(m_app, Api::LimitType::EntitiesPerPartition))
Log::Info("Entity count limit", entity_limit.value);
```

## 앱에서 사용하는 리소스의 양 가져오기
<a name="working-with_quotas_get-app-resources"></a>

**RuntimeMetrics** 앱 API를 호출하여 앱에서 사용하는 리소스의 양을 가져올 수 있습니다.

```
Result<std::reference_wrapper<const AppRuntimeMetrics>> RuntimeMetrics(Application& app) noexcept
```파라미터

**Application 및 앱**  
앱에 대한 참조입니다.

API는 지표가 포함된 struct에 대한 참조를 반환합니다. 카운터 지표에는 실행 중인 총 값이 포함되며 증가만 합니다. 게이지 지표에는 증가 또는 감소할 수 있는 값이 있습니다. 애플리케이션 런타임은 이벤트로 인해 값이 증가할 때마다 카운터를 업데이트합니다. 런타임은 API를 호출할 때만 게이지를 업데이트합니다. SimSpace Weaver 는 참조가 앱 수명 주기 동안 유효하도록 보장합니다. API를 반복해서 호출해도 참조는 변경되지 않습니다.

```
struct AppRuntimeMetrics {
    uint64_t total_committed_ticks_gauge,

    uint32_t active_entity_gauge,
    uint32_t ticks_since_reset_counter,

    uint32_t load_field_counter,
    uint32_t store_field_counter,

    uint32_t created_entity_counter,
    uint32_t deleted_entity_counter,

    uint32_t entered_entity_counter,
    uint32_t exited_entity_counter,

    uint32_t rejected_incoming_transfer_counter,
    uint32_t rejected_outgoing_transfer_counter
}
```

## 지표 재설정
<a name="working-with_quotas_reset-metrics"></a>

**ResetRuntimeMetrics** 앱 API는 `AppRuntimeMetrics` struct의 값을 재설정합니다.

```
Result<void> ResetRuntimeMetrics(Application& app) noexcept
```

다음 예제에서는 앱에서 **ResetRuntimeMetrics**를 호출하는 방법을 보여줍니다.

```
if (ticks_since_last_report > 100)
{
    auto metrics = WEAVERRUNTIME_EXPECT(Api::RuntimeMetrics(m_app));
    Log::Info(metrics);

    ticks_since_last_report = 0;

    WEAVERRUNTIME_EXPECT(Api::ResetRuntimeMetrics(m_app));
}
```

## 제한 초과됨
<a name="working-with_quotas_exceed-limit"></a>

제한을 초과하는 앱 API 직접 호출은 `ErrorCode::CapacityExceeded`를 반환합니다. 단, 엔터티 전송은 예외입니다. SimSpace Weaver 는 **커밋** 및 **BeginUpdate** 앱 API 작업의 일부로 엔터티 전송을 비동기적으로 처리하므로 엔터티 전송 제한 때문에 전송이 실패할 경우 오류를 반환하는 특정 작업은 없습니다. 전송 실패를 감지하기 위해 `rejected_incoming_transfer_counter` 및 `rejected_outgoing_transfer_counter`(`AppRuntimeMetrics` struct)의 현재 값을 이전 값과 비교할 수 있습니다. 거부된 엔터티는 파티션에 포함되지 않지만 앱은 여전히 이를 시뮬레이션할 수 있습니다.

## 메모리 부족
<a name="working-with_quotas_out-of-memory"></a>

SimSpace Weaver 는 가비지 수집기 프로세스를 사용하여 여유 메모리를 정리하고 해제합니다. 가비지 수집기가 메모리를 해제할 수 있는 속도보다 빠르게 데이터를 쓸 수 있습니다. 이 경우 쓰기 작업이 앱의 예약 메모리 제한을 초과할 수 있습니다. SimSpace Weaver 는 `OutOfMemory`(및 추가 세부 정보)가 포함된 메시지와 함께 내부 오류를 반환합니다. 자세한 내용은 [여러 시간에 걸쳐 쓰기 작업 분산](#working-with_quotas_best-practices_spread-writes) 단원을 참조하십시오.

## 모범 사례
<a name="working-with_quotas_best-practices"></a>

다음의 모범 사례는 제한 초과되지 않도록 앱을 설계하기 위한 일반적인 지침입니다. 이는 특정 앱 디자인에는 적용되지 않을 수 있습니다.

### 자주 모니터링하고 속도 줄이기
<a name="working-with_quotas_best-practices_monitor"></a>

지표를 자주 모니터링하여 제한에 가깝게 도달하는 작업의 속도를 늦춰야 합니다.

### 구독 제한 및 전송 제한 초과 방지
<a name="working-with_quotas_best-practices_subscription-and-xfer"></a>

가능한 경우 시뮬레이션을 설계하여 원격 구독 및 엔터티 전송 횟수를 줄입니다. 배치 그룹을 사용하여 동일한 작업자에 여러 파티션을 배치하고 작업자 간에 엔터티를 원격으로 전송해야 하는 필요성을 줄일 수 있습니다.

### 여러 시간에 걸쳐 쓰기 작업 분산
<a name="working-with_quotas_best-practices_spread-writes"></a>

틱의 업데이트 수와 크기는 트랜잭션을 커밋하는 데 필요한 시간과 메모리에 상당한 영향을 미칠 수 있습니다. 메모리 요구 사항이 크면 애플리케이션 런타임에 메모리가 부족해질 수 있습니다. 쓰기를 여러 시간에 걸쳐 분산하여 틱당 평균 총 업데이트 크기를 줄일 수 있습니다. 이를 통해 성능을 개선하고 제한 초과를 방지할 수 있습니다. 각 틱에 평균 12MB 또는 각 엔터티에 1.5KB를 초과하여 쓰지 않는 것이 좋습니다.

# 디버깅 시뮬레이션
<a name="working-with_debugging"></a>

다음과 같은 방법을 사용하여 시뮬레이션에 대한 정보를 확인할 수 있습니다.

**주제**
+ [SimSpace Weaver Local 사용 및 콘솔 출력 살펴보기](#working-with_debugging_use-local)
+ [Amazon CloudWatch Logs의 로그 살펴보기](#working-with_debugging_logs)
+ [**describe** API 직접 호출 사용](#working-with_debugging_api)
+ [클라이언트 연결](#working-with_debugging_client)

## SimSpace Weaver Local 사용 및 콘솔 출력 살펴보기
<a name="working-with_debugging_use-local"></a>

먼저 로컬에서 시뮬레이션을 개발한 다음 AWS 클라우드에서 실행하는 것이 좋습니다. SimSpace Weaver Local를 실행하면 콘솔 출력을 직접 볼 수 있습니다. 자세한 내용은 [의 로컬 개발 SimSpace Weaver](working-with_local-development.md) 단원을 참조하십시오.

## Amazon CloudWatch Logs의 로그 살펴보기
<a name="working-with_debugging_logs"></a>

시뮬레이션 AWS 클라우드 을 실행하면 앱의 콘솔 출력이 Amazon CloudWatch Logs의 로그 스트림으로 전송됩니다. 시뮬레이션은 다른 로그 데이터도 씁니다. 시뮬레이션에서 로그 데이터를 쓰려면 시뮬레이션 스키마에서 로깅을 활성화해야 합니다. 자세한 내용은 [SimSpace Weaver Amazon CloudWatch Logs의 로그](cloudwatch-logs.md) 단원을 참조하십시오.

**주의**  
시뮬레이션을 통해 대량의 로그 데이터가 생성될 수 있습니다. 로그 데이터는 매우 빠르게 증가할 수 있습니다. 로그를 면밀히 관찰하고 더 이상 실행할 필요가 없을 때는 시뮬레이션을 중단해야 합니다. 로깅으로 인해 비용이 많이 들 수 있습니다.

## **describe** API 직접 호출 사용
<a name="working-with_debugging_api"></a>

다음과 서비스 API를 사용하여 AWS 클라우드에서 시뮬레이션에 대한 정보를 확인할 수 있습니다.
+ **ListSimulations** -의 모든 시뮬레이션 목록을 가져옵니다 AWS 클라우드.  
**Example 예제**  

  ```
  aws simspaceweaver list-simulations
  ```
+ **DescribeSimulation** - 시뮬레이션에 대한 세부 정보를 확인합니다.  
**Example 예제**  

  ```
  aws simspaceweaver describe-simulation --simulation MySimulation
  ```
+ **DescribeApp** - 앱에 대한 세부 정보를 확인합니다.  
**Example 예제**  

  ```
  aws simspaceweaver describe-app --simulation MySimulation --domain MyCustomDomain --app MyCustomApp
  ```

 SimSpace Weaver APIs[SimSpace Weaver API 참조](api-reference.md).

## 클라이언트 연결
<a name="working-with_debugging_client"></a>

시뮬레이션 스키마에서 `endpoint_config`를 사용하여 정의한 실행 중인 사용자 지정 또는 서비스 앱에 클라이언트를 연결할 수 있습니다. SimSpace Weaver 앱 SDK에는 샘플 애플리케이션을 보는 데 사용할 수 있는 샘플 클라이언트가 포함되어 있습니다. 이러한 샘플 클라이언트의 소스 코드와 샘플 애플리케이션을 살펴보고 자체 클라이언트를 만드는 방법을 확인할 수 있습니다. 샘플 클라이언트를 빌드하고 실행하는 방법에 대한 자세한 내용은의 자습서를 참조하세요[시작하기 SimSpace Weaver](getting-started.md).

다음 폴더에서 샘플 클라이언트의 소스 코드를 찾을 수 있습니다.
+ `sdk-folder\packaging-tools\clients\PathfindingSampleClients\`

# 디버깅 로컬 시뮬레이션
<a name="working-with_debugging_local"></a>

Microsoft Visual Studio를 사용하여 SimSpace Weaver Local 앱을 디버깅할 수 있습니다. Visual Studio를 사용하여 디버깅하는 방법에 대한 자세한 내용은 [https://learn.microsoft.com/en-us/visualstudio/debugger/debugger-feature-tour](https://learn.microsoft.com/en-us/visualstudio/debugger/debugger-feature-tour) 섹션을 참조하세요.

**로컬 시뮬레이션 디버깅**

1. `schema.yaml`이 작업 디렉터리에 있는지 확인합니다.

1. **Visual Studio**에서 디버깅하려는 각 앱(예: `PathfindingSampleLocalSpatial` 또는`PathfindingSampleLocalView`)의 컨텍스트 메뉴를 열고 디버깅 섹션에서 작업 디렉터리를 설정합니다.

1. 디버깅하려는 앱의 컨텍스트 메뉴를 열고 **시작 프로젝트로 설정**을 선택합니다.

1. F5를 선택하여 앱 디버깅을 시작합니다.

시뮬레이션을 디버깅하기 위한 요구 사항은 시뮬레이션을 정상적으로 실행하기 위한 요구 사항과 동일합니다. 스키마에 지정된 수의 공간 앱을 시작해야 합니다. 예를 들어 스키마가 2x2 그리드를 지정하고 디버그 모드에서 공간 앱을 시작하는 경우 디버그 모드인지 여부에 관계없이 공간 앱을 3개 더 시작할 때까지 시뮬레이션이 실행되지 않습니다.

사용자 지정 앱을 디버깅하려면 먼저 공간 앱을 시작한 다음 디버거에서 사용자 지정 앱을 시작해야 합니다.

참고로 시뮬레이션은 잠금 단계로 실행됩니다. 앱이 중단점에 도달하면 다른 모든 앱이 일시 중지됩니다. 해당 중단점에서 계속 진행하면 다른 앱도 계속 실행됩니다.

# 사용자 지정 컨테이너
<a name="working-with_custom-containers"></a>

AWS SimSpace Weaver 앱은 컨테이너화된 Amazon Linux 2 (AL2) 환경에서 실행됩니다. 에서 AWS 클라우드는 Amazon Elastic Container Registry(Amazon ECR)에서 제공하는 `amazonlinux:2` 이미지로 빌드된 Docker 컨테이너에서 시뮬레이션을 SimSpace Weaver 실행합니다. 사용자 지정 도커 이미지를 생성하여 Amazon ECR에 저장한 다음, 당사에서 제공하는 기본 도커 이미지 대신 이 이미지를 시뮬레이션에 사용할 수 있습니다.

사용자 지정 컨테이너를 사용하여 소프트웨어 종속성을 관리하고 표준 도커 이미지에 없는 추가 소프트웨어 구성 요소를 포함할 수 있습니다. 예를 들어 앱에서 사용하는 공개적으로 사용 가능한 소프트웨어 라이브러리를 컨테이너에 추가하고 앱 zip 파일에는 사용자 지정 코드만 넣을 수 있습니다.

**중요**  
Amazon ECR 퍼블릭 갤러리 또는 프라이빗 Amazon ECR 레지스트리의 Amazon ECR 리포지토리에 호스팅된 AL2 도커 이미지만 지원합니다. Amazon ECR 외부에서 호스팅되는 도커 이미지는 지원되지 않습니다. Amazon ECR에 대한 자세한 내용은 *[Amazon Elastic Container Registry 설명서](https://docs.aws.amazon.com/ecr)*를 참조하세요.

**Topics**
+ [사용자 지정 컨테이너 생성](working-with_custom-containers_create.md)
+ [사용자 지정 컨테이너를 사용하도록 프로젝트 수정](working-with_custom-containers_modify-project.md)
+ [사용자 지정 컨테이너에 대한 FAQ](working-with_custom-containers_faq.md)
+ [사용자 지정 컨테이너 문제 해결](working-with_custom-containers_troubleshooting.md)

# 사용자 지정 컨테이너 생성
<a name="working-with_custom-containers_create"></a>

이 지침에서는 Docker 및 Amazon Elastic Container Registry(Amazon ECR)를 사용하는 방법을 알고 있다고 가정합니다. Amazon ECR에 대한 자세한 내용은 *[Amazon ECR 사용 설명서](https://docs.aws.amazon.com/AmazonECR/latest/userguide)*를 참조하세요.

**사전 조건**
+ 이러한 작업을 수행하는 데 사용하는 IAM 자격 증명(사용 또는 역할)에는 Amazon ECR을 사용할 수 있는 올바른 권한이 있습니다.
+ 도커는 로컬 시스템에 설치됨

**사용자 지정 컨테이너 생성**

1. `Dockerfile`을 생성합니다.

    AWS SimSpace Weaver 앱을 실행`Dockerfile`하기 위한는 Amazon ECR의 Amazon Linux 2 이미지로 시작합니다.

   ```
   # parent image required to run AWS SimSpace Weaver apps
   FROM public.ecr.aws/amazonlinux/amazonlinux:2
   ```

1. `Dockerfile`을 구축합니다.

1. 컨테이너 이미지를 Amazon ECR로 업로드합니다.
   + [AWS Management Console을 사용합니다.](https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-console.html)
   + [AWS Command Line Interface를 사용합니다.](https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-cli.html)
**참고**  
Amazon ECR에 컨테이너 이미지를 업로드하려고 할 때 `AccessDeniedException` 오류가 발생하는 경우, IAM 자격 증명(사용자 또는 역할)에 Amazon ECR을 사용하는 데 필요한 권한이 없는 것일 수 있습니다. `AmazonEC2ContainerRegistryPowerUser` AWS 관리형 정책을 IAM 자격 증명에 연결하고 다시 시도할 수 있습니다. 정책 연결 방법에 대한 자세한 내용은 *AWS Identity and Access Management 사용 설명서*의 [IAM 자격 증명 권한 추가 및 제거](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html)를 참조하세요.

# 사용자 지정 컨테이너를 사용하도록 프로젝트 수정
<a name="working-with_custom-containers_modify-project"></a>

이 지침에서는를 사용하는 방법을 이미 알고 있으며 앱 스토리지 AWS SimSpace Weaver 및 개발 워크플로를 AWS 클라우드 보다 효율적으로 만들고 싶다고 가정합니다.

**사전 조건**
+ Amazon Elastic Container Registry(Amazon ECR)에 사용자 지정 컨테이너가 있습니다. 사용자 지정 컨테이너 생성에 대한 자세한 내용은 [사용자 지정 컨테이너 생성](working-with_custom-containers_create.md) 섹션을 참조하세요.

**사용자 지정 컨테이너를 사용하도록 프로젝트 수정**

1. 프로젝트의 시뮬레이션 앱 역할에 Amazon ECR을 사용할 수 있는 권한을 추가합니다.

   1. 다음 권한이 있는 IAM 정책이 아직 없다면 정책을 생성합니다. 정책 이름 `simspaceweaver-ecr`을 사용하는 것이 좋습니다. IAM 정책 생성 방법에 대한 자세한 내용은 *AWS Identity and Access Management 사용 설명서*의 [IAM 정책 생성](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html)을 참조하세요.

      ```
      {
          "Version": "2012-10-17",		 	 	 
          "Statement": [
              {
                  "Sid": "Statement",
                  "Effect": "Allow",
                  "Action": [
                      "ecr:BatchGetImage",
                      "ecr:GetDownloadUrlForLayer",
                      "ecr:GetAuthorizationToken"
                  ],
                  "Resource": "*"
              }
          ]
      }
      ```

   1. 프로젝트의 시뮬레이션 앱 역할 이름을 찾으려면 다음을 수행합니다.

      1. 텍스트 편집기에서 CloudFormation 템플릿을 엽니다.

         ```
         sdk-folder\PackagingTools\sample-stack-template.yaml
         ```

      1. `WeaverAppRole`에서 `RoleName` 속성을 찾습니다. 해당 값은 프로젝트의 시뮬레이션 앱 역할 이름입니다.  
**Example**  

         ```
         AWSTemplateFormatVersion: "2010-09-09"
         Resources:
           WeaverAppRole:
             Type: 'AWS::IAM::Role'
             Properties:
               RoleName: 'weaver-MySimulation-app-role'
               AssumeRolePolicyDocument:
                 Version: "2012-10-17"		 	 	 
                 Statement:
                 - Effect: Allow
                   Principal:
                     Service:
                       - 'simspaceweaver.amazonaws.com'
         ```

   1. `simspaceweaver-ecr` 정책을 프로젝트의 시뮬레이션 앱 역할에 연결합니다. 정책 연결 방법에 대한 자세한 내용은 *AWS Identity and Access Management 사용 설명서*의 [IAM 자격 증명 권한 추가 및 제거](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html)를 참조하세요.

   1. 다음 명령으로 이동하여 `sdk-folder` 실행하여 샘플 SimSpace Weaver 스택을 업데이트합니다.

      ```
      python setup.py --cloudformation
      ```

1. 프로젝트의 시뮬레이션 스키마에 컨테이너 이미지를 지정합니다.
   + `default_image`에서 선택적 `simulation_properties` 속성을 추가하여 모든 도메인의 기본 사용자 지정 컨테이너 이미지를 지정할 수 있습니다.
   + 사용자 지정 컨테이너 이미지로 사용하려는 도메인에 대해 `image` 속성을 `app_config`에 추가합니다. Amazon ECR 리포지토리 URI를 값으로 지정합니다. 도메인마다 다른 이미지를 지정할 수 있습니다.
     + 도메인에 `image`이 지정되지 않고 `default_image`가 지정된 경우 해당 도메인의 앱이 기본 이미지를 사용합니다.
     + `image`가 도메인에 지정되지 않고가 지정되지 않은 경우 해당 도메인의 앱`default_image`은 표준 SimSpace Weaver 컨테이너에서 실행됩니다.  
**Example 사용자 지정 컨테이너 설정이 포함된 스키마 스니펫**  

   ```
   sdk_version: "1.17.0"
   simulation_properties:
     log_destination_service: "logs"
     log_destination_resource_name: "MySimulationLogs"
     default_entity_index_key_type: "Vector3<f32>"
     default_image: "111122223333.dkr.ecr.us-west-2.amazonaws.com/my-ecr-repository:latest" # image to use if no image specified for a domain
   domains:
     MyCustomDomain:
       launch_apps_via_start_app_call: {}
       app_config:
         package: "s3://weaver-myproject-111122223333-us-west-2/MyViewApp.zip" 
         launch_command: ["MyViewApp"]  
         required_resource_units:
           compute: 1
         endpoint_config:
           ingress_ports:
             - 7000
         image: "111122223333.dkr.ecr.us-west-2.amazonaws.com/my-ecr-repository:latest" # custom container image to use for this domain 
     MySpatialDomain:
       launch_apps_by_partitioning_strategy:
         partitioning_strategy: "MyGridPartitioning"
         grid_partition:
           x: 2
           y: 2
       app_config:
         package: "s3://weaver-myproject-111122223333-us-west-2/MySpatialApp.zip" 
         launch_command: ["MySpatialApp"] 
         required_resource_units:
           compute: 1
         image: "111122223333.dkr.ecr.us-west-2.amazonaws.com/my-ecr-repository:latest" # custom container image to use for this domain
   ```

1. 평소와 같이 프로젝트를 빌드하고 업로드합니다.

# 사용자 지정 컨테이너에 대한 FAQ
<a name="working-with_custom-containers_faq"></a>

## Q1. 컨테이너 내용을 변경하려면 어떻게 해야 하나요?
<a name="working-with_custom-containers_faq_q1"></a>
+ **실행 중인 시뮬레이션의 경우** - 실행 중인 시뮬레이션의 컨테이너는 변경할 수 없습니다. 새 컨테이너를 빌드하고 해당 컨테이너를 사용하는 새 시뮬레이션을 시작해야 합니다.
+ **새 시뮬레이션의 경우** - 새 컨테이너를 구축하고 Amazon Elastic Container Registry(Amazon ECR)에 업로드한 다음 해당 컨테이너를 사용하는 새 시뮬레이션을 시작합니다.

## Q2. 시뮬레이션에 사용할 컨테이너 이미지를 변경하려면 어떻게 해야 하나요?
<a name="working-with_custom-containers_faq_q2"></a>
+ **실행 중인 시뮬레이션의 경우** - 실행 중인 시뮬레이션의 컨테이너는 변경할 수 없습니다. 새 컨테이너를 사용하는 새 시뮬레이션을 시작해야 합니다.
+ **새 시뮬레이션의 경우** - 프로젝트의 시뮬레이션 스키마에 새 컨테이너 이미지를 지정합니다. 자세한 내용은 [사용자 지정 컨테이너를 사용하도록 프로젝트 수정](working-with_custom-containers_modify-project.md) 단원을 참조하십시오.

# 사용자 지정 컨테이너 문제 해결
<a name="working-with_custom-containers_troubleshooting"></a>

**Topics**
+ [Amazon Elastic Container Registry(Amazon ECR)에 이미지를 업로드하는 경우의 AccessDeniedException](working-with_custom-containers_troubleshooting_access-denied.md)
+ [사용자 지정 컨테이너를 사용하는 시뮬레이션이 시작되지 않음](working-with_custom-containers_troubleshooting_no-start.md)

# Amazon Elastic Container Registry(Amazon ECR)에 이미지를 업로드하는 경우의 AccessDeniedException
<a name="working-with_custom-containers_troubleshooting_access-denied"></a>

Amazon ECR에 컨테이너 이미지를 업로드하려고 할 때 `AccessDeniedException` 오류가 발생하는 경우, IAM 자격 증명(사용자 또는 역할)에 Amazon ECR을 사용하는 데 필요한 권한이 없는 것일 수 있습니다. `AmazonEC2ContainerRegistryPowerUser` AWS 관리형 정책을 IAM 자격 증명에 연결하고 다시 시도할 수 있습니다. 정책 연결 방법에 대한 자세한 내용은 *AWS Identity and Access Management 사용 설명서*의 [IAM 자격 증명 권한 추가 및 제거](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html)를 참조하세요.

# 사용자 지정 컨테이너를 사용하는 시뮬레이션이 시작되지 않음
<a name="working-with_custom-containers_troubleshooting_no-start"></a>

**문제 해결 팁**
+ 시뮬레이션에 로깅이 활성화된 경우 오류 로그를 확인하세요.
+ 사용자 지정 컨테이너 없이 시뮬레이션을 테스트합니다.
+ 시뮬레이션을 로컬에서 테스트합니다. 자세한 내용은 [의 로컬 개발 SimSpace Weaver](working-with_local-development.md) 단원을 참조하십시오.

# Python 작업
<a name="working-with_python"></a>

Python을 SimSpace Weaver 앱과 클라이언트에 사용할 수 있습니다. Python 소프트웨어 개발 키트(Python SDK)는 표준 SimSpace Weaver 앱 SDK 배포 가능 패키지의 일부로 포함되어 있습니다. Python을 사용한 개발은 지원되는 다른 언어에서의 개발과 비슷한 방식으로 작동합니다.

**중요**  
SimSpace Weaver 는 Python 버전 3.9만 지원합니다.

**중요**  
SimSpace Weaver Python에 대한 지원에는 SimSpace Weaver 버전 1.15.0 이상이 필요합니다.

**Topics**
+ [Python 프로젝트 생성](working-with_python_create-project.md)
+ [Python 시뮬레이션 시작하기](working-with_python_start-sim.md)
+ [샘플 Python 클라이언트](working-with_python_client.md)
+ [Python 사용에 대한 FAQ](working-with_python_faq.md)
+ [Python과 관련된 문제 해결](working-with_python_troubleshooting.md)

# Python 프로젝트 생성
<a name="working-with_python_create-project"></a>

## Python 사용자 지정 컨테이너
<a name="working-with_python_create-project_container"></a>

에서 Python 기반 SimSpace Weaver 시뮬레이션을 실행하려면 필요한 종속성이 포함된 사용자 지정 컨테이너를 생성할 AWS 클라우드수 있습니다. 자세한 내용은 [사용자 지정 컨테이너](working-with_custom-containers.md) 단원을 참조하십시오.

Python 사용자 지정 컨테이너에는 다음이 포함되어야 합니다.
+ gcc
+ openssl-devel
+ bzip2-devel
+ libfi-devel
+ wget
+ tar
+ gzip
+ make
+ Python(버전 3.9)

`PythonBubblesSample` 템플릿을 사용하여 프로젝트를 생성하는 경우 프로젝트의 `tools` 폴더에 있는 `quick-start.py` 스크립트를 실행하여 필요한 종속성이 있는 도커 이미지를 만들 수 있습니다. 이 스크립트는 Amazon Elastic Container Registry(Amazon ECR)에 이미지를 업로드합니다.

`quick-start.py` 스크립트는 `Dockerfile`을 사용합니다.

```
FROM public.ecr.aws/amazonlinux/amazonlinux:2
RUN yum -y install gcc openssl-devel bzip2-devel libffi-devel 
RUN yum -y install wget
RUN yum -y install tar
RUN yum -y install gzip
RUN yum -y install make
WORKDIR /opt
RUN wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz 
RUN tar xzf Python-3.9.0.tgz
WORKDIR /opt/Python-3.9.0
RUN ./configure --enable-optimizations
RUN make altinstall
COPY requirements.txt ./
RUN python3.9 -m pip install --upgrade pip
RUN pip3.9 install -r requirements.txt
```

`Dockerfile`에 자체 종속성을 추가할 수 있습니다.

```
RUN yum -y install dependency-name
```

`requirements.txt` 파일에는 `PythonBubblesSample` 샘플 시뮬레이션에 필요한 Python 패키지 목록이 들어 있습니다.

```
Flask==2.1.1
```

`requirements.txt`에 자체 Python 패키지 종속성을 추가할 수 있습니다.

```
package-name==version-number
```

`Dockerfile` 및 `requirements.txt`는 프로젝트의 `tools` 폴더에 있습니다.

**중요**  
엄밀히 따지자면 Python 시뮬레이션에서 사용자 지정 컨테이너를 사용할 필요는 없지만 사용자 지정 컨테이너를 사용하는 것이 좋습니다. 우리가 제공하는 표준 Amazon Linux 2(AL2) 컨테이너에는 Python이 없습니다. 따라서 Python이 있는 사용자 지정 컨테이너를 사용하지 않는 경우 업로드하는 각 앱 zip 파일에 Python과 필수 종속성을 포함해야 합니다 SimSpace Weaver.

# Python 시뮬레이션 시작하기
<a name="working-with_python_start-sim"></a>

의 SimSpace Weaver Local 및에서 일반 시뮬레이션과 동일한 방식으로 Python 기반 SimSpace Weaver 시뮬레이션을 시작할 수 SimSpace Weaver 있습니다 AWS 클라우드. 자세한 내용은의 자습서를 참조하세요[시작하기 SimSpace Weaver](getting-started.md).

`PythonBubblesSample`에는 자체 Python 샘플 클라이언트가 포함되어 있습니다. 자세한 내용은 [샘플 Python 클라이언트](working-with_python_client.md) 단원을 참조하십시오.

# 샘플 Python 클라이언트
<a name="working-with_python_client"></a>

`PythonBubblesSample` 템플릿을 사용하여 프로젝트를 만드는 경우 프로젝트에 Python 샘플 클라이언트가 포함됩니다. 샘플 클라이언트를 사용하여 `PythonBubblesSample` 시뮬레이션을 볼 수 있습니다. 샘플 클라이언트를 시작점으로 사용하여 자체 Python 클라이언트를 만들 수도 있습니다.

다음 절차에서는 `PythonBubblesSample` 프로젝트를 생성하고 시뮬레이션을 시작했다고 가정합니다.

**Python 클라이언트 시작**

1. **명령 프롬프트** 창에서 `PyBubbleClient` 샘플 프로젝트 폴더로 이동합니다.

   ```
   cd sdk-folder\Clients\HTTP\PyBubbleClient
   ```

1. Python 클라이언트를 실행합니다.

   ```
   python tkinter_client.py --host ip-address --port port-number
   ```

**파라미터**  
**host**  
시뮬레이션의 IP 주소입니다. 에서 시작된 시뮬레이션의 경우 [SimSpace Weaver 콘솔](https://console.aws.amazon.com/simspaceweaver)에서 시뮬레이션의 IP 주소를 찾거나 빠른 시작 자습서[사용자 지정 앱의 IP 주소 및 포트 번호 가져오기IP 주소 및 포트 번호 가져오기](working-with_get-ip.md)의의 절차를 사용할 AWS 클라우드수 있습니다. 로컬 시뮬레이션의 경우 `127.0.0.1`을 IP 주소로 사용합니다.  
**port**  
시뮬레이션의 포트 번호입니다. 에서 시작된 시뮬레이션의 경우 AWS 클라우드포트 `Actual` 번호입니다. [SimSpace Weaver 콘솔](https://console.aws.amazon.com/simspaceweaver)에서 시뮬레이션의 포트 번호를 찾거나 빠른 시작 자습서의 [사용자 지정 앱의 IP 주소 및 포트 번호 가져오기IP 주소 및 포트 번호 가져오기](working-with_get-ip.md) 절차를 사용할 수 있습니다. 로컬 시뮬레이션의 경우 `7000`을 포트 번호로 사용합니다.  
**simsize**  
클라이언트에 표시할 최대 엔터티 수입니다.

# Python 사용에 대한 FAQ
<a name="working-with_python_faq"></a>

## Q1. 어떤 버전의 Python이 지원되나요?
<a name="working-with_python_faq_q1"></a>

SimSpace Weaver 는 Python 버전 3.9만 지원합니다.

# Python과 관련된 문제 해결
<a name="working-with_python_troubleshooting"></a>

**Topics**
+ [사용자 지정 컨테이너 생성 중 실패](working-with_python_troubleshooting_create-container-failure.md)
+ [Python 시뮬레이션이 시작되지 않음](working-with_python_troubleshooting_no-start.md)
+ [Python 시뮬레이션 또는 보기 클라이언트에서 ModuleNotFound 오류 발생](working-with_python_troubleshooting_module-not-found.md)

# 사용자 지정 컨테이너 생성 중 실패
<a name="working-with_python_troubleshooting_create-container-failure"></a>

`quick-start.py` 실행 후 `no basic auth credentials` 오류가 발생하면 Amazon ECR의 임시 보안 인증에 문제가 있을 수 있습니다. AWS 리전 ID 및 AWS 계정 번호로 다음 명령을 실행합니다.

```
aws ecr get-login-password --region region | docker login --username AWS --password-stdin account_id.dkr.ecr.region.amazonaws.com
```

**Example**  

```
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.region.amazonaws.com
```

**중요**  
 AWS 리전 지정한가 시뮬레이션에 사용하는 것과 동일한지 확인합니다. 에서 AWS 리전 SimSpace Weaver 지원하는 중 하나를 사용합니다. 자세한 내용은 [SimSpace Weaver 엔드포인트 및 할당량](service-quotas.md) 단원을 참조하십시오.

`aws ecr` 명령을 실행한 후 `quick-start.py`를 다시 실행합니다.

**확인할 기타 문제 해결 리소스**
+ [사용자 지정 컨테이너 문제 해결](working-with_custom-containers_troubleshooting.md)
+ *Amazon ECR 사용 설명서*의 [Amazon ECR 문제 해결](https://docs.aws.amazon.com/AmazonECR/latest/userguide/troubleshooting.html)
+ 자세한 내용은 *Amazon ECR 사용 설명서*의 [Amazon ECR을 사용하여 설정](https://docs.aws.amazon.com/AmazonECR/latest/userguide/get-set-up-for-amazon-ecr.html)을 참조하세요.

# Python 시뮬레이션이 시작되지 않음
<a name="working-with_python_troubleshooting_no-start"></a>

시뮬레이션의 관리 로그에 `Unable to start app` 오류가 표시될 수 있습니다. 이는 사용자 지정 컨테이너 생성이 실패한 경우 발생할 수 있습니다. 자세한 내용은 [사용자 지정 컨테이너 생성 중 실패](working-with_python_troubleshooting_create-container-failure.md) 단원을 참조하십시오. 로그에 대한 자세한 내용은 [SimSpace Weaver Amazon CloudWatch Logs의 로그](cloudwatch-logs.md) 섹션을 참조하세요.

컨테이너에 문제가 없는 것이 확실하다면 앱의 Python 소스 코드를 확인합니다. SimSpace Weaver Local을 사용하여 앱을 테스트할 수 있습니다. 자세한 내용은 [의 로컬 개발 SimSpace Weaver](working-with_local-development.md) 단원을 참조하십시오.

# Python 시뮬레이션 또는 보기 클라이언트에서 ModuleNotFound 오류 발생
<a name="working-with_python_troubleshooting_module-not-found"></a>

필요한 Python 패키지를 찾을 수 없는 경우 Python에서 `ModuleNotFound` 오류가 발생합니다.

시뮬레이션이에 있는 경우 사용자 지정 컨테이너에에 나열된 모든 필수 종속성이 있는지 AWS 클라우드확인합니다`requirements.txt`. `requirements.txt`를 편집하는 경우 `quick-start.py`를 다시 실행해야 한다는 점을 잊지 마세요.

`PythonBubblesSample` 클라이언트에서 오류가 발생하는 경우 `pip`를 사용하여 표시된 패키지를 설치합니다.

```
pip install package-name==version-number
```

# 기타 엔진 지원
<a name="working-with_engines"></a>

자체 사용자 지정 C\$1\$1 엔진을와 함께 사용할 수 있습니다 SimSpace Weaver. 현재 다음 엔진에 대한 지원을 개발 중입니다. 각 엔진에 대한 별도의 설명서가 있습니다.

**중요**  
여기에 나열된 엔진과의 통합은 실험용입니다. 미리 보기를 사용할 수 있습니다.

**엔진**
+ [Unity](#working-with_engines_unity) (최소 버전 2022.3.19.F1)
+ [Unreal Engine](#working-with_engines_unreal)(최소 버전 5.0)

## Unity
<a name="working-with_engines_unity"></a>

Unity로 SimSpace Weaver 시뮬레이션을 빌드하기 전에 Unity 개발 환경이 이미 설치되어 있어야 합니다. 자세한 내용은 별도의 지침을 참조하세요.

```
sdk-folder\Unity-Guide.pdf
```

## Unreal Engine
<a name="working-with_engines_unreal"></a>

소스 코드에서 Unreal Engine 전용 서버를 빌드해야 합니다. SimSpaceWeaverAppSdkDistributable에는 Unreal Engine에 대한 PathfindingSample이 포함되어 있습니다. 자세한 내용은 별도의 지침을 참조하세요.

```
sdk-folder\Unreal-Engine-Guide.pdf
```

# 에서 라이선스 소프트웨어 사용 AWS SimSpace Weaver
<a name="working-with_byol"></a>

AWS SimSpace Weaver 를 사용하면 선택한 시뮬레이션 엔진 및 콘텐츠로 시뮬레이션을 빌드할 수 있습니다. 사용과 관련하여 시뮬레이션에 사용하는 소프트웨어 또는 콘텐츠의 라이선스 조건을 획득, 유지 및 준수할 SimSpace Weaver책임은 사용자에게 있습니다. 라이선싱 계약에서 소프트웨어와 콘텐츠를 가상 호스팅 환경에서 배포하도록 허용하는지 확인합니다.

# 를 사용하여 리소스 관리 AWS CloudFormation
<a name="working-with_cloudformation"></a>

 AWS CloudFormation 를 사용하여 AWS SimSpace Weaver 리소스를 관리할 수 있습니다. CloudFormation 는 AWS 인프라를 코드로 지정, 프로비저닝 및 관리하는 데 도움이 되는 별도의 AWS 서비스입니다. 를 CloudFormation 사용하여 *[템플릿](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-concepts.html#cfn-concepts-templates template)*이라는 JSON 또는 YAML 파일을 생성합니다. 템플릿은 인프라의 세부 정보를 지정합니다. CloudFormation 은 템플릿을 사용하여 인프라를 *[스택](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-concepts.html#w2ab1b5c15b9)*이라는 단일 단위로 프로비저닝합니다. 스택을 삭제하면 스택의 모든 항목을 동시에 CloudFormation 삭제할 수 있습니다. 표준 소스 코드 관리 프로세스(예: [Git](https://git-scm.com/)와 같은 버전 관리 시스템에서 추적)를 사용하여 템플릿을 관리할 수 있습니다. 에 대한 자세한 내용은 [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide) CloudFormation참조하세요.

**시뮬레이션 리소스**  
에서 AWS*리소스*는 작업할 수 있는 엔터티입니다. 예를 들어 Amazon EC2 인스턴스, Amazon S3 버킷 또는 IAM 역할이 있습니다. SimSpace Weaver 시뮬레이션은 리소스입니다. 구성에서는 일반적으로 형식으로 AWS 리소스를 지정합니다`AWS::service::resource`. 의 SimSpace Weaver경우 시뮬레이션 리소스를 로 지정합니다`AWS::SimSpaceWeaver::Simulation`. 의 시뮬레이션 리소스에 대한 자세한 내용은 *AWS CloudFormation 사용 설명서*의 [SimSpace Weaver](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-simspaceweaver-simulation.html) 섹션을 CloudFormation참조하세요.

**와 CloudFormation 함께를 사용하려면 어떻게 해야 하나요 SimSpace Weaver?**  
프로비저닝하려는 AWS 리소스를 지정하는 CloudFormation 템플릿을 생성할 수 있습니다. 템플릿은 전체 아키텍처, 아키텍처의 일부 또는 소규모 솔루션을 지정할 수 있습니다. 예를 들어 Amazon S3 버킷, IAM 권한, Amazon Relational Database Service 또는 Amazon DynamoDB의 지원 데이터베이스, `Simulation` 리소스를 포함하는 SimSpace Weaver 솔루션의 아키텍처를 지정할 수 있습니다. 그런 다음 CloudFormation 를 사용하여 이러한 모든 리소스를 하나의 단위로 프로비저닝하면서 동시에 프로비저닝할 수 있습니다.

**Example IAM 리소스를 생성하고 시뮬레이션을 시작하는 템플릿**  
다음 예제 템플릿은 SimSpace Weaver 가 계정에서 작업을 수행하는 데 필요한 IAM 역할 및 권한을 생성합니다. SimSpace Weaver 앱 SDK 스크립트는 프로젝트를 생성할 AWS 리전 때 특정에서 역할과 권한을 생성하지만, CloudFormation 템플릿을 사용하여 스크립트를 다시 실행하지 않고도 시뮬레이션을 다른 AWS 리전 에 배포할 수 있습니다. 예를 들어 재해 복구를 위한 백업 시뮬레이션을 설정하기 위해 이 작업을 수행할 수 있습니다.  
이 예제에서 원래 시뮬레이션 이름은 `MySimulation`입니다. 스키마용 버킷은 CloudFormation 가 스택을 빌드 AWS 리전 하는에 이미 있습니다. 버킷에는 해당 AWS 리전에서 시뮬레이션을 실행하도록 적절하게 구성된 스키마 버전이 포함되어 있습니다. 스키마는 시뮬레이션과 동일한 AWS 리전 의 Amazon S3 버킷인 앱 zip 파일의 위치를 지정한다는 점을 기억하세요. 앱은 스택을 CloudFormation 빌드할 AWS 리전 때 버킷과 파일이 이미에 있어야 합니다. 그렇지 않으면 시뮬레이션이 시작되지 않습니다. 이 예제의 버킷 이름에는이 포함되지만 AWS 리전,는 버킷이 실제로 어디에 있는지를 결정하지 않습니다. 버킷이 실제로 여기에 있는지 확인해야 합니다 AWS 리전 (Amazon S3 콘솔, Amazon S3 APIs 또는의 Amazon S3 Amazon S3 명령을 사용하여 버킷 속성을 확인할 수 있음 AWS CLI).  
이 예제에서는의 일부 내장 함수와 파라미터를 사용하여 변수 대체 CloudFormation 를 수행합니다. 자세한 내용은 *AWS CloudFormation 사용 설명서*의 [내장 함수 참조](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html) 및 [유사 파라미터 참조](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html)를 참조하세요.  

```
AWSTemplateFormatVersion: 2010-09-09
Resources:
  WeaverAppRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: SimSpaceWeaverAppRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service:
              - simspaceweaver.amazonaws.com
          Action:
            - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName: SimSpaceWeaverAppRolePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
            - Effect: Allow
              Action:
                - logs:PutLogEvents
                - logs:DescribeLogGroups
                - logs:DescribeLogStreams
                - logs:CreateLogGroup
                - logs:CreateLogStream
              Resource: *
            - Effect: Allow
              Action:
                - cloudwatch:PutMetricData
              Resource: *
            - Effect: Allow
              Action:
                - s3:ListBucket
                - s3:PutObject
                - s3:GetObject
              Resource: *
  MyBackupSimulation:
    Type: AWS::SimSpaceWeaver::Simulation
    Properties:
      Name: !Sub 'mySimulation-${AWS::Region}'
      RoleArn: !GetAtt WeaverAppRole.Arn
      SchemaS3Location:
        BucketName: !Sub 'weaver-mySimulation-${AWS::AccountId}-schemas-${AWS::Region}'
        ObjectKey: !Sub 'schema/mySimulation-${AWS::Region}-schema.yaml'
```

# 에서 스냅샷 사용 AWS CloudFormation
<a name="working-with_cloudformation_snapshots"></a>

[스냅샷](working-with_snapshots.md)은 시뮬레이션의 백업입니다. 다음 예제에서는 스키마 대신 스냅샷에서 새 시뮬레이션을 시작합니다. 이 예제의 스냅샷은 SimSpace Weaver 앱 SDK 프로젝트 시뮬레이션에서 생성되었습니다.는 새 시뮬레이션 리소스를 CloudFormation 생성하고 스냅샷의 데이터로 초기화합니다. 새 시뮬레이션은 원래 시뮬레이션과 `MaximumDuration`이 다를 수 있습니다.

원래 시뮬레이션의 앱 역할 사본을 만들어 사용하는 것이 좋습니다. 시뮬레이션의 CloudFormation 스택을 삭제하면 원래 시뮬레이션의 앱 역할이 삭제될 수 있습니다.

```
Description: "Example - Start a simulation from a snapshot"
Resources:
  MyTestSimulation:
    Type: "AWS::SimSpaceWeaver::Simulation"
    Properties:
      MaximumDuration: "2D"
      Name: "MyTestSimulation_from_snapshot"
      RoleArn: "arn:aws:iam::111122223333:role/weaver-MyTestSimulation-app-role-copy"   
      SnapshotS3Location:
        BucketName: "weaver-mytestsimulation-111122223333-artifacts-us-west-2"
        ObjectKey: "snapshot/MyTestSimulation_22-12-15_12_00_00-230428-1207-13.zip"
```

# 스냅샷
<a name="working-with_snapshots"></a>

*스냅샷*을 생성하여 언제든지 시뮬레이션 엔터티 데이터를 백업할 수 있습니다. SimSpace Weaver 는 Amazon S3 버킷에서.zip 파일을 만듭니다. 스냅샷으로 새 시뮬레이션을 생성할 수 있습니다.는 스냅샷에 저장된 개체 데이터로 새 시뮬레이션의 상태 패브릭을 SimSpace Weaver 초기화하고, 스냅샷이 생성될 때 실행 중인 공간 및 서비스 앱을 시작하고, 클럭을 적절한 틱으로 설정합니다.는 스키마 파일 대신 스냅샷에서 시뮬레이션 구성을 SimSpace Weaver 가져옵니다. 앱 .zip 파일은 원래 시뮬레이션과 동일한 Amazon S3 위치에 있어야 합니다. 모든 사용자 지정 앱을 개별적으로 시작해야 합니다.

**주제**
+ [스냅샷의 사용 사례](#working-with_snapshots_use-cases)
+ [SimSpace Weaver 콘솔을 사용하여 스냅샷 작업](working-with_snapshots_console.md)
+ [AWS CLI 를 사용하여 스냅샷 작업](working-with_snapshots_cli.md)
+ [에서 스냅샷 사용 AWS CloudFormation](working-with_cloudformation_snapshots.md)
+ [스냅샷에 대한 FAQ](working-with_snapshots_faq.md)

## 스냅샷의 사용 사례
<a name="working-with_snapshots_use-cases"></a>

### 이전 상태로 돌아가 분기 시나리오를 살펴봅니다.
<a name="working-with_snapshots_use-case_branching"></a>

시뮬레이션의 스냅샷을 생성하여 특정 상태로 저장할 수 있습니다. 그런 다음 해당 스냅샷에서 새 시뮬레이션을 여러 개 만들고 해당 상태에서 분기할 수 있는 다양한 시나리오를 탐색할 수 있습니다.

### 재해 복구 및 보안 모범 사례
<a name="working-with_snapshots_use-cases_best-practice"></a>

시뮬레이션을 정기적으로 백업하는 것이 좋습니다. 특히 1시간 이상 실행하거나 여러 작업자를 사용하는 시뮬레이션의 경우에는 더욱 그렇습니다. 백업은 재해 및 보안 사고로부터 복구하는 데 도움이 될 수 있습니다. 스냅샷은 시뮬레이션을 백업할 수 있는 방법을 제공합니다. 스냅샷을 사용하려면 앱 .zip 파일이 Amazon S3의 이전 위치와 동일한 위치에 있어야 합니다. 앱 .zip 파일을 다른 위치로 이동할 수 있어야 하는 경우 사용자 지정 백업 솔루션을 사용해야 합니다.

모범 사례에 대한 자세한 내용은 [작업 시 모범 사례 SimSpace Weaver](best-practices.md) 및 [에 대한 보안 모범 사례 SimSpace Weaver](security_best-practices.md) 섹션을 참조하세요.

### 시뮬레이션 지속 시간 연장
<a name="working-with_snapshots_use-cases_extend-duration"></a>

*시뮬레이션 리소스*는 SimSpace Weaver에서 시뮬레이션을 표현한 것입니다. 모든 시뮬레이션 리소스에는 `MaximumDuration` 설정이 있습니다. 시뮬레이션 리소스가 해당 `MaximumDuration`에 도달하면 자동으로 중지됩니다. `MaximumDuration`의 최대값은 `14D`(14일)입니다.

시뮬레이션을 해당 시뮬레이션 리소스의 `MaximumDuration`보다 오래 지속해야 하는 경우 시뮬레이션 리소스가 해당 `MaximumDuration`에 도달하기 전에 스냅샷을 만들 수 있습니다. 스냅샷으로 새 시뮬레이션을 시작(새 시뮬레이션 리소스 생성)할 수 있습니다. SimSpace Weaver 는 스냅샷에서 엔터티 데이터를 초기화하고, 이전에 실행했던 것과 동일한 공간 및 서비스 앱을 시작하고, 클럭을 복원합니다. 사용자 지정 앱을 시작하고 추가 사용자 지정 초기화를 수행할 수 있습니다. 새 시뮬레이션 리소스를 시작하는 경우 새 시뮬레이션 리소스의 `MaximumDuration`을 다른 값으로 설정할 수 있습니다.

# SimSpace Weaver 콘솔을 사용하여 스냅샷 작업
<a name="working-with_snapshots_console"></a>

 SimSpace Weaver 콘솔을 사용하여 시뮬레이션의 스냅샷을 생성할 수 있습니다.

**Topics**
+ [스냅샷 생성](#working-with_snapshots_console_create)
+ [스냅샷에서 시뮬레이션 시작](#working-with_snapshots_console_start)

## 콘솔을 사용하여 스냅샷 생성
<a name="working-with_snapshots_console_create"></a>

**스냅샷 생성**

1. 에 로그인 AWS Management Console 하고 [SimSpace Weaver 콘솔](https://console.aws.amazon.com/simspaceweaver)에 연결합니다.

1. 탐색 창에서 **시뮬레이션**을 선택합니다.

1. 시뮬레이션 이름 옆의 라디오 버튼을 선택합니다. 시뮬레이션 **상태**가 **시작됨**이어야 합니다.

1. 페이지 상단에서 **스냅샷 생성**을 선택합니다.

1. **스냅샷 설정**의 **스냅샷 대상**에 스냅샷을 생성하려는 버킷 또는 버킷 및 폴더의 Amazon S3 URI SimSpace Weaver 를 입력합니다. 사용 가능한 버킷을 찾아보고 위치를 선택하려는 경우 **S3 찾아보기**를 선택할 수 있습니다.
**중요**  
Amazon S3 버킷은 시뮬레이션과 동일한 AWS 리전 에 있어야 합니다.
**참고**  
SimSpace Weaver 는 선택한 스냅샷 대상 내에 `snapshot` 폴더를 생성합니다.는 해당 `snapshot` 폴더에 스냅샷 .zip 파일을 SimSpace Weaver 생성합니다.

1. **스냅샷 생성(Create snapshot)**을 선택합니다.

## 콘솔을 사용하여 스냅샷에서 시뮬레이션 시작
<a name="working-with_snapshots_console_start"></a>

스냅샷에서 시뮬레이션을 시작하려면 시뮬레이션에서 액세스할 수 있는 Amazon S3 버킷에 스냅샷 .zip 파일이 있어야 합니다. 시뮬레이션에서는 시뮬레이션을 시작할 때 선택한 앱 역할에 정의된 권한을 사용합니다. 원본 시뮬레이션의 모든 앱 .zip 파일은 스냅샷을 만들 때와 같은 위치에 있어야 합니다.

**스냅샷에서 시뮬레이션 시작**

1. 에 로그인 AWS Management Console 하고 [SimSpace Weaver 콘솔](https://console.aws.amazon.com/simspaceweaver)에 연결합니다.

1. 탐색 창에서 **시뮬레이션**을 선택합니다.

1. 페이지 상단에서 **시뮬레이션 시작**을 선택합니다.

1. **시뮬레이션 설정**에서 시뮬레이션의 이름 및 선택적 설명을 입력합니다. 시뮬레이션 이름은 AWS 계정에서 고유해야 합니다.

1. **시뮬레이션 시작 방법**에서 **Amazon S3의 스냅샷 사용**을 선택합니다.

1. **스냅샷용 Amazon S3 URI**의 경우 스냅샷 파일의 Amazon S3 URI를 입력하거나 **S3 찾아보기**를 선택하여 파일을 찾아 선택합니다.
**중요**  
Amazon S3 버킷은 시뮬레이션과 동일한 AWS 리전 에 있어야 합니다.

1. **IAM 역할**의 경우 시뮬레이션에서 사용할 앱 역할을 선택합니다.

1. **최대 지속 시간**에는 시뮬레이션 리소스를 실행해야 하는 최대 시간을 입력합니다. 최대 값은 `14D`입니다. 시뮬레이션의 최대 지속 시간에 대한 자세한 내용은 [https://docs.aws.amazon.com/simspaceweaver/latest/APIReference/API_StartSimulation.html](https://docs.aws.amazon.com/simspaceweaver/latest/APIReference/API_StartSimulation.html) 섹션을 참조하세요.

1. **태그 - *선택 사항***에서 태그를 추가하려면 **새 태그 추가**를 선택합니다.

1. **시뮬레이션 시작**을 선택합니다.

# AWS CLI 를 사용하여 스냅샷 작업
<a name="working-with_snapshots_cli"></a>

를 사용하여 명령 프롬프트에서 SimSpace Weaver APIs를 호출 AWS CLI 할 수 있습니다. 를 올바르게 AWS CLI 설치하고 구성해야 합니다. 자세한 내용은 버전 2 사용 설명서[의 최신 버전의 AWS CLI 설치 또는 업데이트를](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 참조하세요. *AWS Command Line Interface * 

**Topics**
+ [스냅샷 생성](#working-with_snapshots_cli_create)
+ [스냅샷에서 시뮬레이션 시작](#working-with_snapshots_cli_start)

## AWS CLI 를 사용하여 스냅샷 생성
<a name="working-with_snapshots_cli_create"></a>

**스냅샷 생성**
+ **명령 프롬프트**에서 `CreateSnapshot` API를 호출합니다.

  ```
  aws simspaceweaver create-snapshot --simulation simulation-name —destination s3-destination
  ```

  **파라미터**  
시뮬레이션  
시작된 시뮬레이션의 이름입니다. `aws simspaceweaver list-simulations`를 사용하여 시뮬레이션의 이름과 상태를 볼 수 있습니다.  
destination  
대상 Amazon S3 버킷과 스냅샷 파일의 선택적 객체 키 접두사를 지정하는 문자열입니다. 객체 키 접두사는 일반적으로 버킷의 폴더입니다.는이 대상의 `snapshot` 폴더 내에 스냅샷을 SimSpace Weaver 생성합니다.  
Amazon S3 버킷은 시뮬레이션과 동일한 AWS 리전 에 있어야 합니다.

  **예**

  ```
  aws simspaceweaver create-snapshot —simulation MyProjectSimulation_23-04-29_12_00_00 —destination BucketName=weaver-myproject-111122223333-artifacts-us-west-2,ObjectKeyPrefix=myFolder
  ```

`CreateSnapshot` API에 대한 자세한 내용은 *AWS SimSpace Weaver API 참조*의 [CreateSnapshot](https://docs.aws.amazon.com/simspaceweaver/latest/APIReference/API_CreateSnapshot.html)을 참조하세요.

## AWS CLI 를 사용하여 스냅샷에서 시뮬레이션 시작
<a name="working-with_snapshots_cli_start"></a>

**스냅샷에서 시뮬레이션 시작**
+ **명령 프롬프트**에서 `StartSimulation` API를 호출합니다.

  ```
  aws simspaceweaver start-simulation --name simulation-name --role-arn role-arn --snapshot-s3-location s3-location
  ```

  **파라미터**  
이름  
새 시뮬레이션의 이름입니다. 시뮬레이션 이름은에서 고유해야 합니다 AWS 계정. `aws simspaceweaver list-simulations`를 사용하여 기존 시뮬레이션의 이름을 볼 수 있습니다.  
role-arn  
시뮬레이션에서 사용할 앱 역할의 Amazon 리소스 이름(ARN)입니다.  
snapshot-s3-location  
대상 Amazon S3 버킷과 스냅샷 파일의 객체 키를 지정하는 문자열입니다.  
Amazon S3 버킷은 시뮬레이션과 동일한 AWS 리전 에 있어야 합니다.

  **예**

  ```
  aws simspaceweaver start-simulation —name MySimulation —role-arn arn:aws:iam::111122223333:role/weaver-MyProject-app-role —snapshot-s3-location BucketName=weaver-myproject-111122223333-artifacts-us-west-2,ObjectKey=myFolder/snapshot/MyProjectSimulation_23-04-29_12_00_00-230429-1530-27.zip
  ```

`StartSimulation` API에 대한 자세한 내용은 *AWS SimSpace Weaver API 참조*의 [StartSimulation](https://docs.aws.amazon.com/simspaceweaver/latest/APIReference/API_StartSimulation.html)을 참조하세요.

# 스냅샷에 대한 FAQ
<a name="working-with_snapshots_faq"></a>

**스냅샷 중에도 시뮬레이션이 계속 실행되나요?**  
시뮬레이션 리소스는 스냅샷이 생성되는 동안 계속 실행되며 해당 기간에 대한 결제 요금은 계속 청구됩니다. 시간은 시뮬레이션의 최대 지속 시간에 포함됩니다. 스냅샷이 진행되는 동안에는 앱에 틱이 수신되지 않습니다. 클럭 상태가 스냅샷 생성을 시작했던 `STARTED` 상태인 경우 클럭에는 계속 `STARTED` 상태가 표시됩니다. 스냅샷이 완료된 후 앱이 틱을 다시 수신합니다. 클럭 상태가 `STOPPED` 상태였던 경우 클럭 상태는 `STOPPED` 상태로 그대로 유지됩니다. `STARTED` 상태의 시뮬레이션은 클럭 상태가 `STOPPED` 상태이더라도 실행 중이라는 점에 유의하세요.

**스냅샷이 진행 중이고 시뮬레이션이 최대 지속 시간에 도달하면 어떻게 되나요?**  
시뮬레이션은 스냅샷을 완료한 다음 스냅샷 프로세스가 종료되는 즉시 중지됩니다(성공 또는 실패). 스냅샷 프로세스를 미리 테스트하여 소요 시간, 예상 스냅샷 파일 크기, 성공적으로 완료되는지 여부 등을 확인하는 것이 좋습니다.

**스냅샷이 진행 중인 시뮬레이션을 중지하면 어떻게 되나요?**  
시뮬레이션을 중지하면 진행 중인 스냅샷이 즉시 중지됩니다. 스냅샷 파일은 생성되지 않습니다.

**진행 중인 스냅샷을 어떻게 중지할 수 있나요?**  
진행 중인 스냅샷을 중지하는 유일한 방법은 시뮬레이션을 중지하는 것입니다. **시뮬레이션을 중지한 후에는 다시 시작할 수 없습니다.**

**스냅샷을 완료하는 데 얼마나 걸리나요?**  
스냅샷을 만드는 데 필요한 시간은 시뮬레이션에 따라 달라집니다. 스냅샷 프로세스를 미리 테스트하여 시뮬레이션에 소요되는 시간을 확인하는 것이 좋습니다.

**스냅샷 파일 크기는 얼마나 되나요?**  
스냅샷 파일의 크기는 시뮬레이션에 따라 달라집니다. 스냅샷 프로세스를 미리 테스트하여 시뮬레이션에 사용할 수 있는 파일 크기를 확인하는 것이 좋습니다.

# 메시징
<a name="working-with_messaging"></a>

메시징 API는 시뮬레이션 내에서 애플리케이션 간 통신을 간소화합니다. 메시지를 보내고 받는 APIs는 SimSpace Weaver 앱 SDK의 일부입니다. 메시징은 현재 메시지를 보내고 받는 데 최선의 노력을 기울입니다. SimSpace Weaver는 다음 시뮬레이션 틱에서 메시지를 전송/수신하려고 시도하지만 배달, 주문 또는 도착 시간 보장은 없습니다.

**주제**
+ [메시징 사용 사례](#working-with_messaging_use-cases)
+ [메시징 APIs 사용](working-with_messaging_using.md)
+ [메시징을 사용해야 하는 경우](working-with_messaging_when-to-use.md)
+ [메시징 작업 시 팁](working-with_messaging_tips.md)
+ [메시징 오류 및 문제 해결](working-with_messaging_troubleshooting.md)

## 메시징 사용 사례
<a name="working-with_messaging_use-cases"></a>

**시뮬레이션 애플리케이션 간 통신**  
메시징 API를 사용하여 시뮬레이션의 애플리케이션 간에 통신합니다. 이를 사용하여 멀리서 개체의 상태를 변경하거나, 개체 동작을 변경하거나, 정보를 전체 시뮬레이션으로 브로드캐스트할 수 있습니다.

**메시지 수신 확인**  
전송된 메시지에는 메시지 헤더의 발신자에 대한 정보가 포함됩니다. 이 정보를 사용하여 메시지 수신 시 확인 응답을 다시 보냅니다.

**사용자 지정 앱에서 수신한 데이터를 시뮬레이션 내의 다른 앱으로 전달**  
메시징은 클라이언트가에서 실행되는 사용자 지정 앱에 연결하는 방법을 대체하지 않습니다 SimSpace Weaver. 그러나 메시징을 통해 사용자는 클라이언트 데이터를 수신하는 사용자 지정 앱의 데이터를 외부 연결이 없는 다른 앱으로 전달할 수 있습니다. 메시지 흐름은 반대로 작동하여 외부 연결이 없는 앱이 데이터를 사용자 지정 앱에 전달한 다음 클라이언트에 전달할 수 있습니다.

# 메시징 APIs 사용
<a name="working-with_messaging_using"></a>

메시징 APIs는 SimSpace Weaver 앱 SDK(최소 버전 1.16.0)에 포함되어 있습니다. 메시징은 C\$1\$1, Python 및 Unreal Engine 5 및 Unity와의 통합에서 지원됩니다.

메시지 트랜잭션을 처리하는 함수는 `SendMessage` 및 입니다`ReceiveMessages`. 전송된 모든 메시지에는 대상과 페이로드가 포함됩니다. `ReceiveMessages` API는 현재 앱의 인바운드 메시지 대기열에 있는 메시지 목록을 반환합니다.

------
#### [ C\$1\$1 ]

**메시지 전송**

```
AWS_WEAVERRUNTIME_API Result<void> SendMessage(
    Transaction& txn,
    const MessagePayload& payload,
    const MessageEndpoint& destination,
    MessageDeliveryType deliveryType = MessageDeliveryType::BestEffort
    ) noexcept;
```

**메시지 수신**

```
AWS_WEAVERRUNTIME_API Result<MessageList> ReceiveMessages(
    Transaction& txn) noexcept;
```

------
#### [ Python ]

**메시지 전송**

```
api.send_message(
 txn, # Transaction
 payload, # api.MessagePayload
 destination, # api.MessageDestination
 api.MessageDeliveryType.BestEffort # api.MessageDeliveryType
)
```

**메시지 수신**

```
api.receive_messages(
 txn, # Transaction
) -> api.MessageList
```

------

**주제**
+ [메시지 보내기](#working-with_messaging_using_send)
+ [메시지 수신](#working-with_messaging_using_receive)
+ [발신자에게 회신](#working-with_messaging_using_reply)

## 메시지 보내기
<a name="working-with_messaging_using_send"></a>

메시지는 트랜잭션(다른 Weaver API 호출과 유사), 페이로드 및 대상으로 구성됩니다.

### 메시지 페이로드
<a name="working-with_messaging_using_send_payload"></a>

메시지 페이로드는 최대 256바이트의 유연한 데이터 구조입니다. 메시지 페이로드를 생성하는 모범 사례로 다음을 사용하는 것이 좋습니다.

**메시지 페이로드를 생성하려면**

1. 메시지 내용을 정의하는 데이터 구조(예: `struct` C\$1\$1의 )를 생성합니다.

1. 메시지에 보낼 값이 포함된 메시지 페이로드를 생성합니다.

1. `MessagePayload` 객체를 생성합니다.

### 메시지 대상
<a name="working-with_messaging_using_send_destination"></a>

메시지의 대상은 `MessageEndpoint` 객체에 의해 정의됩니다. 여기에는 엔드포인트 유형과 엔드포인트 ID가 모두 포함됩니다. 현재 지원되는 유일한 엔드포인트 유형은 시뮬레이션의 다른 파티션으로 메시지를 보낼 수 `Partition`있는 입니다. 엔드포인트 ID는 대상 대상의 파티션 ID입니다.

메시지에는 대상 주소를 하나만 제공할 수 있습니다. 둘 이상의 파티션에 동시에 메시지를 보내려면 여러 메시지를 생성하고 전송합니다.

위치에서 메시지 엔드포인트를 확인하는 방법에 대한 지침은 섹션을 참조하세요[메시징 작업 시 팁](working-with_messaging_tips.md).

### 메시지 전송
<a name="working-with_messaging_using_send_send"></a>

대상 및 페이로드 객체를 생성한 후 `SendMessage` API를 사용할 수 있습니다.

------
#### [ C\$1\$1 ]

```
Api::SendMessage(transaction, payload, destination, MessageDeliveryType::BestEffort);
```

------
#### [ Python ]

```
api.send_message(txn, payload, destination, api.MessageDeliveryType.BestEffort)
```

------

**메시지 전송의 전체 예**  
다음 예제에서는 일반 메시지를 구성하고 전송하는 방법을 보여줍니다. 이 예제에서는 16개의 개별 메시지를 보냅니다. 각 메시지에는 값이 0과 15 사이인 페이로드와 현재 시뮬레이션 틱이 포함되어 있습니다.

**Example**  

```
// Message struct definition
struct MessageTickAndId
{
    uint32_t id;
    uint32_t tick;
};

Aws::WeaverRuntime::Result<void> SendMessages(Txn& txn) noexcept
{
     // Fetch the destination MessageEndpoint with the endpoint resolver
    WEAVERRUNTIME_TRY(
        Api::MessageEndpoint destination,
        Api::Utils::MessageEndpointResolver::ResolveFromPosition(
        txn,
            "MySpatialSimulation",
            Api::Vector2F32 {231.3, 654.0}
        )
    );
    Log::Info("destination: ", destination);

    WEAVERRUNTIME_TRY(auto tick, Api::CurrentTick(txn));

    uint16_t numSentMessages = 0;
    for (std::size_t i=0; i<16; i++)
    {
        // Create the message that'll be serialized into payload
        MessageTickAndId message {i, tick.value};
        
        // Create the payload out of the struct
        const Api::MessagePayload& payload = Api::Utils::CreateMessagePayload(
            reinterpret_cast<const std::uint8_t*>(&message), 
            sizeof(MessageTickAndId)
        );
        
        // Send the payload to the destination
        Result<void> result = Api::SendMessage(txn, payload, destination);
        if (result.has_failure())
        {
            // SendMessage has failure modes, log them
            auto error = result.as_failure().error();
            std::cout<< "SendMessage failed, ErrorCode: " << error << std::endl;
            continue;
        }
        
        numSentMessages++;
    }

    std::cout << numSentMessages << " messages is sent to endpoint" 
       << destination << std::endl;
    return Aws::WeaverRuntime::Success();
}
```

```
# Message data class
@dataclasses.dataclass
class MessageTickAndId:
    tick: int = 0
    id: int = 0
    
# send messages
def _send_messages(self, txn):
    tick = api.current_tick(txn)
    num_messages_to_send = 16

    # Fetch the destination MessageEndpoint with the endpoint resolver
    destination = api.utils.resolve_endpoint_from_domain_name_position(
       txn,
       "MySpatialSimulation",
       pos
   )
    Log.debug("Destination_endpoint = %s", destination_endpoint)

   for id in range(num_messages_to_send):
       # Message struct that'll be serialized into payload
        message_tick_and_id = MessageTickAndId(id = id, tick = tick.value)
        
       # Create the payload out of the struct
        message_tick_and_id_data = struct.pack(
           '<ii',
           message_tick_and_id.id,
           message_tick_and_id.tick
       )
        payload = api.MessagePayload(list(message_tick_and_id_data))

        # Send the payload to the destination
        Log.debug("Sending message: %s, endpoint: %s",
           message_tick_and_id,
           destination
       )
        api.send_message(
           txn,
           payload,
           destination,
           api.MessageDeliveryType.BestEffort
       )

    Log.info("Sent %s messages to %s", num_messages_to_send, destination)
    return True
```

## 메시지 수신
<a name="working-with_messaging_using_receive"></a>

SimSpace Weaver 는 파티션의 인바운드 메시지 대기열로 메시지를 전송합니다. `ReceiveMessages` API를 사용하여 대기열에서 메시지가 포함된 `MessageList` 객체를 가져옵니다. `ExtractMessage` API로 각 메시지를 처리하여 메시지 데이터를 가져옵니다.

**Example**  

```
Result<void> ReceiveMessages(Txn& txn) noexcept
{
     // Fetch all the messages sent to the partition owned by the app
    WEAVERRUNTIME_TRY(auto messages, Api::ReceiveMessages(txn));
    std::cout << "Received" << messages.messages.size() << " messages" << std::endl;
    for (Api::Message& message : messages.messages)
    {
        std::cout << "Received message: " << message << std::endl;

         // Deserialize payload to the message struct
        const MessageTickAndId& receivedMessage 
            = Api::Utils::ExtractMessage<MessageTickAndId>(message);
        std::cout << "Received MessageTickAndId, Id: " << receivedMessage.id 
            <<", Tick: " << receivedMessage.tick << std::endl;
    }

    return Aws::WeaverRuntime::Success();
}
```

```
# process incoming messages
def _process_incoming_messages(self, txn):
    messages = api.receive_messages(txn)
    for message in messages:
        payload_list = message.payload.data
        payload_bytes = bytes(payload_list)
        message_tick_and_id_data_struct 
           = MessageTickAndId(*struct.unpack('<ii', payload_bytes))

        Log.debug("Received message. Header: %s, message: %s", 
                    message.header, message_tick_and_id_data_struct)

    Log.info("Received %s messages", len(messages))
    return True
```

## 발신자에게 회신
<a name="working-with_messaging_using_reply"></a>

수신된 모든 메시지에는 메시지의 원래 발신자에 대한 정보가 포함된 메시지 헤더가 포함되어 있습니다. message.header.source\$1endpoint를 사용하여 회신을 보낼 수 있습니다.

**Example**  

```
Result<void> ReceiveMessages(Txn& txn) noexcept
{
     // Fetch all the messages sent to the partition owned by the app
    WEAVERRUNTIME_TRY(auto messages, Api::ReceiveMessages(txn));
    std::cout << "Received" << messages.messages.size() << " messages" << std::endl;
    for (Api::Message& message : messages.messages)
    {
        std::cout << "Received message: " << message << std::endl;

         // Deserialize payload to the message struct
        const MessageTickAndId& receivedMessage 
            = Api::Utils::ExtractMessage<MessageTickAndId>(message);
        std::cout << "Received MessageTickAndId, Id: " << receivedMessage.id 
            <<", Tick: " << receivedMessage.tick << std::endl;
        
        // Get the sender endpoint and payload to bounce the message back
        Api::MessageEndpoint& sender = message.header.source_endpoint;
        Api::MessagePayload& payload = message.payload;
        Api::SendMessage(txn, payload, sender);
    }

    return Aws::WeaverRuntime::Success();
}
```

```
# process incoming messages
def _process_incoming_messages(self, txn):
    messages = api.receive_messages(txn)
    for message in messages:
        payload_list = message.payload.data
        payload_bytes = bytes(payload_list)
        message_tick_and_id_data_struct 
           = MessageTickAndId(*struct.unpack('<ii', payload_bytes))

        Log.debug("Received message. Header: %s, message: %s", 
                    message.header, message_tick_and_id_data_struct)
       # Get the sender endpoint and payload 
       # to bounce the message back
       sender = message.header.source_endpoint
       payload = payload_list
       api.send_message(
           txn,
           payload_list,
           sender,
           api.MessageDeliveryType.BestEffort

    Log.info("Received %s messages", len(messages))
    return True
```

# 메시징을 사용해야 하는 경우
<a name="working-with_messaging_when-to-use"></a>

의 메시징 SimSpace Weaver 은 시뮬레이션 애플리케이션 간에 정보를 교환하기 위한 또 다른 패턴을 제공합니다. 구독은 시뮬레이션의 특정 애플리케이션 또는 영역에서 데이터를 읽을 수 있는 풀 메커니즘을 제공하며, 메시지는 시뮬레이션의 특정 애플리케이션 또는 영역으로 데이터를 전송하는 푸시 메커니즘을 제공합니다.

다음은 구독을 통해 데이터를 가져오거나 읽는 대신 메시징을 사용하여 데이터를 푸시하는 것이 더 유용한 두 가지 사용 사례입니다.

**Example 1: 다른 앱에 명령을 전송하여 개체 위치 변경**  

```
// Message struct definition
struct MessageMoveEntity
{
     uint64_t entityId;
    std::array<float, 3> destinationPos;
};

// Create the message 
MessageMoveEntity message {45, {236.67, 826.22, 0.0} };

// Create the payload out of the struct
const Api::MessagePayload& payload = Api::Utils::CreateMessagePayload(
    reinterpret_cast<const std::uint8_t*>(&message), 
    sizeof(MessageTickAndId)
);

// Grab the MessageEndpoint of the recipient app.
Api::MessageEndpoint destination = ...

// One way is to resolve it from the domain name and position
WEAVERRUNTIME_TRY(
    Api::MessageEndpoint destination,
    Api::Utils::MessageEndpointResolver::ResolveFromPosition(
    txn,
        "MySpatialSimulation",
        Api::Vector2F32 {200.0, 100.0}
    )
);

// Then send the message 
Api::SendMessage(txn, payload, destination);
```
수신 측에서 앱은 개체의 위치를 업데이트하고 상태 패브릭에 기록합니다.  

```
Result<void> ReceiveMessages(Txn& txn) noexcept
{
    WEAVERRUNTIME_TRY(auto messages, Api::ReceiveMessages(txn));
    for (Api::Message& message : messages.messages)
    {
        std::cout << "Received message: " << message << std::endl;
         // Deserialize payload to the message struct
        const MessageMoveEntity& receivedMessage 
            = Api::Utils::ExtractMessage<MessageMoveEntity>(message);
            
        ProcessMessage(txn, receivedMessage);
    }

    return Aws::WeaverRuntime::Success();
}

void ProcessMessage(Txn& txn, const MessageMoveEntity& receivedMessage)
{
     // Get the entity corresponding to the entityId
    Entity entity = EntityFromEntityId (receivedMessage.entityId);
    
    // Update the position and write to StateFabric
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
            txn,
            entity,
            k_vector3f32TypeId, // type id of the entity
            reinterpret_cast<std::int8_t*>(&receivedMessage.destinationPos),
            sizeof(receivedMessage.destinationPos)));
    
}
```

**Example 2: 공간 앱에 개체 생성 메시지 전송**  

```
struct WeaverMessage
{
    const Aws::WeaverRuntime::Api::TypeId messageTypeId;
};

const Aws::WeaverRuntime::Api::TypeId k_createEntityMessageTypeId = { 1 };

struct CreateEntityMessage : WeaverMessage
{
    const Vector3 position;
   const Aws::WeaverRuntime::Api::TypeId typeId;
}; 


CreateEntityMessage messageData { 
    k_createEntityMessageTypeId,                           
    Vector3{ position.GetX(), position.GetY(), position.GetZ() },
    Api::TypeId { 0 }
}

WEAVERRUNTIME_TRY(Api::MessageEndpoint destination, Api::Utils::MessageEndpointResolver::ResolveFromPosition(
    transaction, "MySpatialDomain", DemoFramework::ToVector2F32(position)
));

Api::MessagePayload payload = Api::Utils::CreateMessagePayload(
    reinterpret_cast<const uint8_t*>(&messageData),
    sizeof(CreateEntityMessage));
        
Api::SendMessage(transaction, payload, destination);
```
수신 측에서 앱은 상태 패브릭에 새 개체를 생성하고 위치를 업데이트합니다.  

```
Result<void> ReceiveMessages(Txn& txn) noexcept
{
    WEAVERRUNTIME_TRY(auto messageList, Api::ReceiveMessages(transaction));
    WEAVERRUNTIME_TRY(auto tick, Api::CurrentTick(transaction));
    for (auto& message : messageList.messages)
    {
        // cast to base WeaverMessage type to determine MessageTypeId
        WeaverMessage weaverMessageBase = Api::Utils::ExtractMessage<WeaverMessage>(message);
        if (weaverMessageBase.messageTypeId == k_createEntityMessageTypeId)
        {
            CreateEntityMessage createEntityMessageData =
                Api::Utils::ExtractMessage<CreateEntityMessage>(message);
        CreateActorFromMessage(transaction, createEntityMessageData));
        }
        else if (weaverMessageBase.messageTypeId == k_tickAndIdMessageTypeId)
        {
            ...
        }
    }
}

void ProcessMessage(Txn& txn, const CreateEntityMessage& receivedMessage)
{
    // Create entity
    WEAVERRUNTIME_TRY(
        Api::Entity entity,
        Api::CreateEntity(transaction, receivedMessage.typeId)
    );
    
    // Update the position and write to StateFabric
    WEAVERRUNTIME_TRY(Api::StoreEntityIndexKey(
        transaction,
        entity,
        receivedMessage.typeId,
        reinterpret_cast<std::int8_t*>(&receivedMessage.position),
        sizeof(receivedMessage.position)));
}
```

# 메시징 작업 시 팁
<a name="working-with_messaging_tips"></a>

## 위치 또는 앱 이름에서 엔드포인트 확인
<a name="working-with_messaging_tips_resolve-endpoint"></a>

`AllPartitions` 함수를 사용하여 메시지 파티션 ID 및 메시지 대상을 결정하는 데 필요한 공간 경계와 도메인 IDs 가져올 수 있습니다. 그러나 메시지를 보낼 위치를 알고 있지만 파티션 ID는 모르는 경우 MessageEndpointResolver 함수를 사용할 수 있습니다.

```
/**
* Resolves MessageEndpoint's from various inputs
**/
class MessageEndpointResolver
{
    public:
    /**
    * Resolves MessageEndpoint from position information
    **/
    Result<MessageEndpoint> ResolveEndpointFromPosition(
        const DomainId& domainId,
        const weaver_vec3_f32_t& pos);

    /**
    * Resolves MessageEndpoint from custom app name
    **/
    Result<MessageEndpoint> ResolveEndpointFromCustomAppName(
        const DomainId& domainId,
        const char* agentName);
};
```

## 메시지 페이로드 직렬화 및 역직렬화
<a name="working-with_messaging_tips_serialize-payload"></a>

다음 함수를 사용하여 메시지 페이로드를 생성하고 읽을 수 있습니다. 자세한 내용은 로컬 시스템의 앱 SDK 라이브러리에서 MessagingUtils.h를 참조하세요.

```
/**
* Utility function to create MessagePayload from a custom type
*
* @return The @c MessagePayload.
*/
template <class T>
AWS_WEAVERRUNTIME_API MessagePayload CreateMessagePayload(const T& message) noexcept
{
    const std::uint8_t* raw_data = reinterpret_cast<const std::uint8_t*>(&message);

    MessagePayload payload;
    std::move(raw_data, raw_data + sizeof(T), std::back_inserter(payload.data));

    return payload;
}

/**
* Utility function to convert MessagePayload to custom type
*/
template <class T>
AWS_WEAVERRUNTIME_API T ExtractMessage(const MessagePayload& payload) noexcept
{
    return *reinterpret_cast<const T*>(payload.data.data());
}
```

# 메시징 오류 및 문제 해결
<a name="working-with_messaging_troubleshooting"></a>

메시징 APIs.

## 엔드포인트 해결 오류
<a name="working-with_messaging_troubleshooting_endpoint-resolution"></a>

이러한 오류는 앱이 메시지를 보내기 전에 발생할 수 있습니다.

### 도메인 이름 확인
<a name="working-with_messaging_troubleshooting_dns-check"></a>

잘못된 엔드포인트에 메시지를 보내면 다음 오류가 발생합니다.

```
ManifoldError::InvalidArgument {"No DomainId found for the given domain name" }
```

이는 사용자 지정 앱에 메시지를 보내려고 하는데 해당 사용자 지정 앱이 아직 시뮬레이션에 조인하지 않은 경우에 발생할 수 있습니다. `DescribeSimulation` API를 사용하여 메시지를 보내기 전에 사용자 지정 앱이 시작되었는지 확인합니다. 이 동작은 SimSpace Weaver Local 및에서 동일합니다 AWS 클라우드.

### 위치 확인
<a name="working-with_messaging_troubleshooting_position-check"></a>

도메인 이름이 유효하지만 위치가 잘못된 엔드포인트를 확인하려고 하면 다음 오류가 발생합니다.

```
ManifoldError::InvalidArgument {"Could not resolve endpoint from domain : DomainId { value: domain-id } and position: Vector2F32 { x: x-position, y: y-position}" }
```

 SimSpace Weaver 앱 SDK`MessageEndpointResolver`에 포함된 `MessageUtils` 라이브러리에서를 사용하는 것이 좋습니다.

## 메시지 전송 오류
<a name="working-with_messaging_troubleshooting_message-sending"></a>

앱이 메시지를 전송할 때 다음과 같은 오류가 발생할 수 있습니다.

### 앱당, 틱당, 초과된 메시지 전송 한도
<a name="working-with_messaging_troubleshooting_send-limit"></a>

시뮬레이션 틱당 앱당 전송할 수 있는 메시지 수의 현재 제한은 128개입니다. 동일한 틱에 대한 후속 호출은 다음 오류와 함께 실패합니다.

```
ManifoldError::CapacityExceeded {"At Max Outgoing Message capacity: {}", 128}
```

SimSpace Weaver 는 다음 틱에서 전송되지 않은 메시지를 보내려고 시도합니다. 이 문제를 해결하려면 전송 빈도를 낮춥니다. 256바이트 제한보다 작은 메시지 페이로드를 결합하여 아웃바운드 메시지 수를 줄입니다.

이 동작은 SimSpace Weaver Local 및에서 동일합니다 AWS 클라우드.

### 메시지 페이로드 크기 제한을 초과했습니다.
<a name="working-with_messaging_troubleshooting_size-limit"></a>

메시지 페이로드 크기의 현재 제한은 및 모두에서 256바이트SimSpace Weaver Local입니다 AWS 클라우드. 페이로드가 256바이트보다 큰 메시지를 전송하면 다음 오류가 발생합니다.

```
ManifoldError::CapacityExceeded {"Message data too large! Max size: {}", 256}
```

SimSpace Weaver 는 각 메시지를 확인하고 한도를 초과하는 메시지만 거부합니다. 예를 들어 앱이 10개의 메시지를 보내려고 하는데 1개가 검사에 실패하면 해당 메시지 1개만 거부됩니다.는 다른 9개의 메시지를 SimSpace Weaver 보냅니다.

이 동작은 SimSpace Weaver Local 및에서 동일합니다 AWS 클라우드.

### 대상이 소스와 동일함
<a name="working-with_messaging_troubleshooting_dst-src-same"></a>

앱은 자신이 소유한 파티션에 메시지를 보낼 수 없습니다. 앱이 소유한 파티션에 메시지를 보내는 경우 다음 오류가 발생합니다.

```
ManifoldError::InvalidArgument { "Destination is the same as source" }
```

이 동작은 SimSpace Weaver Local 및에서 동일합니다 AWS 클라우드.

### 최선의 메시지 전송
<a name="working-with_messaging_troubleshooting_best-effort"></a>

SimSpace Weaver 는 메시지 전송을 보장하지 않습니다. 서비스는 후속 시뮬레이션 틱에서 메시지 전송을 완료하려고 시도하지만 메시지가 손실되거나 지연될 수 있습니다.