블록 정의 수정

기존 블록의 정의를 수정하는 방법이 자주 질문됩니다. 예를 들어 연결 확인을 추가하거나 필드를 값 입력으로 변경할 수 있습니다.

일반적으로 블록 정의를 수정할 수 없습니다.

기존 정의를 수정하는 방법

기존 블록 유형의 정의를 수정하려면 다음 단계를 따르세요.

  1. 블록 코드 생성기를 포함하여 기존 정의의 사본을 만듭니다.
  2. 사본을 수정하고 유형에 새 이름을 지정합니다.
  3. 새 정의를 Blockly.Blocks에 추가합니다.

기존 정의를 직접 수정할 수 없는 이유는 무엇인가요?

기존 정의를 수정할 수 없는 이유가 궁금하다면 계속 읽어보세요. 몇 가지 가능성을 고려해 보겠습니다.

기존 정의를 직접 수정

기존 블록의 정의를 직접 수정하는 방법에는 두 가지가 있습니다. 하나는 원숭이 패치이고 다른 하나는 코드 포크입니다. 둘 다 권장하지 않습니다. 원숭이 패치 또는 포크된 코드에 종속된 코드가 손상될 위험이 있기 때문입니다. 또한 두 기술 모두 업데이트와 버그 수정을 통합하기가 어렵습니다. 자세한 내용은 몬키 패칭Blockly 포크를 참고하세요.

기존 정의의 서브클래스 만들기

기존 정의를 어떻게든 서브클래스화하는 방법을 생각해 볼 수 있습니다. 안타깝게도 블록 정의는 클래스가 아니라 믹스인이므로 불가능합니다. 예를 들어 정의에 색상 속성이 없으므로 색상 속성을 덮어쓸 방법이 없습니다. 대신 setColour를 호출하여 블록의 색상 속성을 설정하는 init 함수가 있습니다. 호출이 init 내에 있으므로 전체 init 함수를 대체하지 않고는 이를 대체할 방법이 없습니다.

기존 정의에서 함수 덮어쓰기

기존 정의에서 함수를 덮어쓸 수 있습니다.

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

이렇게 하면 작동하지만 기존 함수를 복사하고 수정해야 합니다. 한 줄만 마술처럼 대체할 수는 없습니다. 이 방법에는 몇 가지 문제가 있습니다.

  • 전체 정의를 복사하고 수정하는 것과 크게 다르지 않습니다.
  • Blockly의 기본 제공 블록과 같이 JSON에 정의된 블록의 init 함수를 수정하는 데는 사용할 수 없습니다. 복사할 init 함수가 없기 때문입니다. init 함수는 런타임에 생성됩니다.
  • JavaScript를 사용하여 블록을 정의해야 하므로 현지화 문제가 발생할 수 있습니다.

init 결과 덮어쓰기

init 함수의 '한 줄만 바꾸는' 한 가지 방법은 init 함수를 원래 init 함수를 호출한 다음 해당 호출의 결과를 덮어쓰는 함수로 바꾸는 것입니다. 예를 들어 다음 코드는 logic_null 블록의 색상을 변경합니다.

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

하지만 이 방법은 생각보다 유용하지 않습니다. 예를 들면 다음과 같습니다.

  • 연결 검사를 확장하거나 제약 조건이 덜한 필드 검사기를 적용하면 블록 코드 생성기와 이벤트 핸들러가 만든 가정이 무효화될 수 있습니다.

  • 필드를 값 입력으로 대체하면 블록 코드 생성기와 필드 검사기가 손상되고 이벤트 핸들러가 손상될 수 있습니다. 또한 현지화된 블록의 경우 입력란과 필드의 유형 및 순서가 다를 수 있으므로 이를 실행하기가 매우 어려울 수 있습니다.

JSON 정의에서 키-값 쌍 덮어쓰기

블록의 JSON을 공개적으로 사용할 수 있는 경우 개별 JSON 값을 덮어쓸 수 있습니다. 예를 들면 다음과 같습니다.

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

그러나 이는 JSON 객체가 공개적으로 제공되고 정의에서 init 함수를 명시적으로 정의하며 init 함수가 initJson를 호출하는 경우에만 작동합니다. 서드 파티에서 JSON을 수정할 기회가 있기 전에 JSON이 처리되므로 JSON이 defineBlocksWithJsonArray 또는 createBlockDefinitionsFromJsonArray에 전달되는 경우에는 작동하지 않습니다. Blockly의 기본 제공 블록은 createBlockDefinitionsFromJsonArray를 사용합니다.

하지만 JSON이 이렇게 정의되지 않은 경우는 어떻게 해야 하나요? JSON 속성을 덮어쓸 수 있어야 하지 않나요? 안타깝게도 그렇지 않습니다. 정의에는 JSON이 아닌 init 함수가 포함되어 있으며 init 함수를 JSON으로 변환하는 함수는 없습니다.

// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
  initJson(json);
};

재사용을 고려한 설계

맞춤 블록을 설계할 때 재사용을 촉진하는 방식으로 설계할 수 있습니다.

JSON 재사용

대략적으로 유사한 두 개의 블록이 있는 경우 상위 JSON 정의를 만들고 하위 정의에서 이를 재사용할 수 있습니다. 예를 들면 다음과 같습니다.

const parentJson = {
  // shared properties
};

Blockly.Blocks['child_block_1'] = {
  init: function() {
    initJson({...parentJson, colour: 100})
  }
}

Blockly.Blocks['child_block_2'] = {
  init: function() {
    initJson({...parentJson, colour: 200})
  }
}

또 다른 방법은 공개적으로 사용 가능한 객체에서 JSON을 정의하고 init 함수에서 해당 객체를 initJson에 전달하는 것입니다. 이렇게 하면 다른 사용자가 개별 속성을 덮어쓸 수 있습니다. 자세한 내용은 JSON 정의에서 키-값 쌍 덮어쓰기를 참고하세요.

함수 재사용

블록은 블록 수준 이벤트 핸들러, 맞춤 도움말, 필드 검사기, 뮤테이터에서 사용하는 함수와 같은 여러 표준 함수를 정의할 수 있으며, 로봇 팔의 현재 위치와 같은 외부 데이터에서 필드 값을 설정하는 함수와 같은 맞춤 동작을 제공하는 함수도 정의할 수 있습니다.

이러한 함수는 여러 블록에서 재사용할 수 있습니다.

드롭다운 필드 사용

연산자를 제외하고 거의 동일한 블록 세트가 있는 경우 연산자를 위한 드롭다운 필드가 있는 단일 블록을 설계할 수 있습니다. 예를 들면 다음과 같습니다.

  • 내장 logic_operation 블록은 andor 연산자와 함께 드롭다운을 사용합니다.
  • 기본 제공 math_arithmetic 블록은 +, -, ×, ÷, ^ 연산자가 있는 드롭다운을 사용합니다.

이러한 블록의 코드 생성기를 작성하는 것은 일반적으로 약간 더 복잡하지만 여러 블록을 작성하고 유지하는 것보다 쉽습니다.

뮤테이터 사용

동일한 프로그래밍 구조의 다양한 변형을 나타내는 블록 세트가 있는 경우 변경자를 사용하는 단일 블록을 만들 수 있습니다. 예를 들어 내장 controls_if 블록은 if-then-else 문이 여러 가지로 변형된 것을 나타낼 수 있습니다.