블록코딩 라이브러리 Blockly 사용법

· by 박승재

Blockly는 Google에서 만든 블록코딩 라이브러리로 웹페이지에 블록코딩으로 코드를 생성하는 에디터를 제공합니다.

Blockly를 통해 Scratch와 같은 블록코딩 환경을 웹사이트에서 쉽게 구축할 수 있습니다.

과거에는 웹(자바스크립트) 이외의 안드로이드와 iOS와 같은 환경에서 라이브러리를 사용할 수 있었지만 Deprecated되어 현재는 웹 버전만 업데이트되고 있습니다.

설치

Blockly GitHub에서 코드를 다운받습니다. 다운받은 파일을 압축을 풀고 blockly_compressed.js, blocks_compressed.js을 복사해 다른 곳에 저장해 둡니다. 이 두 파일이 Blockly 라이브러리의 핵심 파일입니다.

그다음 블록코딩한 블록을 코드로 변환하기 위한 파일을 가져와야 합니다. javascript_compressed.js, lua_compressed.js, php_compressed.js, python_compressed.js와 같이 프로그래밍 언어 이름 파일이 있는데 이 중 javascript_compressed.js는 코딩한 블록을 블록을 자바스크립트 코드로 변환하기 위해 필요한 파일입니다. javascript_compressed.js 역시 복사해 다른 곳에 저장해둡니다.

마지막으로 한국어 번역 파일이 필요합니다. Blockly의 블록 설명은 영어로 쓰여있는데 이를 한국어로 번역해 놓은 것을 가져오기 위해 한국어 번역 파일이 필요합니다. 한국어 파일은 msg/js/ko.js에서 찾을 수 있습니다.

정리하자면, blockly_compressed.js, blocks_compressed.js, javascript_compressed.js, msg/js/ko.js을 찾아 프로젝트의 라이브러리 폴더에 넣으시면 됩니다.

React, Vue, Webpack과 함께 사용하는 방법은 Blockly Samples를 참고하세요.

적용

아래와 같이 웹사이트의 HTML에서 라이브러리를 불러와 줍니다.

<script src="./script/blockly/blockly_compressed.js"></script>
<script src="./script/blockly/blocks_compressed.js"></script>
<script src="./script/blockly/javascript_compressed.js"></script>
<script src="./script/blockly/ko.js"></script>

그리고 Blockly 에디터가 들어갈 빈 div를 하나 만들어줍니다.

<div id="blockly"></div>

HTML 파일에서 블록코딩에서 사용할 수 있는 블록이 정의된 XML을 하나 정의해줍니다. 새로운 블록을 추가할 때는 이 XML을 수정하면 됩니다.

<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
  <category name="Logic" colour="%{BKY_LOGIC_HUE}">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_negate"></block>
    <block type="logic_boolean"></block>
    <block type="logic_null"></block>
    <block type="logic_ternary"></block>
  </category>
</xml>

웹페이지의 자바스크립트에서 Blockly 라이브러리 사용을 위한 초기화를 해줍니다.

const blockly = document.querySelector('#blockly');

const workspace = Blockly.inject(blockly, {
  toolbox: document.querySelector('#toolbox'),
  theme: Blockly.Themes.Classic,
});

Blockly.svgResize(workspace);

테마

Blockly의 테마를 정의하면 에디터의 배경색 등을 바꿀 수 있습니다.

참고: Blockly Theme Guide

자바스크립트 파일에서 Blockly.Themes.Custom으로 사용자 테마를 만들어줍니다.

Blockly.Themes.Custom = Blockly.Theme.defineTheme('custom', {
  base: Blockly.Themes.Classic,
  componentStyles: {
    workspaceBackgroundColour: '#eef2f9',
    toolboxBackgroundColour: '#f6f8fc',
    toolboxForegroundColour: '#6e6e8e',
    flyoutBackgroundColour: '#f6f8fc',
    flyoutForegroundColour: '#6e6e8e',
    flyoutOpacity: 1,
    scrollbarColour: '#bbbccb',
    scrollbarOpacity: 1,
    insertionMarkerColour: '#fff',
    insertionMarkerOpacity: 1,
    cursorColour: '#fff',
  },
});

