[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | OnMonsterDestroy 이벤트 핸들러 내에서 _TimerService:SetTimer를 호출할 때 self를 첫 번째 인자로 전달하는 이유는 무엇인가요? [answer] | `_TimerService:SetTimer` 호출 시 `self`를 첫 번째 인자로 전달하는 이유는, 타이머가 만료되었을 때 실행될 콜백 함수가 **해당 스크립트 컴포넌트(`self`)의 컨텍스트**에서 실행되어야 하기 때문입니다. 메이플스토리 월드의 mLua에서는 서비스 메서드가 특정 엔티티나 컴포넌트에 대해 동작할 때, 해당 대상이 누구인지 명시적으로 전달해야 합니다. `SetTimer`의 첫 번째 인자는 타이머가 만료된 후 콜백이 연결될 대상(Owner)을 지정하는 역할을 합니다. * **컨텍스트 유지**: `self` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 몬스터가 등장하고 몇 초 뒤 공격을 시작하거나, 몬스터가 죽고 1초 뒤 아이템을 떨어뜨리는 것과 같은 어떤 일(액션)이 일어나는 타이밍을 제어해야 할 때가 있습니다. TimerService를 활용해 쉽고 효율적으로 액션이 일어나는 타이밍을 제어할 수 있습니다. TimerService가 제공하는 기능과 활용법을 알아보겠습니다. # TimerService 소개 **TimerService**는 원하는 순간에 어떤 액션을 실행하도록 예약하는 편의 기능을 제공합니다. Timer Service를 사용하지 않고 몬스터가
[task] | two_turn [pass] | ⏳ 미판정 [title] | 캐릭터 초기 속성 편집 [question] | 메이커에서 플레이어 캐릭터의 초기 속성을 어떻게 설정할 수 있나요? [answer] | 메이커에서 플레이어 캐릭터의 초기 속성을 설정하려면 **Player** 모델의 프로퍼티를 직접 편집해야 합니다. 이 방법은 모든 플레이어가 동일한 환경에서 게임을 시작할 수 있도록 보장합니다. 설정 절차는 다음과 같습니다. 1. **Workspace**에서 **BaseEnvironment - NativeModel - Player** 경로를 따라 **Player** 모델을 선택합니다. 2. 선택된 **Player** 모델의 프로퍼티 값을 원하는 값으로 변경합니다. 3. **[시작]** 버튼을 눌러 변경 사항을 적용하고 확인합니다. [followup] | Workspace - BaseEnvironment - NativeModel - Player 경로에서 어떤 프로퍼티 값을 수정해야 하는지 구체적인 예시를 알려주세요. [followup_answer] | 제공된 참고 문서에는 **Player** 모델의 구체적인 프로퍼티 목록이나 수정 예시(예: 이동 속도 점프 높이 등)가 포함되어 있지 않습니다. 문서는 "Player 모델에서 기본 속성을 변경할 수 있다"는 사실과, "여러 프로퍼티를 참조할 경우 정확한 구분을 위해 모델 프로퍼티 명칭이 변경된 경우가 있다"는 점만을 언급하고 있을 뿐, 실제로 어떤 프로퍼티 값을 어떻게 수정해야 하는지에 대한 구체적인 정보는 제공하지 않습니다. 따라서 해당 문서만으로는 구체적인 프로퍼티 수정 예시를 알려드리기 어렵습니다. [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 동시에 출발해 목표 지점까지 오르는 게임을 시작할 때 누군가는 한 번에 4칸을 오르고, 다른 누구는 2칸을 오른다면 함께 게임을 즐길 수 없을 것입니다. 게임을 처음 시작할 때 만큼은 동일한 환경에서 시작하는 게 중요합니다. 모델 프로퍼티를 이용해 속성을 편집하는 방법을 알아봅니다. # 플레이어 기본 속성 변경 **Player** 모델에서 기본 속성을 변경할 수 있습니다. 1. **Workspace - BaseEnvironment - NativeModel - Player**를 선택합니다. 2. **Play
[task] | code [pass] | ⏳ 미판정 [title] | EntryService [question] | EntryService:GetModelIdByName("Monster") 로 Model Id를 조회하여 _SpawnService:SpawnByModelId 로 self.Entity 를 parent 로 하는 "SpawnedMonster" 엔티티를 생성하고 생성된 엔티티의 TransformComponent.Position 을 Vector3(1, 0, 1) 로 설정하는 스크립트를 작성해줘 [answer] | EntryService의 `GetModelIdByName` 메서드를 사용해 "Monster" 이름의 Model Id를 조회하고, 조회된 Id가 존재하는지 확인한 뒤 `SpawnByModelId`로 엔티티를 스폰합니다. 생성된 엔티티의 `TransformComponent.Position`을 `Vector3(1, 0, 1)`로 설정하는 로직은 스폰 직후 바로 실행할 수 있으며, mLua의 표준 생명주기 메서드인 `OnBeginPlay` 안에서 수행하도록 작성했습니다. ```lua Method: [server only] void OnB [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 엔트리 관련 기능을 제공합니다. # Methods - **string GetDataSetIdByName(string name)** DataSet 이름으로 DataSet Id를 찾아 반환합니다. - **string GetMaterialIdByName(string name)** Material 이름으로 Material Id를 찾아 반환합니다. - **string GetModelIdByName(string name)** Model 이름으로 Modle Id를 찾아 반환합니다. ##### inherited from Servi
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | Tip 섹션에 언급된 46초~54초 로그 출력 구간이 형성되는 시간적 근거를 `SetTimer()` 및 `ClearTimer()` 실행 시점을 중심으로 3문장 이내로 서술하세요. [answer] | `SetTimer()` 실행 후 2초 뒤에 첫 번째 CallBack이 실행되며 3초 간격으로 반복됩니다. 10초 후 `ClearTimer()`가 실행되어 다음 반복이 예약된 11초 시점의 실행이 취소됩니다. 따라서 10초까지 실행된 CallBack 로그만 출력되어 총 8초(0, 3, 6, 9초 시점) 동안 로그가 생성됩니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimer()`를 통해 특정 액션을 수행하도록 예약을 만들었지만, 특정 조건에 의해 해당 예약을 취소해야 하는 경우가 빈번히 발생합니다. 게임 내에서 스위치를 On 했을 때 3초 후 문이 열리는 장치를 만들었다고 가정해 봅시다. 유저가 스위치를 On 상태로 만들었을 때 3초 뒤에 문이 열리게 됩니다. 문이 열리는 액션을 `SetTimer()`를 이용해 예약했기 때문입니다. 그러나 유저가 문이 열리기 전에 스위치를 Off 상태로 만든다면 `SetTimer()`의 예약을 취소해 Off일 때 문이 열리지 않게 만들어야 합니다.
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | `SetTimerOnce()`와 `SetTimerRepeat()`의 기능적 차이점을 비교하는 표를 생성하되, '사용 목적'과 '매개변수 복잡도' 두 개의 컬럼만 포함해야 합니다. [answer] | 요청하신 조건에 따라 `SetTimerOnce()`와 `SetTimerRepeat()`의 기능적 차이점을 비교한 표입니다. | 사용 목적 | 매개변수 복잡도 | | :--- | :--- | | 1회 액션 예약 시 사용 | `SetTimer()`에 비해 전달할 매개 변수 개수가 적어 간편하게 사용 | | 반복 액션 수행 시 사용 | `SetTimer()`에 비해 전달할 매개 변수 개수가 적어 간편하게 사용 | [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimer()`를 통해 특정 액션을 수행하도록 예약을 만들었지만, 특정 조건에 의해 해당 예약을 취소해야 하는 경우가 빈번히 발생합니다. 게임 내에서 스위치를 On 했을 때 3초 후 문이 열리는 장치를 만들었다고 가정해 봅시다. 유저가 스위치를 On 상태로 만들었을 때 3초 뒤에 문이 열리게 됩니다. 문이 열리는 액션을 `SetTimer()`를 이용해 예약했기 때문입니다. 그러나 유저가 문이 열리기 전에 스위치를 Off 상태로 만든다면 `SetTimer()`의 예약을 취소해 Off일 때 문이 열리지 않게 만들어야 합니다.
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | 단순히 5초 뒤에 함수를 한 번만 실행하고 싶을 때, SetTimer를 사용할 때 매개변수를 어떻게 설정해야 하나요? [answer] | 5초 뒤에 함수를 한 번만 실행하려면 `SetTimer()`의 매개변수를 다음과 같이 설정해야 합니다. 1. **scriptable**: `self` (타이머를 관리할 컴포넌트 자신) 2. **callback**: 실행할 함수 3. **intervalSeconds**: `5` (3초가 아닌 5초 후 실행) 4. **isRepeat**: `false` (반복 없이 1회만 실행) 5. **startDelaySeconds**: 무시 (1회 실행 시에는 사용되지 않음) 코드 예시는 다음과 같습니다. ```lua Method: [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | code [pass] | ⏳ 미판정 [title] | MaplePreferencesLogic [question] | _LogicService 와 _MaplePreferencesLogic 을 사용하여 OnBeginPlay 시 특정 키 값의 이벤트를 연결하고, 이벤트 발생 시 IsClient() 로 실행 환경을 검사하여 클라이언트일 경우에만 로그를 출력하는 스크립트를 작성해줘 [answer] | `_LogicService`라는 서비스는 제공된 참고 문서에 포함되어 있지 않습니다. 문서에는 `MaplePreferencesLogic`에서 상속된 `Logic` 클래스의 메서드(`ConnectEvent`, `DisconnectEvent`, `IsClient` 등)만 설명되어 있습니다. 따라서 요청하신 `_LogicService`를 사용한 코드를 작성할 수 없으며, 존재하지 않는 API를 지어낼 수 없습니다. 다만, 문서에 명시된 `MaplePreferencesLogic`의 `ConnectEvent`와 `IsClient` 메서드 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ##### inherited from Logic: - **EventHandlerBase ConnectEvent(string key, IScriptFunction eventHandler)** 이벤트를 연결합니다. 이 서비스에서 타입의 이름이 key인 이벤트가 발생하면, 핸들러로 통지받습니다. 핸들러는 event 매개 변수를 전달받습니다. - **EventHandlerBase ConnectEvent(Type eventType, IScriptFunction eventHandler)** 이벤트를 연결합니다. 이 서비스에서 타입이
[task] | single_turn [pass] | ⏳ 미판정 [title] | 엔티티에 이름표 붙이기 [question] | 동일한 스프라이트의 몬스터 여러 마리를 배치할 때, 특정 개체만 NPC처럼 보이게 하려면 어떤 컴포넌트를 어떻게 적용해야 하나요? [answer] | 동일한 스프라이트의 몬스터나 오브젝트 여러 개를 배치할 때 특정 개체만 NPC처럼 보이게 하려면 **NameTagComponent**를 적용합니다. NameTagComponent는 엔티티에 이름표를 붙여 사물을 구분하는 데 사용되며, 동일한 스프라이트를 가진 개체 중 일부에만 이름표를 달아 "대장"이나 "NPC"처럼 연출할 수 있습니다. ### 적용 방법 1. **엔티티 선택**: 이름을 붙이고 싶은 특정 몬스터 또는 오브젝트 엔티티를 선택합니다. 2. **컴포넌트 추가**: 해당 엔티티에 **NameTagComponent [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 사물을 구분하는 효과적인 방법은 이름을 붙이는 것입니다. 이름표를 이용해 오브젝트와 NPC를 구분하거나, 유저를 구분할 수 있습니다. NameTagComponent 사용 방법과 활용 예시를 소개합니다. # 이름표 붙이기 엔티티를 선택하고 **NameTagComponent**를 추가합니다. 프로퍼티 값을 변경해 원하는 이름표를 만듭니다. NameTagComponent 프로퍼티 구성은 다음과 같습니다. | 이름 | 설명 | | --- | --- | | FontColor | 글씨 색상을 변경합니다. 색상 변
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | SetTimer가 실패했을 때 반환되는 값은 무엇이며, 이 값을 어떻게 활용하여 오류 처리를 할 수 있나요? [answer] | `SetTimer()`는 예약이 성공하면 integer 타입의 타이머 ID를 반환하고, 예약이 실패하면 **0**을 반환합니다. 따라서 오류 처리는 반환 값을 확인하여 **0인 경우 예약에 실패한 것으로 판단**하는 방식으로 수행할 수 있습니다. 실패 시에는 추가적인 로직을 중단하거나(`return`) 다른 조치를 취할 수 있습니다. 다음은 예약 실패 시 오류를 처리하는 예시 코드입니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = functio [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | 컴포넌트 메서드를 직접 callback으로 전달했을 때 EnableInHierarchy가 false면 어떤 문제가 발생하나요? [answer] | `_TimerService:SetTimerOnce(self.Method, 1)` 또는 `_TimerService:SetTimerRepeat(self.Method, 1, 1)`처럼 컴포넌트의 메서드를 직접 callback 매개변수로 전달하는 경우, 예약된 액션의 실행 여부가 해당 컴포넌트의 `EnableInHierarchy` 상태에 영향을 받습니다. 따라서 타이머가 실행될 시점에 컴포넌트의 `EnableInHierarchy`가 `false` 상태라면, 예약되어 있던 callback이 정상적으로 실행되지 않을 수 있습니다. 이는 타이 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimerOnce()`는 `SetTimer()`의 isRepeat를 false로 설정한 것과 동일하게 작동합니다. 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | func | callback | 몇 초 뒤 수행할 액션을 CallBack 형태로 넘겨줍니다. | | float | delaySeconds | 액션을 예약한 뒤 몇 초 후에 액션을 실행할 것인지를 설정합니다.
[task] | two_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | `TimerService`의 `SetTimer()` 함수를 활용하여 몬스터 사망 후 아이템 드롭 시기를 제어하는 코드의 전체 구조와 핵심 논리는 무엇인가요? [answer] | `TimerService`의 `SetTimer()` 함수는 특정 액션(콜백 함수)이 미래의 특정 시점에 실행되도록 예약하는 역할을 합니다. 이를 통해 몬스터가 사망한 시점부터 몇 초 후가 되었는지 매 프레임마다 확인하는 번거로운 로직(`OnUpdate` 내 시간 비교)을 제거하고, 코드 구조를 간결하게 만들 수 있습니다. 몬스터 사망 후 3초 뒤 아이템이 드롭되도록 설정하는 전체 구조와 핵심 논리는 다음과 같습니다. ### 1. 핵심 논리 1. **사망 이벤트 처리**: 몬스터가 파괴되는 이벤트(`OnMonsterDestroy [followup] | `SetTimer()`의 마지막 인자 `false`가 의미하는 반복 설정과, `OnUpdate`에서 프레임별 시간 확인을 제거한 방식의 차이점을 구체적인 코드 예시로 비교해 주세요. [followup_answer] | `SetTimer()`의 마지막 인자 `false`는 해당 타이머가 **1회만 실행**되고 종료됨을 의미합니다. `true`였다면 지정된 간격으로 반복 실행되었을 것입니다. `OnUpdate`에서 프레임별 시간 확인을 제거하는 방식의 차이는, 매 프레임마다 `os.clock()`을 호출하여 현재 시각과 드롭 예정 시각을 비교하는 로직을 없애고, 엔진 레벨에서 타이머 만료 시점에 콜백을 호출하도록 위임하는 것입니다. 아래는 두 방식을 구체적으로 비교한 코드 예시입니다. ### 1. `OnUpdate` 프레임별 시간 확인 방식 ( [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 몬스터가 등장하고 몇 초 뒤 공격을 시작하거나, 몬스터가 죽고 1초 뒤 아이템을 떨어뜨리는 것과 같은 어떤 일(액션)이 일어나는 타이밍을 제어해야 할 때가 있습니다. TimerService를 활용해 쉽고 효율적으로 액션이 일어나는 타이밍을 제어할 수 있습니다. TimerService가 제공하는 기능과 활용법을 알아보겠습니다. # TimerService 소개 **TimerService**는 원하는 순간에 어떤 액션을 실행하도록 예약하는 편의 기능을 제공합니다. Timer Service를 사용하지 않고 몬스터가
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | isRepeat가 true로 설정된 경우, intervalSeconds와 startDelaySeconds 두 매개변수가 각각 어떤 역할을 하는지 설명해 주세요. [answer] | `isRepeat`가 `true`로 설정된 경우, `intervalSeconds`와 `startDelaySeconds`는 다음과 같은 역할을 합니다. * **startDelaySeconds**: 타이머가 시작된 후, 콜백 함수가 **최초 1회 실행되기까지** 대기할 시간을 설정합니다. * **intervalSeconds**: 콜백 함수가 최초 1회 실행된 **이후**, 다음 실행까지의 간격(주기)을 설정합니다. 즉, `startDelaySeconds`는 첫 실행까지의 지연 시간이고, `intervalSeconds`는 반복 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | code [pass] | ⏳ 미판정 [title] | EntryService [question] | EntryService:GetModelIdByName("Tree") 로 Model Id를 조회하여 _SpawnService:SpawnByModelId 로 "/maps/main/props" 경로에 "GeneratedTree" 이름의 엔티티를 생성하는 스크립트를 작성해줘 [answer] | EntryService의 `GetModelIdByName` 메서드를 사용하여 "Tree"라는 이름의 Model ID를 조회한 후, 해당 ID가 존재하는지 확인하여 `_SpawnService:SpawnByModelId` 메서드를 호출하는 로직입니다. 참고로 `_SpawnService:SpawnByModelId`의 시그니처는 `(modelId, name, position, parent)`이며, 마지막 매개변수인 `parent`는 엔티티가 생성될 부모 엔티티나 경로를 지정하는 데 사용됩니다. 질문에서 "/maps/main/props" [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 엔트리 관련 기능을 제공합니다. # Methods - **string GetDataSetIdByName(string name)** DataSet 이름으로 DataSet Id를 찾아 반환합니다. - **string GetMaterialIdByName(string name)** Material 이름으로 Material Id를 찾아 반환합니다. - **string GetModelIdByName(string name)** Model 이름으로 Modle Id를 찾아 반환합니다. ##### inherited from Servi
[task] | code [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | _TimerService:SetTimer(self, callBack, 1, true)를 사용하여 OnBeginPlay()에서 1초 간격으로 반복적으로 log("Tick")을 출력하는 스크립트를 작성해줘 [answer] | `_TimerService:SetTimer(self, callBack, 1, true)`를 사용하면 콜백 함수가 1초 간격으로 반복적으로 실행됩니다. 요청하신 기능을 구현한 스크립트는 다음과 같습니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = function() log("Tick") end _TimerService:SetTimer(self, callBack, 1, true) } ``` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | two_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | _TimerService:SetTimer의 isRepeat 파라미터가 true일 때와 false일 때 intervalSeconds의 의미 차이가 무엇이며, 두 경우 모두 SetTimer 호출 시점과 CallBack 최초 실행 시점의 관계는 어떻게 다른가? [answer] | `_TimerService:SetTimer`의 `isRepeat` 파라미터에 따라 `intervalSeconds`의 의미와 CallBack 함수의 최초 실행 시점이 다음과 같이 달라집니다. **1. `isRepeat`가 `false`일 때 (1회 실행)** * **`intervalSeconds`의 의미:** `SetTimer()`가 실행된 시점부터 **몇 초 뒤에** CallBack 함수를 실행할 것인지를 의미합니다. * **최초 실행 시점:** `SetTimer()` 호출 시점과 CallBack 최초 실행 시점은 다릅니다 [followup] | isRepeat이 true일 때 SetTimer 호출과 동시에 CallBack이 최초 실행되는 이유는 무엇이며, 이를 방지하고 SetTimer 호출 후 2초 뒤에 최초 실행되도록 하려면 startDelaySeconds 매개변수를 어떻게 설정해야 하는가? [followup_answer] | `isRepeat`가 `true`일 때 `SetTimer` 호출과 동시에 CallBack이 최초 실행되는 이유는 반복 실행 모드에서 `intervalSeconds`의 의미가 **CallBack 함수가 최초 실행된 후 몇 초 뒤에 다시 실행할 것인지를 정의**하기 때문입니다. 이러한 설계로 인해 `SetTimer()`가 호출되는 시점에 CallBack 함수가 즉시(동시에) 첫 번째로 실행되고, 이후 `intervalSeconds`에 지정된 시간 간격으로 반복 실행됩니다. 즉, 반복 타이머는 "예약 후 대기"가 아닌 "즉시 실행 후 [scores] | [judge_reason] | [source_doc] | isRepeat을 true로 변경해 반복 실행하게 만듭니다. `SetTimer()`가 호출될 때 "SetTimer!"라는 문자열과 함께 해당 코드가 실행된 시간을 출력합니다. 예시 코드는 다음과 같습니다. 3초마다 `SetTimer()`로 넘긴 CallBack 함수가 호출될 때 "CallBack!"이라는 문자열과 함께 코드가 실행된 시간을 출력합니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = function() log("Ca
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | 예약 실패 시 0을 반환하는 SetTimerOnce에서 0 반환을 확인하는 코드는 어떻게 작성해야 하나요? [answer] | `_TimerService:SetTimerOnce()`는 예약 성공 시 integer 타입의 타이머 id를 반환하고, 실패 시 0을 반환합니다. 따라서 반환 값을 변수에 저장한 뒤 `0`과 비교하면 예약 실패 여부를 확인할 수 있습니다. 아래는 `SetTimerOnce()` 호출 시 0이 반환되는 경우를 확인하는 mLua 코드입니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = function() log("CallBack! : ".. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimerOnce()`는 `SetTimer()`의 isRepeat를 false로 설정한 것과 동일하게 작동합니다. 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | func | callback | 몇 초 뒤 수행할 액션을 CallBack 형태로 넘겨줍니다. | | float | delaySeconds | 액션을 예약한 뒤 몇 초 후에 액션을 실행할 것인지를 설정합니다.
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | `SetTimer()` 메서드의 매개변수 중 반복 실행 여부를 결정하는 boolean 타입 파라미터의 위치와 `ClearTimer()` 호출 시 전달해야 하는 `timerId`의 원출처를 코드 주석으로만 설명하세요. [answer] | ```lua Method: [server only] void OnBeginPlay() { -- _TimerService:SetTimer(self, callBack, 3, true, 2) -- 파라미터 순서: -- 1. self (스크립트 컴포넌트 참조) -- 2. callBack (실행할 함수) -- 3. 3 (초기 지연 시간 또는 반복 주기, 문맥에 따라 의미 상이하나 여기서는 반복 주기) -- 4. true (반복 실행 여부: true이면 반복, false이면 1회) -- 5. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimer()`를 통해 특정 액션을 수행하도록 예약을 만들었지만, 특정 조건에 의해 해당 예약을 취소해야 하는 경우가 빈번히 발생합니다. 게임 내에서 스위치를 On 했을 때 3초 후 문이 열리는 장치를 만들었다고 가정해 봅시다. 유저가 스위치를 On 상태로 만들었을 때 3초 뒤에 문이 열리게 됩니다. 문이 열리는 액션을 `SetTimer()`를 이용해 예약했기 때문입니다. 그러나 유저가 문이 열리기 전에 스위치를 Off 상태로 만든다면 `SetTimer()`의 예약을 취소해 Off일 때 문이 열리지 않게 만들어야 합니다.
[task] | two_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | TimerService 에서 예약한 액션을 취소하는 방법과 ClearTimer() 함수의 역할에 대해 알려주세요. [answer] | `TimerService`에서 예약한 액션을 취소하려면 `ClearTimer()` 함수를 사용합니다. `SetTimerOnce()`나 `SetTimerRepeat()` 호출 시 반환되는 타이머 ID를 인자로 전달하면 해당 예약이 취소됩니다. `ClearTimer()` 함수의 주요 역할과 사용 시 유의사항은 다음과 같습니다. 1. **예약 취소**: 특정 타이머 ID에 해당하는 예약된 액션 실행을 중단시킵니다. 2. **성능 최적화 권장**: 반복적으로 실행되는 액션을 예약할 경우, 컴포넌트나 로직이 종료되는 시점(`OnEnd [followup] | SetTimerRepeat() 으로 만든 타이머를 10초 후 자동으로 취소하는 전체 코드 예제를 작성해 주세요. [followup_answer] | `SetTimerRepeat()`로 생성된 반복 타이머를 10초 후 자동으로 취소하는 전체 코드 예제는 다음과 같습니다. `SetTimerRepeat()` 호출 시 반환되는 `timerId`를 저장한 뒤, `SetTimerOnce()`를 이용해 10초 후에 실행될 취소 콜백을 예약합니다. 10초가 지나면 해당 콜백이 실행되어 `_TimerService:ClearTimer(timerId)`를 호출함으로써 반복 타이머가 취소됩니다. ```lua Method: [server only] void OnBeginPlay() { lo [scores] | [judge_reason] | [source_doc] | `SetTimerOnce()`와 `SetTimerRepeat()`는 예약한 액션을 취소할 수 있습니다. 예약한 액션을 취소할 때는 `ClearTimer()` 함수를 활용합니다. 아래의 예제처럼 10초 뒤 액션 실행을 취소할 수 있습니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = function() log("CallBack! :"..tostring(os.date())) end log("SetTimer! :"
[task] | code [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | _TimerService:SetTimer(self, callBack, 5, false)를 사용하여 OnBeginPlay()에서 5초 후 log("Ready!")를 출력하는 스크립트를 작성해줘 [answer] | `_TimerService:SetTimer()`를 사용하여 5초 후 특정 액션을 1회 실행하는 스크립트는 다음과 같습니다. ```lua Method: [server only] void OnBeginPlay() { local callBack = function() log("Ready!") end _TimerService:SetTimer(self, callBack, 5, false) } ``` **설명:** * `_TimerService:SetTimer(self, callBack, 5, fa [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | code [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | _TimerService:SetTimer(self, callBack, 3, false)의 반환값을 저장하고, 3초 후 log("Timer ID: " .. tostring(timerId))를 출력하는 스크립트를 작성해줘 [answer] | `_TimerService:SetTimer()` 호출 시 반환되는 `timerId`를 변수에 저장하고, 3초 뒤에 해당 ID를 출력하도록 작성한 스크립트는 다음과 같습니다. ```lua Method: [server only] void OnBeginPlay() { local timerId = _TimerService:SetTimer(self, function() -- 원래 예약된 콜백 내용 (예: 아무것도 없거나 다른 로직) end, 3, false) -- 3초 후 timerId를 출력하 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | 타이머 예약 성공 시 반환되는 id가 0이 아닌 경우, 이를 통해 타이머를 어떻게 관리할 수 있나요? [answer] | 타이머 예약이 성공하면 `integer` 타입의 고유한 id가 반환되며, 이 id를 통해 해당 타이머를 취소할 수 있습니다. 예약이 실패한 경우에만 0이 반환되므로, 반환된 id가 0이 아니면 유효한 타이머임을 의미합니다. 반환된 id를 변수에 저장한 후, `_TimerService:ClearTimer(timerId)`를 호출하면 해당 타이머의 예약이 취소됩니다. 아래 예제는 `SetTimerRepeat`으로 반복 타이머를 예약한 후, 10초 뒤에 `SetTimerOnce`를 이용해 해당 타이머를 취소하는 흐름을 보여줍니다. ` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimerOnce()`는 `SetTimer()`의 isRepeat를 false로 설정한 것과 동일하게 작동합니다. 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | func | callback | 몇 초 뒤 수행할 액션을 CallBack 형태로 넘겨줍니다. | | float | delaySeconds | 액션을 예약한 뒤 몇 초 후에 액션을 실행할 것인지를 설정합니다.
[task] | two_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | 컴포넌트를 `scriptable` 매개 변수로 전달할 때 `EnableInHierarchy` 상태가 콜백 실행에 미치는 영향과, 이 경우를 피하기 위한 일반적인 처리 방식은 무엇인가? [answer] | `scriptable` 매개 변수에 컴포넌트를 전달하는 경우, `SetTimer()`로 예약한 콜백 함수의 실행은 해당 컴포넌트의 `EnableInHierarchy` 상태에 영향을 받습니다. 즉, 컴포넌트가 비활성화 상태(`EnableInHierarchy`가 false)라면 예약된 콜백이 실행되지 않을 수 있습니다. 이 문제를 피하기 위한 일반적인 처리 방식은, `scriptable` 매개 변수에 컴포넌트 자신을(`self`) 전달하는 대신, 컴포넌트의 활성화 상태와 무관하게 실행이 보장되는 다른 객체(예: 로직 객체 등)를 전달 [followup] | `EnableInHierarchy`가 false인 상태에서 `SetTimer`가 예약되어 있다가 컴포넌트가 활성화되면 예약된 콜백은 즉시 실행되는지, 아니면 원래 예약된 시간 기준으로 실행되는지 구체적인 시나리오를 알려줘. [followup_answer] | 제공된 참고 문서에는 `EnableInHierarchy`가 false인 상태에서 `SetTimer`가 예약되어 있다가 컴포넌트가 활성화될 때 콜백이 즉시 실행되는지, 아니면 원래 예약된 시간 기준으로 실행되는지에 대한 구체적인 시나리오나 동작 방식이 설명되어 있지 않습니다. 문서에는 "`scriptable` 매개 변수에 컴포넌트를 전달하는 경우 callback 실행이 컴포넌트의 EnableInHierarchy에 영향을 받습니다"라는 주의사항만 명시되어 있을 뿐, 비활성화 상태에서의 예약 처리 로직이나 재활성화 시점의 콜백 실행 타 [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | single_turn [pass] | ⏳ 미판정 [title] | MaterialService [question] | 예제 코드에서 WorldToScreenPosition으로 변환한 좌표를 ScreenWidth와 ScreenHeight로 나눠주는 과정이 필수적인 이유와, 이 단계를 생략하면 어떤 시각적 오류가 발생하나요? [answer] | `_MaterialService:ChangeMaterialProperty`로 머티리얼 프로퍼티를 변경할 때, 셰이더 내부에서 사용하는 좌표계는 일반적으로 **0.0~1.0 사이의 정규화(Normalized)된 화면 좌표**입니다. 예제 코드에서 `WorldToScreenPosition`으로 변환한 좌표를 `ScreenWidth`와 `ScreenHeight`로 나누어주는 과정은 **화면 픽셀 좌표를 0~1 범위의 정규화 좌표로 변환**하기 위함입니다. ### 필수적인 이유 1. **좌표계 매칭**: `WorldToScreenPo [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 머티리얼 프로퍼티를 제어하는 기능을 제공합니다. # Methods - **void ChangeMaterialProperty(string entryId, Dictionary<string, any> changeProperty) [ClientOnly]** entryId에 해당하는 머티리얼의 프로퍼티를 변경합니다. changeProperty의 key는 프로퍼티 이름, value는 프로퍼티 값입니다. ##### inherited from Service: - **EventHandlerBase ConnectEvent(string ke
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 캐릭터 초기 속성 편집 [question] | Player 모델 프로퍼티 편집 시 모델 프로퍼티 명칭 변경의 이유를 1문장으로 답변하세요. [answer] | 여러 프로퍼티를 참조할 경우 정확한 구분을 위해 모델 프로퍼티 명칭이 변경된 경우가 있습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 동시에 출발해 목표 지점까지 오르는 게임을 시작할 때 누군가는 한 번에 4칸을 오르고, 다른 누구는 2칸을 오른다면 함께 게임을 즐길 수 없을 것입니다. 게임을 처음 시작할 때 만큼은 동일한 환경에서 시작하는 게 중요합니다. 모델 프로퍼티를 이용해 속성을 편집하는 방법을 알아봅니다. # 플레이어 기본 속성 변경 **Player** 모델에서 기본 속성을 변경할 수 있습니다. 1. **Workspace - BaseEnvironment - NativeModel - Player**를 선택합니다. 2. **Play
[task] | single_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | TimerService의 SetTimer 메서드에서 self 인자를 반드시 전달해야 하는 이유가 무엇인가요? [answer] | 제공된 참고 문서에는 `_TimerService:SetTimer()` 메서드의 첫 번째 인자로 `self`를 전달하는 이유에 대한 설명이 포함되어 있지 않습니다. 문서에는 `SetTimer()`, `SetTimerOnce()`, `SetTimerRepeat()` 메서드의 사용 예제와 `ClearTimer()`를 이용한 예약 취소 방법, 그리고 반복 실행 시 `timerId`를 저장하여 `OnEndPlay()`에서 해제하는 패턴이 제시되어 있지만, `self` 인자의 기술적 필요성이나 생명주기 관리와의 연관성에 대한 명시적인 근거는 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `SetTimer()`를 통해 특정 액션을 수행하도록 예약을 만들었지만, 특정 조건에 의해 해당 예약을 취소해야 하는 경우가 빈번히 발생합니다. 게임 내에서 스위치를 On 했을 때 3초 후 문이 열리는 장치를 만들었다고 가정해 봅시다. 유저가 스위치를 On 상태로 만들었을 때 3초 뒤에 문이 열리게 됩니다. 문이 열리는 액션을 `SetTimer()`를 이용해 예약했기 때문입니다. 그러나 유저가 문이 열리기 전에 스위치를 Off 상태로 만든다면 `SetTimer()`의 예약을 취소해 Off일 때 문이 열리지 않게 만들어야 합니다.
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 캐릭터 초기 속성 편집 [question] | 메이커 사용 캐릭터 초기 속성 편집의 핵심 목적이 되는 게임 환경의 특징을 2문장 이내로 설명하세요. [answer] | 동시에 출발해 목표 지점까지 도달하는 게임에서는 모든 플레이어가 동일한 환경에서 시작해야 함께 즐길 수 있습니다. 이를 위해 Player 모델의 기본 속성을 편집하여 출발 시점의 조건을 통일하는 것이 핵심입니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 동시에 출발해 목표 지점까지 오르는 게임을 시작할 때 누군가는 한 번에 4칸을 오르고, 다른 누구는 2칸을 오른다면 함께 게임을 즐길 수 없을 것입니다. 게임을 처음 시작할 때 만큼은 동일한 환경에서 시작하는 게 중요합니다. 모델 프로퍼티를 이용해 속성을 편집하는 방법을 알아봅니다. # 플레이어 기본 속성 변경 **Player** 모델에서 기본 속성을 변경할 수 있습니다. 1. **Workspace - BaseEnvironment - NativeModel - Player**를 선택합니다. 2. **Play
[task] | two_turn [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | `SetTimer` 예약 시 `intervalSeconds`와 `startDelaySeconds` 매개 변수의 역할은 `isRepeat` 값에 따라 어떻게 달라지는가? [answer] | `SetTimer`의 `intervalSeconds`와 `startDelaySeconds` 매개 변수는 `isRepeat` 값에 따라 다음과 같이 역할이 달라집니다. **1. `isRepeat` 값이 `false`인 경우 (1회 실행)** * **`intervalSeconds`**: `SetTimer()`가 호출된 후 **몇 초 뒤에** CallBack 함수를 1회 실행할지를 설정합니다. * **`startDelaySeconds`**: 무시됩니다. **2. `isRepeat` 값이 `true`인 경우 (반복 실행)** * [followup] | `isRepeat`이 true일 때 `startDelaySeconds`와 `intervalSeconds`를 각각 2초와 5초로 설정하면 콜백 함수는 언제 처음 실행되고 이후 어떤 간격으로 반복되는가? [followup_answer] | `isRepeat`이 `true`일 때 `startDelaySeconds`를 2초, `intervalSeconds`를 5초로 설정하면 다음과 같습니다. 1. **처음 실행 시점**: `startDelaySeconds` 값에 따라 함수 실행 후 **2초 뒤**에 콜백 함수가 처음 실행됩니다. 2. **반복 간격**: `intervalSeconds` 값에 따라 콜백 함수를 최초 1회 실행한 후 **5초 간격**으로 재실행됩니다. 즉, 타이머가 시작된 후 2초에 1회 실행되고, 이후 7초, 12초, 17초...와 같이 5초마다 반 [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | code [pass] | ⏳ 미판정 [title] | 액션을 예약하고 실행하는 TimerService [question] | _TimerService:SetTimer(self, callBack, 2, true, 1)를 사용하여 OnBeginPlay()에서 1초 대기 후 2초 간격으로 log("Repeat Start")을 출력하는 스크립트를 작성해줘 [answer] | `_TimerService:SetTimer(self, callBack, 2, true, 1)`의 매개 변수 의미를 기준으로 코드를 구성합니다. * `self`: 타이머의 소유자(현재 컴포넌트) * `callBack`: 실행할 함수 (`log("Repeat Start")`) * `2`: 반복 주기(`intervalSeconds`)로, 첫 실행 후 2초 간격으로 다시 실행됩니다. * `true`: 반복 실행 여부(`isRepeat`) * `1`: 초기 지연 시간(`startDelaySeconds`)으로, 타이머 시작 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # SetTimer 활용 `SetTimer()`는 예약 함수입니다. 특정 시점에 수행할 액션을 예약할 수 있고, 액션 반복 수행 여부와 반복 주기를 설정할 수 있습니다. 예약한 액션 반복 여부에 따라 매개 변수의 의미와 쓰임새가 달라집니다. `SetTimer()`는 예약 성공 시 integer 타입의 타이머의 id를 반환하고, 예약 실패 시 0을 반환합니다. 매개 변수는 다음과 같습니다. | 타입 | 매개 변수 | 설명 | | --- | ------ | --- | | IScriptable | scriptable | 예약할 액션의
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 캐릭터 초기 속성 편집 [question] | Player 모델에서 기본 속성을 변경하기 위한 워크스페이스 경로와 확인 단계를 3단계 번호 목록으로 작성하세요. [answer] | Player 모델에서 기본 속성을 변경하기 위한 워크스페이스 경로와 확인 단계는 다음과 같습니다. 1. **Workspace - BaseEnvironment - NativeModel - Player**를 선택합니다. 2. **Player**의 프로퍼티 값을 변경합니다. 3. **[시작]** 버튼을 눌러 변경 사항을 확인합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 동시에 출발해 목표 지점까지 오르는 게임을 시작할 때 누군가는 한 번에 4칸을 오르고, 다른 누구는 2칸을 오른다면 함께 게임을 즐길 수 없을 것입니다. 게임을 처음 시작할 때 만큼은 동일한 환경에서 시작하는 게 중요합니다. 모델 프로퍼티를 이용해 속성을 편집하는 방법을 알아봅니다. # 플레이어 기본 속성 변경 **Player** 모델에서 기본 속성을 변경할 수 있습니다. 1. **Workspace - BaseEnvironment - NativeModel - Player**를 선택합니다. 2. **Play
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 캐릭터 초기 속성 편집 [question] | NameTagComponent의 Enable 체크를 해제하는 방법을 1문장으로 설명하세요. [answer] | 프로퍼티 에디터에서 NameTagComponent의 Enable 체크를 해제하면 됩니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 모델 프로퍼티 비활성화하기 크리에이터의 의도에 따라 필요 없는 모델 프로퍼티를 비활성화 할 수 있습니다. 예를 들어, **NameTag**을 사용하고 싶지 않다면, 프로퍼티 에디터에서 **NameTagComponent**의 **Enable** 체크를 해제합니다. # DefaultPlayer의 기본 컴포넌트 삭제하기 기본 컴포넌트를 확장한 컴포넌트를 제작해 사용한다면, **DefaultPlayer**의 기본 컴포넌트를 삭제해야 합니다. 근본이 같은 컴포넌트가 여러 개 있으면 오동작 가능성이 커지기 때문에 기본적으로 하나의 컴