Blockly 블럭 만들기 #1

Blockly는 사용자가 원하는 기능의 블럭을 자유롭게 만들수 있다. 기본 제공된 블럭 외에 사용자의 로봇이나 기타 기능과 연동하기 위한 블럭을 생성하고, 이를 툴박스에 넣어 사용하면 된다.

Blockly는 이러한 블럭들을 쉽게 만들수 있도록 도구를 제공한다. 재밌게도 블럭을 생성하는 것도 블럭으로 만든다. 물론 나중에 좀 익숙해지만 텍스트로 편집하는게 좀더 쉬울 수 있지만, 제공되는 도구를 이용하면 생성되는 블럭 모양을 확인하면서 수정이 가능하다.

먼저 Blockly Developer Tools를 사용해보자. 사파리나 크롬 등 브라우저를 열고 (https://blockly-demo.appspot.com/static/demos/blockfactory/index.html)으로 이동한다.

위와 같은 화면이 나오는데, 왼쪽은 블럭을 생성하기 위한 블럭 코딩 영역, 오른쪽 상단엔 생성된 블럭 모양을 실시간으로 확인이 가능하고, 오른쪽 중앙엔 추후 코드에 삽입하기 위한 JSON 코드, 오른쪽 하단엔 블럭을 실제 코드로 생성하기 위한 generator 코드가 생성된다.

일단 흐름을 보기 위해 아주 간단한 블럭을 생성해보도록 한다. 사용자의 문자열 입력을 받아 console에 출력하는 블럭을 만들어 보록 한다. 먼저 블럭의 name을 수정한다. 여기선 console_print로 한다.

다음으로 이 블럭은 다른 블럭들과 상하 연결하여 사용 가능해야 하므로, connection을 top+bottom connections로 변경한다. 이렇게 되면 미리보기 창의 블럭 모양이 변경됨을 볼 수 있다.

다음으로 사용자의 문자열을 입력 받아야 한다. 왼쪽 툴박스 창에서 Input 카테고리내에 value input 블럭을 inputs에 추가한다. 이 입력을 받아서 내부에서 처리하기 위한 변수명은 value_input 옆에 있는 NAME이다. 이를 필요에 따라 적절하게 수정한다.

이제 블럭에 기능을 설명하기 위한 텍스트 블럭을 추가한다. (Field 카테고리 Text 블럭)

입력은 Text만 받아야 하므로, 입력 type에 String 블럭을 추가한다.

마지막으로 블럭의 색상과, 이 블럭을 사용하는데 필요한 tooltip, help url 등을 입력한다.

이제 생성된 블럭의 모양을 확인해보면,

와 같이 간단한 모양의 블럭이 생성되었다. 생성된 JSON 코드를 확인해보면,

{
  "type": "console_print",
  "message0": "console print %1",
  "args0": [
    {
      "type": "input_value",
      "name": "NAME",
      "check": "String"
    }
  ],
  "previousStatement": null,
  "nextStatement": null,
  "colour": 330,
  "tooltip": "Block for printing the user message",
  "helpUrl": "https://ahnbk.com"
}

그리고, 이와 같이 생성된 Generator stub을 보면,

Blockly.JavaScript['console_print'] = function(block) {
  var value_name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);
  // TODO: Assemble JavaScript into code variable.
  var code = '...;\n';
  return code;
};

와 같이 되어 있다. 자 이제 블럭이 생성되었으므로, 이를 지난번에 만들었던 예제에 생성된 블럭을 추가해보도록 한다. 예제 디렉토리로 이동하여, my_blocks.js 파일을 생성하고, 다음과 같이 입력한다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    // insert blocks here

]);

이제 “insert blocks here 부분에 위에서 생성된 코드를 붙여 넣는다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    {
        "type": "console_print",
        "message0": "console print %1",
        "args0": [
          {
            "type": "input_value",
            "name": "NAME",
            "check": "String"
          }
        ],
        "previousStatement": null,
        "nextStatement": null,
        "colour": 330,
        "tooltip": "Block for printing the user message",
        "helpUrl": "https://ahnbk.com"
      },
]);

