[task] | two_turn [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | 모델(Model)을 사용해 카드를 생성하는 방식에서 SpriteGUIRendererComponent의 ImageRUID를 투명 RUID로 설정하는 구체적인 절차 [answer] | 제공된 참고 문서에는 `SpriteGUIRendererComponent`의 `ImageRUID`를 투명 RUID로 설정하는 **구체적인 조작 절차(클릭 순서, 메뉴 경로 등)** 가 상세히 설명되어 있지 않습니다. 문서에 나와 있는 내용만으로는 다음 사실만 확인할 수 있습니다: 1. **대상 엔티티**: `Workspace - MyDesk - Models - Model_Card` 2. **설정 대상 컴포넌트**: `SpriteGUIRendererComponent` 3. **설정 대상 프로퍼티**: `ImageRUID` 4. **사 [followup] | CardSet 데이터셋에 이미 저장된 29개 SpriteRUID 외에 마지막 하나를 Resource Storage에서 찾아 추가하는 과정 [followup_answer] | `CardSet` 데이터셋에 마지막 하나인 SpriteRUID를 추가하는 과정은 다음과 같습니다. 1. **Resource Storage**를 열고 **MSW 리소스**를 선택합니다. 2. **Sprite - Item** 카테고리에서 사용할 네모 모양 스프라이트를 찾습니다. 3. 해당 스프라이트에서 **Copy RUID**를 선택하여 RUID를 복사합니다. 4. **Workspace - MyDesk - DataSets - CardSet**을 선택한 후, 복사한 RUID를 붙여 넣습니다. 이 과정을 통해 총 30개의 SpriteR [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 서버와 클라이언트 간 통신 시 매개변수 타입이 전송 불가능한 형태일 때 발생하는 문제와 대응법은 어떻게 되나요? [answer] | 서버와 클라이언트 간 실행 제어 함수를 통해 통신할 때, 매개 변수는 전송 가능한 "약속된 형태"로 변환된 후에만 전송됩니다. `number`나 `string`과 같은 기본 타입은 이러한 약속된 형태로 변환되어 전송될 수 있습니다. 그러나 **`any`** 타입은 어떤 타입이든 호환 가능하지만, 전송 가능한 특정 형태로 변환할 수 없으므로 서버와 클라이언트 간에 전송할 수 없습니다. `any` 타입의 매개 변수를 가진 실행 제어 함수를 다른 공간(Server/Client)에서 호출하면 해당 시점에 에러가 발생합니다. ### 문제 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 입력 처리나 내 캐릭터 로직을 작성할 때 `self.Entity`와 `_UserService.LocalPlayer`를 어떻게 비교해야 제 클라이언트에서만 실행되나요? [answer] | 메이플스토리 월드에서 입력 처리나 내 캐릭터 로직을 제 클라이언트에서만 실행되도록 하려면, 이벤트 핸들러에서 `self.Entity`와 `_UserService.LocalPlayer`를 직접 비교하여 현재 스크립트가 적용된 엔티티가 '나'의 캐릭터인지 확인해야 합니다. `Player` 엔티티는 멀티 플레이 환경에서 모든 참여자의 클라이언트에 존재하므로, 특정 플레이어의 키 입력 이벤트가 발생했을 때 다른 플레이어의 클라이언트에서도 동일하게 트리거됩니다. 이를 방지하기 위해 이벤트 처리 시작 부분에 `self.Entity`가 `_U [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardBoard 컴포넌트의 CardSpawnRoot 프로퍼티 선언부와 CreateCardEntities 함수 본문만 출력하고, 나머지 설명은 주석으로 작성하세요. [answer] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void CreateCardEntities() { local cardModelId = "Model_Card EntiryID" local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawn [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 1. **CardBoard** 컴포넌트를 **CardPairGameUI** 그룹에 추가합니다. 2. **CardBoard** 컴포넌트를 선택해 스크립트 에디터를 엽니다. 3. **CardSpawnRoot** 프로퍼티를 추가하고, 반환 타입을 Entity로 변경합니다. 4. 엔티티는 **UI_Block_Group**으로 지정합니다. ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardPairGameLogic 스크립트에서 OnBeginPlay()와 StartGame() 함수 내부의 코드만 단계별 번호 목록으로 나열하세요. [answer] | CardPairGameLogic 스크립트에서 `OnBeginPlay()`와 `StartGame()` 함수 내부의 코드는 다음과 같습니다. **OnBeginPlay() 함수 내부 코드** 1. `self:StartGame()` **StartGame() 함수 내부 코드** 1. `self.CardPairGameUI.Enable = true` 2. `local cardboard = self.CardPairGameUI.CardBoard` 3. `cardboard:SetCards()` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 1. **CardBoard** 컴포넌트를 **CardPairGameUI** 그룹에 추가합니다. 2. **CardBoard** 컴포넌트를 선택해 스크립트 에디터를 엽니다. 3. **CardSpawnRoot** 프로퍼티를 추가하고, 반환 타입을 Entity로 변경합니다. 4. 엔티티는 **UI_Block_Group**으로 지정합니다. ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 멀티 플레이 환경에서 내가 획득한 돈만 내 화면의 UI에 표시되도록 코드를 수정하려면 어떤 체크가 필요할까요? [answer] | 멀티 플레이 환경에서 내가 획득한 돈만 내 화면의 UI에 표시하려면, 돈이 변경되는 로직 내부에서 **현재 처리되고 있는 Entity가 내 캐릭터인지**를 확인하는 체크가 필요합니다. 문서에서 제시한 문제 상황(다른 사람이 돈을 획득해도 내 UI가 갱신되는 현상)을 해결하기 위해, `SetMoney` 메서드 호출 시점의 Entity가 `_UserService.LocalPlayer`와 동일한지 비교해야 합니다. 아래는 Local 체크를 포함하여 수정된 코드 예시입니다. ```lua Method: void SetMoney(numb [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | `SetCards` 메서드 내부에서 호출되는 로직에 대해 2문장 이내로 요약하세요. [answer] | `SetCards` 메서드는 `CreateCardEntities` 메서드를 호출하여 카드 엔티티 생성 로직을 실행합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void CreateCardEntities() { local cardModelId = "Model_Card EntiryID" local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawn
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | _wait() 함수를 사용하여 1초 단위로 OnUpdate 로직 대신 주기적으로 실행되며, _DataStorage:SetAndWait() 로 데이터를 저장하고 _DataStorage:GetAndWait() 로 데이터를 가져와서 로컬 변수에 할당하는 스크립트를 작성해줘 [answer] | 참고 문서에는 `wait()` 함수가 Wait 계열 함수의 예시로 언급되어 있지만, `_wait()`이라는 특정 함수 이름은 문서에 존재하지 않습니다. 또한 `_DataStorage:SetAndWait()`와 `_DataStorage:GetAndWait()`의 정확한 파라미터 형식(키/값의 타입 등)이 문서에 명시되어 있지 않아, 잘못된 API 시그니처를 추측하여 코드를 작성할 수 없습니다. 따라서 요청하신 **`_wait()`** 사용과 **`_DataStorage`**의 구체적인 데이터 저장/가져오기 로직을 포함한 코드를 문서 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리 월드는 쉬운 코딩을 위해 다양한 편의 기능을 지원하고 있습니다. 대표적으로 간편한 실행 제어 설정 기능이 있습니다. 그러나 간단한 설정이 얽히며 복잡해질 수 있고, 잘못 설정하면 원하는 기능이 제대로 실행되지 않을 수도 있습니다. 때로는 월드가 매우 느려질 수도 있습니다. 이런 문제는 월드의 규모가 작거나 월드 제작 초창기에는 발견하기 어렵습니다. 하지만 월드의 규모가 커지고 완성도가 높아질수록 문제가 될 수 있습니다. 그러므로 제작 초기부터 모든 동작이 원활하고 효율적으로 진행되도록 구조를 고민
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | _EntityService:GetEntity() 로 엔티티를 가져온 뒤 nil 체크 대신 isvalid() 를 사용하여 상태가 '삭제 처리 대기' 또는 '삭제 중'인 경우를 안전하게 우회하고, 유효할 때만 _TimerService:SetTimerOnce() 를 호출하여 5초 후 삭제 요청을 보내는 스크립트를 작성해줘 [answer] | 메이플스토리 월드에서 엔티티를 삭제 요청하면 즉시 `nil`이 되지 않고, '삭제 처리 대기'와 '삭제 중' 상태가 있는 동안에도 참조가 유효한 것처럼 보일 수 있습니다. 단순히 `~= nil`로만 체크하면 이 과도기적인 상태의 엔티티에 대해 코드가 실행되어 의도하지 않은 동작이나 오류가 발생할 수 있습니다. 이러한 문제를 해결하기 위해 `isvalid()` 함수를 사용해야 합니다. `isvalid()`는 대상이 단순히 존재하는지 여부를 넘어, 실제로 안전하고 유효하게 접근할 수 있는 상태인지(즉, 삭제 과정에 있지 않은지)를 종 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리 월드는 쉬운 코딩을 위해 다양한 편의 기능을 지원하고 있습니다. 대표적으로 간편한 실행 제어 설정 기능이 있습니다. 그러나 간단한 설정이 얽히며 복잡해질 수 있고, 잘못 설정하면 원하는 기능이 제대로 실행되지 않을 수도 있습니다. 때로는 월드가 매우 느려질 수도 있습니다. 이런 문제는 월드의 규모가 작거나 월드 제작 초창기에는 발견하기 어렵습니다. 하지만 월드의 규모가 커지고 완성도가 높아질수록 문제가 될 수 있습니다. 그러므로 제작 초기부터 모든 동작이 원활하고 효율적으로 진행되도록 구조를 고민
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 혼자 테스트할 때는 정상 작동하던 스크립트가 멀티 플레이로 전환했을 때 비정상적으로 동작하는 주된 원인은 무엇인가요? [answer] | 혼자 테스트할 때는 정상 작동했으나 멀티 플레이 환경에서 비정상적으로 동작하는 주된 원인은 **Local(로컬) 개념의 누락**입니다. 메이플스토리 월드에서 `UIEntity`, **입력(Input)**, 그리고 **내 캐릭터**는 해당 유저의 클라이언트에만 존재하는 개념입니다. 스크립트에서 이들을 처리할 때 단순히 일반 Entity나 입력 이벤트만으로 처리하면, 다른 플레이어의 입력이나 상태 변경까지 내 클라이언트에서 반응하게 되어 버그가 발생합니다. 특히 **내 캐릭터**를 처리할 때는 `_UserService.LocalPl [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | 문서에 제시된 CreateCardEntities 함수에서 _SpawnService:SpawnByModelId() 호출 시 인자 순서(모델ID, 이름, 위치, parent)를 표로 정리하세요. [answer] | `_SpawnService:SpawnByModelId()` 호출 시 인자 순서는 다음과 같습니다. | 순서 | 인자 이름 | 설명 | | :--- | :--- | :--- | | 1 | 모델 ID | 소환할 모델의 Entry ID (예: `cardModelId`) | | 2 | 이름 | 소환된 엔티티에 부여할 이름 (예: `"Card"`) | | 3 | 위치 | 모델을 불러올 위치 (예: `Vector3(0,0,0)`) | | 4 | parent | 불러올 위치의 기준이 되는 부모 엔티티 (예: `self.CardSpawnRoot [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 1. **CardBoard** 컴포넌트를 **CardPairGameUI** 그룹에 추가합니다. 2. **CardBoard** 컴포넌트를 선택해 스크립트 에디터를 엽니다. 3. **CardSpawnRoot** 프로퍼티를 추가하고, 반환 타입을 Entity로 변경합니다. 4. 엔티티는 **UI_Block_Group**으로 지정합니다. ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `OnUpdate`에서 매 프레임마다 다른 실행 공간의 메서드를 호출하지 않도록 주의하며, `delta`를 누적하여 1초가 될 때마다 로컬 로직만 수행하도록 최적화한 스크립트를 작성해줘 [answer] | `OnUpdate`는 매 프레임마다 실행되므로, 이 안에서 다른 실행 공간(예: Server에서 Client 호출 또는 그 반대)의 메서드를 호출하거나 동기화 프로퍼티를 변경하면 막대한 네트워크 비용이 발생합니다. 이를 방지하고 1초 간격으로 로컬 로직을 실행하려면 `delta`를 누적하는 변수를 프로퍼티로 선언하고, `OnUpdate`에서 해당 값을 업데이트한 후 1초 이상 되었을 때만 로직을 수행하고 누적값을 초기화해야 합니다. 다음은 이러한 요구사항을 충족하는 mLua 스크립트입니다. ```lua Property: [Non [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait`는 대기한 뒤에 직접 어떤 행동을 수행하는 것이고, `TimerService`는 해당 행동을 수행하는 별도의 일을 만드는 것입니다. `wait`는 직렬, `TimerService`는 병렬 형태로 동작합니다. 따라서 `wait`의 사용 코드는 다음과 같이 표현할 수 있습니다 ```lua local targetEntity = _EntityService:GetEntity("a22396e1-6365-42d4-9cef-5924c223f7fa") targetEntity.TestComponent:ShowMessage() func
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | 서버가 모든 클라이언트에게 Client 함수를 전송하는 것과 특정 유저에게만 전송하는 것의 차이점은 무엇인가요? [answer] | 서버가 실행 공간이 **Client**로 설정된 함수를 호출할 때, 모든 클라이언트에게 전송하는 것과 특정 유저에게만 전송하는 것의 차이점은 **네트워크 비용**과 **코드 구조(매개 변수)** 에서 발생합니다. **1. 모든 클라이언트에게 전송 (기존 방식)** * **동작 원리:** 서버가 함수를 호출하면 해당 메시지가 월드에 있는 모든 클라이언트로 전송됩니다. * **클라이언트 측 처리:** 각 클라이언트는 수신한 메시지에서 `UserId`를 확인하여, 요청을 보낸 본인인지 판단해야 합니다. 본인인 경우에만 로직을 실 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 어떤 함수의 실행 제어 옵션이 **Client**로 설정되어 있다면 해당 함수는 모든 클라이언트에서 실행됩니다. 하지만 서버에서는 모든 클라이언트가 아니라 특정 클라이언트에만 함수를 실행하고 싶은 상황이 생길 수 있습니다. 특정 클라이언트가 서버에 어떤 요청(Request)을 보내고 서버가 로직을 처리한 이후, 해당 클라이언트에 응답(Response) 하는 상황이 그것입니다. 예를 들어 봅시다. 1. A 클라이언트에서 아이템 구매 버튼을 누른다면 : (Request) 2. 서버에서는 A가 가진 돈을 차감하고 아이템 개수를 늘린 다
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | 문서에 포함된 `CreateCardEntities` 메서드의 코드만 출력하고, 나머지 설명은 주석으로 작성하세요. [answer] | ```lua -- 문서에 포함된 CreateCardEntities 메서드 코드입니다. -- 이 메서드는 client only 공간에서 실행되며, _SpawnService:SpawnByModelId를 사용해 카드 엔티티를 생성합니다. Method: [client only] void CreateCardEntities() { local cardModelId = "Model_Card EntiryID" local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Ve [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void CreateCardEntities() { local cardModelId = "Model_Card EntiryID" local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawn
[task] | two_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | OnUpdate()에서 wait() 사용 자제 원칙을 지키면서도 특정 시간 간격으로 로직을 실행해야 하는 경우, 어떤 서비스나 메서드를 활용하는 것이 권장되나요? [answer] | 제공된 참고 문서에는 `OnUpdate()` 내에서 `wait()` 사용 자제 원칙과 그 이유(객체 누적으로 인한 성능 저하)에 대해 명시되어 있습니다. 하지만, **특정 시간 간격으로 로직을 실행하기 위해 권장되는 구체적인 서비스나 메서드(예: 타이머 서비스 등)에 대한 정보는 문서에 포함되어 있지 않습니다.** 문서에서 확인할 수 있는 내용은 다음과 같습니다. 1. `OnUpdate()`에서 `wait()`를 사용하면 Update 객체가 계속 쌓여 메모리 및 속도 저하, 디버깅 난이도 증가라는 문제가 발생합니다. 2. 따라서 [followup] | _TimerService:SetTimerOnce() 또는 다른 타이머 관련 API를 사용하여 OnUpdate()의 비효율을 피하면서도 주기적인 로직을 실행하는 코드 예제를 작성해 주세요. [followup_answer] | 제공된 참고 문서에는 `_TimerService:SetTimerOnce()` 또는 다른 타이머 관련 API에 대한 내용이 **포함되어 있지 않습니다.** 문서에서 확인 가능한 것은 `OnUpdate()`에서 `wait()`를 사용할 때 발생하는 문제점(Update 객체 누적, 성능 저하, 디버깅 난이도 증가)과, 이를 대체하기 위해 문서 예제에서 보여준 **시간 누적(AccTime) 방식**뿐입니다. 따라서 요청하신 `_TimerService` 기반 코드 예제는 문서에 근거하여 작성할 수 없으며, 문서에 존재하지 않는 API를 [scores] | [judge_reason] | [source_doc] | 그럼 `OnUpdate()`에서 사용할 경우를 살펴보겠습니다. `OnUpdate()` 문에서 아래 코드는 어떻게 동작할까요? ```lua log("TEST") -- No.1 wait(1) log("TEST2") -- No.2 ``` * 기대 동작: TEST - 1초 대기 - TEST2 - TEST1 - 1초 대기 - TEST2 * 실제 동작: **TEST - TEST - TEST - .... (첫 번째 TEST가 찍힌 시점부터 1초가 지났을 때 ) - TEST2** `OnUpdate()`는 논리적인 측면에서 수행이 끝날 당시
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `wait`와 `TimerService`의 직렬/병렬 차이를 이용해, `OnBeginPlay`에서 "A"를 출력하고 1초 후 "B"를 출력한 뒤, 동시에 2초 후 "C"를 출력하는 스크립트를 작성해줘 [answer] | `wait`는 현재 실행 흐름을 멈추고 대기한 후 다음 코드를 순서대로(직렬) 실행하는 반면, `_TimerService`는 현재 코드의 실행을 방해하지 않고 지정된 시간 후에 별도의 작업을 예약하여 병렬로 실행합니다. 요청하신 조건인 **"A" 출력 → 1초 대기 후 "B" 출력 → (B 출력과 동시에) 2초 후 "C" 출력**을 만족시키기 위해서는 다음과 같은 논리가 필요합니다. 1. `OnBeginPlay`가 시작되면 즉시 `"A"`를 출력합니다. 2. `wait(1)`을 사용하여 1초 동안 직렬로 대기합니다. 3. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait`는 대기한 뒤에 직접 어떤 행동을 수행하는 것이고, `TimerService`는 해당 행동을 수행하는 별도의 일을 만드는 것입니다. `wait`는 직렬, `TimerService`는 병렬 형태로 동작합니다. 따라서 `wait`의 사용 코드는 다음과 같이 표현할 수 있습니다 ```lua local targetEntity = _EntityService:GetEntity("a22396e1-6365-42d4-9cef-5924c223f7fa") targetEntity.TestComponent:ShowMessage() func
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | `CardSpawnRoot` 속성의 경로와 `cardModelId` 변수의 값을 표로 정리해서 보여주세요. [answer] | 참고 문서에 제시된 mLua 코드 예제에서 `CardSpawnRoot` 속성의 경로와 `cardModelId` 변수의 값은 다음과 같습니다. | 항목 | 값 | | :--- | :--- | | `CardSpawnRoot` 경로 | `/ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group` | | `cardModelId` 값 | `"Model_Card EntiryID"` | 참고로 문서의 첫 번째 코드 블록에서 `CardSpawnRoot`는 `/ui/CardParirGameUi/...` 경 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void CreateCardEntities() { local cardModelId = "Model_Card EntiryID" local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawn
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | OnUpdate 메서드 내에서 wait() 계열 함수를 남발하지 않고, 대신 _TimerService:SetTimerOnce() 를 활용하여 효율적인 주기적 실행을 구현하되, 실행 중 _EntityService 로 조회한 대상이 isvalid() 검사에 실패하면 즉시 처리를 중단하는 로직을 포함하는 스크립트를 작성해줘 [answer] | 메이플스토리 월드에서 `OnUpdate` 내부에 `wait()` 계열 함수를 사용하면 프레임 처리가 막혀 월드 성능 저하를 유발할 수 있습니다. 따라서 주기적인 작업은 `OnUpdate`에서 타이머를 설정하거나, `_TimerService:SetTimerOnce()`를 통해 비동기적으로 스케줄링하는 방식이 효율적입니다. 또한 엔티티는 삭제 요청 후 실제 nil이 되기까지 `삭제 요청 → 삭제 처리 대기 → 삭제 중 → 삭제 완료 → nil`이라는 과정을 거치므로, 단순한 `nil` 체크(`~= nil`)보다는 `isvalid()` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리 월드는 쉬운 코딩을 위해 다양한 편의 기능을 지원하고 있습니다. 대표적으로 간편한 실행 제어 설정 기능이 있습니다. 그러나 간단한 설정이 얽히며 복잡해질 수 있고, 잘못 설정하면 원하는 기능이 제대로 실행되지 않을 수도 있습니다. 때로는 월드가 매우 느려질 수도 있습니다. 이런 문제는 월드의 규모가 작거나 월드 제작 초창기에는 발견하기 어렵습니다. 하지만 월드의 규모가 커지고 완성도가 높아질수록 문제가 될 수 있습니다. 그러므로 제작 초기부터 모든 동작이 원활하고 효율적으로 진행되도록 구조를 고민
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | Local 개념과 관련된 버그가 혼자 테스트할 때는 나타나지 않고 멀티 테스트 시 발생하는 원인을 표로 정리하세요. [answer] | 메이플스토리 월드에서는 대부분의 엔티티와 컴포넌트가 모든 클라이언트에 동기화되어 존재합니다. 하지만 **입력(Input)**, **UIEntity**, **내 캐릭터(LocalPlayer)** 는 해당 플레이어의 클라이언트에만 존재하는 로컬(Local) 개념입니다. 혼자 테스트할 때는 내 클라이언트와 서버(또는 동기화되는 대상)가 동일하므로 로컬과 글로벌의 구분이 모호하게 느껴져 버그가 드러나지 않습니다. 그러나 멀티 플레이 환경에서는 **다른 플레이어의 클라이언트에서도 동일한 스크립트가 실행**되기 때문에, 로컬이 아닌 다른 플 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 서버의 OnBeginPlay()에서 클라이언트로 정보를 바로 전송하는 대신, 클라이언트의 OnBeginPlay()에서 서버로 호출을 보내어 서버의 응답 메서드를 통해 데이터를 받는 통신 구조를 가진 스크립트를 작성해줘 [answer] | 서버가 먼저 시작되고 클라이언트가 나중에 접속하기 때문에, 서버의 `OnBeginPlay()`에서 클라이언트로 정보를 보내면 클라이언트가 아직 존재하지 않아 데이터가 전달되지 않습니다. 이를 해결하기 위해 클라이언트가 `OnBeginPlay()` 시점에 서버에 호출을 보내고, 서버가 그 응답으로 데이터를 전송하는 구조를 사용해야 합니다. 아래는 이러한 통신 구조를 적용한 mLua 스크립트 예시입니다. ```lua Method: [Client Only] void OnBeginPlay() { self:NotifyClientO [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `OnUpdate()`에서 `wait()`를 사용하지 않고, `delta`를 누적하여 5초가 지난 시점에 `_UserService`를 통해 현재 접속한 유저 목록을 `log()`로 출력하는 기능을 가진 스크립트를 작성해줘 [answer] | `OnUpdate()` 내에서 `wait()`를 사용하지 않고 `delta`를 누적하여 5초가 지난 시점에 유저 목록을 출력하는 스크립트는 다음과 같습니다. `Property:` 블록에 누적 시간을 저장할 변수를 선언하고, `OnUpdate()`에서 매 프레임마다 전달되는 `delta` 값을 더해 5초 이상 되었을 때 `_UserService.UserEntities`를 조회하여 `log()`로 출력하도록 작성했습니다. ```lua Property: [None] number elapsedTime = 0 number targetTi [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 그럼 `OnUpdate()`에서 사용할 경우를 살펴보겠습니다. `OnUpdate()` 문에서 아래 코드는 어떻게 동작할까요? ```lua log("TEST") -- No.1 wait(1) log("TEST2") -- No.2 ``` * 기대 동작: TEST - 1초 대기 - TEST2 - TEST1 - 1초 대기 - TEST2 * 실제 동작: **TEST - TEST - TEST - .... (첫 번째 TEST가 찍힌 시점부터 1초가 지났을 때 ) - TEST2** `OnUpdate()`는 논리적인 측면에서 수행이 끝날 당시
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 클라이언트의 OnBeginPlay()에서 self:NotifyClientOnBegin()을 호출하여 서버에 접속 시점을 알리고, 서버의 NotifyClientOnBegin()에서 _UserService를 이용해 해당 유저에게 데이터를 전송하는 스크립트를 작성해줘 [answer] | 참고 문서에 `_UserService` API나 해당 서비스가 제공하는 메서드에 대한 정보가 포함되어 있지 않습니다. 따라서 `_UserService`를 이용하여 데이터를 전송하는 구체적인 코드 작성은 불가능합니다. 문서에 명시된 방식은 클라이언트의 `OnBeginPlay()`에서 서버의 실행 제어 함수를 호출하여 서버가 클라이언트로 데이터를 전송하는 구조입니다. 아래는 문서의 예제 형식을 따르되, `_UserService` 없이 서버에서 클라이언트로 데이터를 전달하는 기본 구조만 작성한 스크립트입니다. ```lua Method [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardSet 데이터셋에 이미 29개의 SpriteRUID가 들어있는데, 마지막 하나를 추가하기 위해 Resource Storage에서 RUID를 복사해서 붙여넣어야 하는 이유는 무엇인가요? [answer] | CardSet 데이터셋은 카드 모델에 사용할 이미지 목록을 저장하는 역할을 합니다. 게임에 필요한 카드 이미지는 총 30개인데, 데이터셋에는 이미 29개만 들어있어 하나가 부족합니다. 따라서 마지막 1개의 이미지를 추가하기 위해 Resource Storage에서 원하는 스프라이트를 찾아 RUID를 복사하고, CardSet에 붙여넣어야 합니다. 이 과정을 통해 CardSet에 30개의 SpriteRUID가 모두 저장되어, 카드 생성 시 필요한 모든 이미지를 불러올 수 있게 됩니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | Model_Card의 SpriteGUIRendererComponent에서 ImageRUID에 투명 이미지를 설정하는 구체적인 방법과 필요한 RUID 값은 무엇인가요? [answer] | Model_Card의 SpriteGUIRendererComponent에서 ImageRUID에 투명 이미지를 설정하는 방법은 다음과 같습니다. 1. **Workspace - MyDesk - Models - Model_Card**를 선택합니다. 2. 해당 모델의 **SpriteGUIRendererComponent** 컴포넌트를 찾아 **ImageRUID** 프로퍼티를 확인합니다. 3. **ImageRUID** 값에 아래 투명 이미지 RUID를 입력하여 설정합니다. **투명 RUID 값:** `98c34caab88ee34459cb3e [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | UserService의 LocalPlayer를 기준으로 InputService의 HandleTouchEvent에서 터치 좌표가 화면 중앙을 벗어나면 self:MoveToEdge()를 실행하는 로직을 작성해줘 [answer] | 메이플스토리 월드의 `Touch` 이벤트는 문서에 언급된 `KeyDownEvent`와 달리 **좌표(좌표값)를 직접 전달하는 파라미터가 명시되어 있지 않습니다.** 문서에는 `_UserService.LocalPlayer`를 사용하여 "내가 아닌 다른 플레이어"의 입력을 필터링하는 로직은 명확히 설명되어 있지만, `HandleTouchEvent`가 터치 좌표(`x`, `y` 등)를 어떻게 전달하는지(예: `event.position`, `event.x` 등)에 대한 구체적인 API 명세가 제공되지 않았습니다. 따라서 **문서에 없 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 행동 트리를 활용한 AI 만들기 [question] | FSM, HFSM, 행동 트리의 차이점을 각각 한 문장으로 비교해서 설명하세요. [answer] | FSM은 특정 조건이 충족되면 유한개의 상태 중 하나로 전환되며 해당 상태에 맞는 액션을 수행하는 모델이고, HFSM은 상태 변화의 흐름을 모듈화하여 재사용성을 높이고 현재 상태의 출처를 파악하기 쉽게 만들었으나 개별 상태의 재사용이 어려운 단점이 있으며, 행동 트리는 개별 로직으로 모듈화된 노드들이 결합 순서에 따라 순차적으로 실행되어 특정 행동의 연결과 해제가 용이하고 재사용이 가능한 구조입니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 행동 트리와 BTNodeType 개념을 이해하고, AIComponent를 활용해 Action Node를 생성하는 방법을 알아봅시다. [행동 트리 노드 만들기](/docs?postId=1155)에서 행동 트리 구현 방법과 예제를 소개합니다. ##### 참고 가이드 * [행동 트리 노드 만들기](/docs?postId=1155) * [Behaviour Tree Editor로 행동 트리 만들기](/docs?postId=1399) ##### 참고 API Reference * [BTNode](/apiReference
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 행동 트리를 활용한 AI 만들기 [question] | AIComponent와 연결된 BTNode 계열 API(ParallelNode, RandomSelectorNode, SelectorNode, SequenceNode)를 표로 정리하고 각 API의 역할을 한 줄로 요약하세요. [answer] | 제공된 참고 문서에는 `ParallelNode`, `RandomSelectorNode`, `SelectorNode`, `SequenceNode`의 API Reference 링크와 mLua 예제 코드 내에서 `Parallel`, `RandomSelector`, `Selector`, `Sequence`라는 함수명이 사용된 것 외에는 각 노드의 구체적인 역할이나 동작 방식에 대한 설명이 포함되어 있지 않습니다. 따라서 제공된 문서 내용만으로는 각 API의 역할을 정확히 요약하여 표로 정리할 수 없습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 행동 트리와 BTNodeType 개념을 이해하고, AIComponent를 활용해 Action Node를 생성하는 방법을 알아봅시다. [행동 트리 노드 만들기](/docs?postId=1155)에서 행동 트리 구현 방법과 예제를 소개합니다. ##### 참고 가이드 * [행동 트리 노드 만들기](/docs?postId=1155) * [Behaviour Tree Editor로 행동 트리 만들기](/docs?postId=1399) ##### 참고 API Reference * [BTNode](/apiReference
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | 동일한 카드 엔티티를 여러 장 배치할 때 모델을 사용해야 하는 이유는 무엇이며, 직접 엔티티를 복제하는 것과 어떤 차이가 있나요? [answer] | 문서에 따르면, 동일한 프로퍼티를 가진 엔티티를 여러 번 사용해야 할 때는 **모델(Model)**을 사용하는 것이 좋습니다. 이는 필요한 만큼의 카드 엔티티를 직접 미리 만드는 것보다는 '틀(판본)'을 만들어 한 번에 생성하는 방식이 효율적이기 때문입니다. 모델은 특정 엔티티의 컴포넌트와 프로퍼티 정보를 가지고 있는 판본입니다. 이러한 모델은 동일한 행동을 하는 몬스터를 배치하거나, 같은 오브젝트를 반복적으로 배치하는 등 동일한 구조를 가진 엔티티를 효율적으로 관리하기 위해 활용됩니다. 카드 짝 맞추기 게임에서는 월드가 실행될 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `OnUpdate()`에서 `wait()` 대신 `_TimerService:SetTimerOnce()`를 사용해 2초 간격으로 `log("Tick")`을 반복 출력하되, `OnEndPlay()` 시점에 타이머가 정리되도록 하는 스크립트를 작성해줘 [answer] | `OnUpdate()`에서 `wait()`를 사용하게 되면 Update 객체가 정상적으로 종료되지 못하고 계속 쌓여 메모리 및 성능 문제가 발생합니다. 이를 방지하고 정확한 시간 간격으로 로직을 실행하려면 `_TimerService:SetTimerRepeat()`을 사용해야 합니다. 요청하신 **2초 간격**으로 `log("Tick")`을 출력하고, 스크립트가 종료될 때(`OnEndPlay()`) 타이머를 정리하는 스크립트는 다음과 같습니다. ```lua Property: [None] integer TimerId = 0 Met [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 그럼 `OnUpdate()`에서 사용할 경우를 살펴보겠습니다. `OnUpdate()` 문에서 아래 코드는 어떻게 동작할까요? ```lua log("TEST") -- No.1 wait(1) log("TEST2") -- No.2 ``` * 기대 동작: TEST - 1초 대기 - TEST2 - TEST1 - 1초 대기 - TEST2 * 실제 동작: **TEST - TEST - TEST - .... (첫 번째 TEST가 찍힌 시점부터 1초가 지났을 때 ) - TEST2** `OnUpdate()`는 논리적인 측면에서 수행이 끝날 당시
[task] | two_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | OnUpdate()에서 wait()를 사용할 때 기대 동작과 실제 동작이 어떻게 달라지나요? [answer] | `OnUpdate()`에서 `wait()`를 사용할 때 기대 동작과 실제 동작은 다음과 같이 크게 다릅니다. **기대 동작** 개발자가 의도하는 동작은 현재 프레임의 로직을 잠시 멈추고, `wait()`에 입력한 시간만큼 기다린 후 나머지 로직을 이어가되, 그 다음 프레임에서는 다시 정상적인 `OnUpdate()` 순서가 유지되는 것입니다. 예를 들어 1초를 기다린 후 로그를 출력하더라도, 그 다음 프레임에서는 이전 프레임의 중단된 로직이 이어지는 것이 아닙니다. **실제 동작** `OnUpdate()`는 프레임이 진행될 때마다 [followup] | OnUpdate() 내부에서 wait()가 호출될 때 Update 객체가 왜 계속 쌓이는지, 그리고 이것이 메모리나 성능에 어떤 구체적인 문제를 일으키는지 자세히 설명해 주세요. [followup_answer] | `OnUpdate()` 내부에서 `wait()`를 호출했을 때 Update 객체가 계속 쌓이는 이유와 이로 인해 발생하는 구체적인 문제는 다음과 같습니다. ### 1. Update 객체가 계속 쌓이는 이유 `OnUpdate()`는 기본적으로 한 프레임의 로직이 모두 종료되면 해당 Update 객체도 종료되어야 하는 생명주기를 가집니다. 그러나 `wait()` 함수는 코드의 실행을 즉시 중단시키고, 지정된 시간만큼 대기를 유지하는 비동기적인 동작을 수행합니다. 이로 인해 다음과 같은 과정이 반복됩니다. 1. **현재 프레임의 [scores] | [judge_reason] | [source_doc] | 그럼 `OnUpdate()`에서 사용할 경우를 살펴보겠습니다. `OnUpdate()` 문에서 아래 코드는 어떻게 동작할까요? ```lua log("TEST") -- No.1 wait(1) log("TEST2") -- No.2 ``` * 기대 동작: TEST - 1초 대기 - TEST2 - TEST1 - 1초 대기 - TEST2 * 실제 동작: **TEST - TEST - TEST - .... (첫 번째 TEST가 찍힌 시점부터 1초가 지났을 때 ) - TEST2** `OnUpdate()`는 논리적인 측면에서 수행이 끝날 당시