Learning
레슨 8 / 9·3개 토픽

커스텀 블록과 직렬화

커스텀 블록 정의 — defineBlocksWithJsonArray

Blockly에서 커스텀 블록을 정의하는 가장 간결한 방법은 Blockly.defineBlocksWithJsonArray()를 사용하는 것입니다. JSON 배열로 여러 블록을 한 번에 정의할 수 있으며, 블록의 외관(입력, 필드, 색상, 연결부)을 선언적으로 지정합니다.

javascript
// JSON 배열로 커스텀 블록 한 번에 정의
Blockly.defineBlocksWithJsonArray([
  // 블록 1: 인사 메시지 블록
  {
    type: 'greeting_block',
    message0: '%1 에게 인사하기',
    args0: [
      {
        type: 'field_input',
        name: 'NAME',
        text: '홍길동',
      },
    ],
    previousStatement: null,
    nextStatement: null,
    colour: 160,
    tooltip: '지정한 이름으로 인사 메시지를 출력합니다',
  },
  // 블록 2: 반복 실행 블록 (값 입력 포함)
  {
    type: 'repeat_action',
    message0: '%1 번 반복하여 %2 실행',
    args0: [
      {
        type: 'input_value',
        name: 'COUNT',
        check: 'Number',
      },
      {
        type: 'input_statement',
        name: 'ACTION',
      },
    ],
    previousStatement: null,
    nextStatement: null,
    colour: 120,
    tooltip: '지정한 횟수만큼 내부 블록을 반복 실행합니다',
  },
  // 블록 3: 값을 반환하는 블록
  {
    type: 'random_color',
    message0: '랜덤 색상',
    output: 'Colour',
    colour: 20,
    tooltip: '랜덤한 색상 값을 반환합니다',
  },
]);

코드 생성기 — javascriptGenerator.forBlock

커스텀 블록을 정의한 후에는 해당 블록이 어떤 코드를 생성할지 지정해야 합니다. javascriptGenerator.forBlock 객체에 블록 타입을 키로 하여 생성 함수를 등록합니다. 생성 함수는 블록의 필드 값과 입력을 읽어 코드 문자열을 반환합니다.

javascript
import { javascriptGenerator, Order } from 'blockly/javascript';

// greeting_block의 코드 생성기
javascriptGenerator.forBlock['greeting_block'] = function (block) {
  var name = block.getFieldValue('NAME');
  return 'window.alert("안녕하세요, ' + name + '님!");
';
};

// repeat_action의 코드 생성기
javascriptGenerator.forBlock['repeat_action'] = function (block) {
  var count = javascriptGenerator.valueToCode(
    block, 'COUNT', Order.ATOMIC
  ) || '0';
  var action = javascriptGenerator.statementToCode(block, 'ACTION');
  return (
    'for (var i = 0; i < ' + count + '; i++) {
' +
    action +
    '}
'
  );
};

// random_color의 코드 생성기 (값 블록 — 배열 반환)
javascriptGenerator.forBlock['random_color'] = function (block) {
  var code =
    '"#" + Math.floor(Math.random() * 16777215).toString(16).padStart(6, "0")';
  return [code, Order.FUNCTION_CALL];
};

// 워크스페이스에서 코드 생성
// var code = javascriptGenerator.workspaceToCode(workspace);

워크스페이스 직렬화 — save / load

Blockly.serialization.workspaces.save()와 load()를 사용하면 워크스페이스의 모든 블록 상태를 JSON 객체로 저장하고 복원할 수 있습니다. localStorage, 서버, 파일 등 다양한 저장소에 활용 가능합니다. 이 JSON 직렬화 방식은 레거시 XML 방식을 대체하는 권장 API입니다.

javascript
// ── 워크스페이스 저장 ──
var state = Blockly.serialization.workspaces.save(workspace);
// state는 JSON 객체 — blocks, variables 등 포함

// localStorage에 저장
localStorage.setItem('myBlocklyState', JSON.stringify(state));

// ── 워크스페이스 불러오기 ──
var savedJson = localStorage.getItem('myBlocklyState');
if (savedJson) {
  var state = JSON.parse(savedJson);
  Blockly.serialization.workspaces.load(state, workspace);
}

// ── JSON 직렬화 형식 예시 ──
// save()가 반환하는 JSON 구조:
// {
//   "blocks": {
//     "languageVersion": 0,
//     "blocks": [
//       {
//         "type": "greeting_block",
//         "id": "abc123",
//         "x": 50,
//         "y": 30,
//         "fields": { "NAME": "홍길동" },
//         "next": {
//           "block": {
//             "type": "repeat_action",
//             "id": "def456",
//             "inputs": {
//               "COUNT": { "block": { "type": "math_number", ... } },
//               "ACTION": { "block": { ... } }
//             }
//           }
//         }
//       }
//     ]
//   },
//   "variables": [
//     { "name": "myVar", "id": "var_001" }
//   ]
// }
  • defineBlocksWithJsonArray() — JSON 배열로 여러 커스텀 블록을 한 번에 정의
  • javascriptGenerator.forBlock[type] — 블록 타입별 코드 생성 함수 등록
  • valueToCode() — 값 입력에 연결된 블록의 코드를 문자열로 반환
  • statementToCode() — 문장 입력에 연결된 블록들의 코드를 문자열로 반환
  • serialization.workspaces.save() — 워크스페이스를 JSON 객체로 저장
  • serialization.workspaces.load() — JSON 객체에서 워크스페이스 복원
  • Order — 코드 생성 시 연산자 우선순위 지정 (ATOMIC, FUNCTION_CALL 등)
💡

값을 반환하는 블록(output이 있는 블록)의 코드 생성기는 [code, order] 배열을 반환해야 합니다. order는 연산자 우선순위로, 괄호 삽입 여부를 결정합니다. 문장 블록은 코드 문자열만 반환하면 됩니다.