저장한 다음, index.html 파일을 열고 위 파일을 로딩하도록 추가한다.

<!DOCTYPE html>
<!-- HTML file to host Blockly in a mobile WebView. -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <style type="text/css">
    html, body, #blocklyDiv {
      border: 0;
      height: 100%;
      margin: 0;
      padding: 0;
      width: 100%;
    }
  </style>
  <script src="blockly_compressed.js"></script>
  <script src="blocks_compressed.js"></script>
  <!-- TODO: Select msg file based on locale. -->
  <script src="msg/js/en.js"></script>
  <script src="toolbox_standard.js"></script>
  <script src="my_blocks.js"></script>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script type="text/javascript">
    var workspacePlayground = Blockly.inject('blocklyDiv', {
          media: 'media/',
          toolbox: BLOCKLY_TOOLBOX_XML['standard'],
          zoom: {controls: true}
        });
  </script>
</body>
</html>

바로 이전 포스팅에서 만든 Custom 카테고리에 생성된 블럭을 추가한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+   '</block>'
+ '</category>'

자, 이제 예제를 다시 열어보면,

Custom 카테고리에 console print 블럭이 추가되어 있음을 볼 수있다. 만약 문자열 입력에 default 값을 넣어 주고 싶다면, 툴박스에 코드를 다음과 같이 수정한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+     '<value name="NAME">'
+       '<shadow type="text">'
+         '<field name="TEXT">Hello Blockly</field>'
+       '</shadow>'
+     '</value>'
+   '</block>'
+ '</category>'

이제 다시 예제를 확인해보면,

와 같이 되어 있음을 확인할 수 있다. 다음엔 좀더 다양한 모양의 블럭을 만들어보록 한다.

Blockly 툴박스에 카테고리 추가

Blockly를 실행하면 좌측 (혹은 설정에 의해서 하단)에 툴박스가 존재한다. 사용자의 필요에 따라 이 툴박스에 카테고리를 추가하는 등에 대한 작업을 할 수 있다.

툴박스에 설정은 toolbox_standard.js 파일에서 진행한다. 지난 포스팅에서 실행했던 디렉토리에서 toolbox_standard.js 파일을 열어보면…

var BLOCKLY_TOOLBOX_XML = BLOCKLY_TOOLBOX_XML || Object.create(null);

/* BEGINNING BLOCKLY_TOOLBOX_XML ASSIGNMENT. DO NOT EDIT. USE BLOCKLY DEVTOOLS. */
BLOCKLY_TOOLBOX_XML['standard'] =
// From XML string/file, replace ^\s?(\s*)?(<.*>)$ with \+$1'$2'
// Tweak first and last line.
'<xml>'
+ '<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" disabled="true"></block>'
+   '<block type="logic_ternary"></block>'
+ '</category>'
+ '<category name="Loops" colour="%{BKY_LOOPS_HUE}">'
+   '<block type="controls_repeat_ext">'
+     '<value name="TIMES">'
+       '<shadow type="math_number">'
+         '<field name="NUM">10</field>'

...

와 같이 되어 있다. Javascript로 되어 있으며, 자세히 살펴보면 XML을 String 형태로 변환해서 사용함을 볼 수 있다. 주석에 나와 있는 것처럼, Blockly Dev Tools를 이용해서 만들어 줄 수 있지만, 간단한 작업은 본 파일을 수정하면 된다.

Custom이라는 카테고리를 추가해보도록 한다. 블럭은 아직 추가하지 않기로 한다. 카테코리를 추가하고 싶은 지점에 다음과 같이 추가한다.

+ '<category name="Custom" colour="100">'
+ '</category>'

이제 다시 웹페이지를 reload해서 보게 되면,

