기존 블록의 정의를 수정하는 방법이 자주 질문됩니다. 예를 들어 연결 확인을 추가하거나 필드를 값 입력으로 변경할 수 있습니다.
일반적으로 블록 정의를 수정할 수 없습니다.
기존 정의를 수정하는 방법
기존 블록 유형의 정의를 수정하려면 다음 단계를 따르세요.
- 블록 코드 생성기를 포함하여 기존 정의의 사본을 만듭니다.
- 사본을 수정하고 유형에 새 이름을 지정합니다.
- 새 정의를
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
블록은and
및or
연산자와 함께 드롭다운을 사용합니다. - 기본 제공
math_arithmetic
블록은+
,-
,×
,÷
,^
연산자가 있는 드롭다운을 사용합니다.
이러한 블록의 코드 생성기를 작성하는 것은 일반적으로 약간 더 복잡하지만 여러 블록을 작성하고 유지하는 것보다 쉽습니다.
뮤테이터 사용
동일한 프로그래밍 구조의 다양한 변형을 나타내는 블록 세트가 있는 경우 변경자를 사용하는 단일 블록을 만들 수 있습니다. 예를 들어 내장 controls_if
블록은 if-then-else
문이 여러 가지로 변형된 것을 나타낼 수 있습니다.