그리고 workspace 정의 부분에서 themeBlockly.Themes.Custom으로 수정합니다.

const workspace = Blockly.inject(blockly, {
  toolbox: document.querySelector('#toolbox'),
  theme: Blockly.Themes.Custom,
});`

참고: Blockly Classic Theme

블록 추가

Blockly.Blocks에 추가할 블록의 형태를 정의합니다.

블록의 형태는 Blockly Developer Tools에서 블록을 만들고, Block Definition을 JavaScript로 바꿔서 출력한 코드를 가져와 붙여넣으면 쉽게 제작할 수 있습니다.

Blockly.Blocks.hello = {
  init() {
    this.jsonInit({
      message0: '%1번 인사하기',
      args0: [
        {
          type: 'field_number',
          name: 'TIME',
          value: 0,
          min: 0,
          precision: 1,
        },
      ],
      previousStatement: null,
      nextStatement: null,
      colour: '#fdd835',
    });
  },
};

정의한 블록이 코드로 바뀔 때의 코드 형태를 작성합니다.

저희는 javascript_compressed.js를 사용하기 때문에 Blockly.JavaScript에 블록의 형태를 정의합니다. 이때, Blockly.Blocks에서 정의한 블록 이름과 같아야 합니다. (아래 예제에서는 “hello” 블록)

Blockly.JavaScript.hello = (block) => {
  const time = block.getFieldValue('TIME');
  const code = `for(let i = 0; i < ${time}; i += 1) { alert("hello"); }\n`;
  return code;
};

마지막으로 XML에 돌아와서 생성한 블록을 추가합니다.

<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
  <category name="New" colour="#fdd835">
    <block type="hello"></block> <!-- 정의한 블록 -->
  </category>
  <category name="Logic" colour="%{BKY_LOGIC_HUE}">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_negate"></block>
    <block type="logic_boolean"></block>
    <block type="logic_null"></block>
    <block type="logic_ternary"></block>
  </category>
</xml>

참고: Blockly Developer Tools Guide

유용한 함수

Blockly 라이브러리에서 자주 사용하는 코드를 함수로 정리해봤습니다.

코드 생성

Blockly.JavaScript.workspaceToCode 함수로 workspace의 블록을 자바스크립트 코드로 변환할 수 있습니다.

const generateCode = () => {
  // 무한루프 방지 설정
  window.LoopTrap = 1000;
  Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if (--window.LoopTrap == 0) throw "Infinite loop.";';
  const code = Blockly.JavaScript.workspaceToCode(workspace);
  // 무한루프 방지 해제
  Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
  return code;
};

코드 실행

eval 함수를 이용해 위에서 생성한 자바스크립트 코드를 실행합니다.

const runWorkspace = async () => {
  const code = generateCode();
  console.log(code);
  try {
    console.log('Now running...');
    await eval(code);
    console.log('Done.');
  } catch (e) {
    alert(e);
  }
};

참고: Generating and Running JavaScript

workspace의 모든 블록 제거

const clearWorkspace = () => {
  Blockly.mainWorkspace.clear();
};

workspace의 모든 블록을 XML로 변환

const printWorkspaceAsXml = () => {
  const workspaceDomXml = Blockly.Xml.workspaceToDom(workspace);
  const workspaceRawXml = Blockly.Xml.domToPrettyText(workspaceDomXml);
  console.log(workspaceRawXml);
};

XML로 저장된 블록을 workspace에 불러오기

const MIME_XML = 'application/xml';

const loadWorkspace = (workspaceRawXml) => {
  const parser = new DOMParser();
  const workspaceDomXml = parser.parseFromString(workspaceRawXml, MIME_XML).documentElement;
  Blockly.Xml.clearWorkspaceAndLoadFromXml(workspaceDomXml, workspace);
};