Custom 카테고리가 추가되어 있는 것을 볼 수 있다. 아직 블럭들이 추가되어 있지 않으므로, 클릭해도 블럭들은 보이지 않는다. 카테고리 사이에 구분자(Seperator)를 추가하려면 원하는 지점에 다음과 같이 추가한다.

여기까지.

Blockly 개발환경 구축

Blockly는 오픈소스로 소스가 모두 개방되어 있다. 이를 가져와 개발자의 의도 및 용도에 맞게 수정하여 사용 가능하다. Blockly는 미들웨어 성격으로, 사용자의 어플리케이션에 맞게끔 수정 및 기능 추가가 용이하다.

일단 Blockly 소스를 가져와 빌드해보도록 한다. Javascript의 빌드 결과물은 주로 xxxx_compressed.js의 이름으로 저장되며, 여러개로 분산된 코드를 하나의 파일에 압축(정확히는 함축)하여 저장한다.

먼저 소스를 가져온다. github의 Blockly 레포지토리 (https://github.com/google/blockly/releases) 에서 최신 릴리즈 소스 (https://github.com/google/blockly/archive/1.20190215.0.tar.gz)를 다운로드한다.

받은 압축 파일을 임의의 디렉토리로 이동한 후 압축을 푼다. (본 포스팅에선 ~/Developer 를 사용.)

$ cd ~/Developer
$ tar zxf blockly-1.20190215.0.tar.gz

빌드하기 위해선 일단 python2.7이 설치되어 있어야 한다. (macOS엔 python 2.7이 기본 설치되어 있으므로 괜찮음. Windows에선 따로 설치 필요)

$ cd blockly-1.20190215.0
$ ./build.py

이때 다음과 같은 에러가 발생하면, closure-library가 필요하다.

Error: Closure not found.  Read this:
developers.google.com/blockly/guides/modify/web/closure

위 에러 메시지에서 알려준 링크 (developers.google.com/blockly/guides/modify/web/closure)로 가서 압축 파일을 다운로드 한다.

받은 파일을 ~/Developer에 풀고, 디렉토리 이름을 closure-library로 변경한다.

$ cd ~/Developer
$ tar zxf ~/Downloads/google-closure-library-v20190301-20-gde66a8a.tar.gz 
$ mv google-closure-library-de66a8a closure-library

이제 다시 blockly 디렉토리로 이동하여 빌드한다.

$ cd blockly-1.20190215.0
$ ./build.py

여러 메시지가 쭈욱 나오고, 에러가 없이 마무리되면 다음과 같은 파일이 생성된다. (이미 컴파일된 파일이 있긴 하지만, 업데이트 된다) 만약 java.lang.RuntimeException: INTERNAL COMPILER ERROR와 같은 에러가 발생한다면, 잠시후 다시 실행하면 정상적으로 빌드 된다.

...

SUCCESS: msg/js/tlh.js
SUCCESS: msg/js/ba.js
SUCCESS: blockly_uncompressed.js
SUCCESS: blockly_accessible_uncompressed.js
SUCCESS: blockly_compressed.js
Size changed from 2637 KB to 780 KB (30%).
SUCCESS: blockly_accessible_compressed.js
Size changed from 2739 KB to 826 KB (30%).
SUCCESS: blocks_compressed.js
Size changed from 154 KB to 74 KB (48%).
SUCCESS: javascript_compressed.js
Size changed from 85 KB to 47 KB (56%).
SUCCESS: python_compressed.js
Size changed from 71 KB to 36 KB (51%).
SUCCESS: php_compressed.js
Size changed from 74 KB to 38 KB (52%).
SUCCESS: lua_compressed.js
Size changed from 66 KB to 33 KB (50%).
SUCCESS: dart_compressed.js
Size changed from 78 KB to 41 KB (52%).

빌드 완료.

생성된 파일의 용도를 설명하면…

  • blockly_compressed.js : Blockly 코어 파일
  • blocks_compressed.js : 기본 블럭
  • javascript_compressed : Javascript 코드 제너레이터
  • python_compressed.js : Python 코드 제너레이터
  • php_compressed.js : PHP 코드 제너레이터
  • lua_compressed.js : Lua 코드 제너레이터
  • dart_compressed.js : Dart 코드 제너레이터

끗!

Blockly 소개

Blockly (블럭클리?, 블로키?)는 구글에서 오픈소스로 개발하고 있는  visual programming editors (소위 블럭코딩)을 개발하기 위한 Javascript 라이브러리이다. Scratch 3.0도 블럭을 제어하기 위한 부분은 Blockly 라이브러리를 가져와 수정하여 사용하고 있다.

대다수의 블럭코딩 툴들이 Blockly를 기반으로 개발되고 있다. 라이센스는 Apach 2.0을 사용하며, 소스는 https://github.com/google/blockly에 있다. 상업적 사용이 가능하고, 변경 및 배포에 아무런 제약이 없다.

Scratch 3.0이 그만의 프레임웍에 맞추어 개발해야 했다면, Blockly는 사용자가 자유롭게 변형하여 목적에 맞는 프로그램을 개발 가능하다.

블럭으로 만들어놓은 부분을 각종 언어 (Javascript, Pythom, Lua, Dart 등)로 생성이 가능하다. 또 사용자가 원하는 블럭도 생성이 가능하다. 코어가 Javascript로 되어 있어, 웹은 물론 Android, iOS에서도 WebView를 통해 실행이 가능하다. (동일한 코드로)

https://developers.google.com/blockly/ 이 메인 싸이트이며, Blockly에 대한 설명, 사용방법, 개발 방법 등에 대한 부분이 상세하게 적혀있다.

https://groups.google.com/forum/#!forum/blockly 는 Blockly 사용자들이 모여 있는 포럼으로 다수의 사용자들이 Blockly를 Customizing하고, 다양한 사례에 적용할 때 발생하는 문제점을 제시하고 서로 논의 하는 곳이다.


Scratch 3.0 소개

Scratch (https://scratch.mit.edu)는 전세계적으로 가장 널리 알려진 블럭 코딩 툴이다. 다른 거의 대부분의 블럭 코딩 도구들이 참조를 하고 있고, 거의 유사한 형태로 개발되고 있기도 하다.

MIT 미디어랩의  Lifelong Kindergarten Group에서 프로젝트로 개발되고 있고, 이전 버전은 사용해보지 않아서 모르겠지만 3.0은 오픈소스 형태로 개발중이며, 사용하고 있는 거의 모든 소스를 공개해 놓고 있다.

블럭 관련한 부분은 구글 Blockly를 가져와 디자인, 일부 동작 관련한 부분을 수정하여 사용하고 있으며, 나머지는 그룹에서 자체 개발 중인듯 하다.

사용하고 싶은 기업이나 개발자들은 자유롭게 복사해서 수정, 재배포 등이 가능하다. Scratch의 스크린샷을 책이나 발표자료에 사용하는 것도 가능하며, 브로셔 등에도 삽입이 가능하다. 다만 각 자료에 다음과 같은 내용을 담아주길 권고하고 있다.

“Scratch is a coding language and online community where you can create your own interactive stories, games, and animations — and share your creations with others around the world. As young people create and share Scratch projects, they learn to think creatively, reason systematically, and work collaboratively. Scratch is a project of the Scratch Foundation in collaboration with the Lifelong Kindergarten group at the MIT Media Lab. It is available for free at https://scratch.mit.edu

Scratch를 사업화에 사용하려는 기업에서 소스를 가져와 수정하고 이를 상용화 하여도 문제가 없다. 자세한 라이센스 및 사용 관련한 내용은 https://scratch.mit.edu/info/faq 를 참조하면 된다.

실제 동작하는 데모 버전을 구경하고 싶다면 https://llk.github.io/scratch-gui/develop/ 에서 사용해볼 수 있다.