TheOpenCloudEngine / uEngine5-base

uEngine5 BPMS that totally re-written in Microservices architecture. uEngine5 can act as not only a conventional Workflow or BPMS but also as a REST api orchestrator or a BPaaS (Business process as a service) of members of OCE's MSA components.
MIT License
10 stars 13 forks source link

Opengraph for vue #14

Closed SeungpilPark closed 7 years ago

SeungpilPark commented 7 years ago

Prop 타입 퍼포먼스 영향도

SvgTest.vue

<template>
  <div>
    <v-btn v-on:click="update()" flat class="orange--text">UpdateOne</v-btn>
    <v-btn v-on:click="updateAll()" flat class="orange--text">UpdateAll(expand)</v-btn>
    <v-btn v-on:click="updateCollapse()" flat class="orange--text">UpdateAll(collapse)</v-btn>
    <div style="width:10000px;height: 10000px">
      <svg height="10000" width="10000">

        <svg-test-element v-for="node in items" :x="node.x" :y="node.y" :path="node.path" :text="node.text" fill="none"
                          stroke="#000" :aaa="node.aaa">

        </svg-test-element>
      </svg>
    </div>
  </div>

</template>

<script>
  export default {
    props: {},

    data: function () {
      var items = [];
      for (var i = 0; i < 10000; i++) {
        var x = (i % 10) * 100;
        var y = Math.floor(i / 10) * 100 + 100;
        items.push(this.getGeom(x, y))
      }

      return {
        items: items
      };

    },

    watch: {
      items: function () {
        console.log('items change');
      }
    },

    computed: {},

    methods: {
      getGeom: function (x, y) {
        return {
          x: x,
          y: y,
          path: "M" + x + "," + y +
          "Q" + x + "," + (y - 10) + "," + (x + 10) + "," + (y - 10) +
          "L" + (x + 100) + "," + (y - 10) +
          "Q" + (x + 100) + "," + (y - 10) + "," + (x + 100) + "," + (y) +
          "L" + (x + 100) + "," + (y + 60) +
          "Q" + (x + 100) + "," + (y + 70) + "," + (x + 90) + "," + (y + 70) +
          "L" + (x + 10) + "," + (y + 70) +
          "Q" + (x) + "," + (y + 70) + "," + x + "," + (y + 60)
          + "Z",
          text: 'hello',
          aaa: {
            bbb: 'ccc'
          }
        }
      },
      update: function () {
        let geom = this.getGeom(50, 150);
        this.items[0].x = geom.x;
        this.items[0].y = geom.y;
        this.items[0].path = geom.path;
        this.items = JSON.parse(JSON.stringify(this.items))
      },
      updateAll: function () {
        var items = [];
        for (var i = 0; i < 10; i++) {
          var x = (i % 10) * 110;
          var y = Math.floor(i / 10) * 110 + 100;
          items.push(this.getGeom(x, y));
        }
        this.items = items;
      },
      updateCollapse: function () {
        var items = [];
        for (var i = 0; i < 10; i++) {
          var x = (i % 10) * 100;
          var y = Math.floor(i / 10) * 100 + 100;
          items.push(this.getGeom(x, y));
        }
        this.items = items;
      }
    }
  }
</script>

SvgTestElement.vue

<template>
  <g :x="x" :y="y" fill="none" stroke="#000">
    <path fill="url(#364r_.1__.1__FFFFFF-_FFFFCC)"
          stroke="#000000"
          :d="path"
          fill-r="1"
          fill-cx="0.1"
          fill-cy="0.1"
          opacity="1"
          fill-opacity="1"
          stroke-width="1.2"
          r="10"
          name="ORIGINAL_NODE"
          style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); opacity: 1; fill-opacity: 1; cursor: move;">
    </path>
    <g x="0" y="0" fill="none" stroke="#000" id="Task_1_LABEL"
       style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
      <text :x="x+50" :y="y+50" text-anchor="middle" font="10px 'serif'" stroke="none" fill="#000000"
            style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); text-anchor: middle; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; font-size: 12px; line-height: normal; font-family: serif; cursor: move; background-color: rgb(255, 255, 255);"
            fill-r="1" fill-cx="0.1" fill-cy="0.1" fill-opacity="1" stroke-width="1.2" r="10" font-size="12px"
            id="Task_1_LABELFO">
        <tspan dy="3.75" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
          {{ text }}
        </tspan>
      </text>
    </g>
  </g>
</template>

<script>
  export default {
    props: {
      x: Number,
      y: Number,
      path: String,
      text: String,
      aaa: Object
    },

    data: function () {

      return {};

    },

    watch: {
      aaa(){
        console.log('aaa');
      },
      x(){
          console.log('x');
      },
      y(){
        console.log('y');
      },
      path(){
        console.log('path');
      },
      text(){
        console.log('text');
      }
    },

    computed: {},

    methods: {}
  }
</script>

결과

2017-08-29 10 26 30

Class Object (OG.Canvas) prop 변경 테스트

다음과 같이 로직을 변경해봄.

SvgTest.vue

update: function () {
        let geom = this.getGeom(50, 150);
        this.items[0].x = geom.x;
        this.items[0].y = geom.y;
        this.items[0].path = geom.path;
        this.items[0].aaa = geom.aaa;
        //this.items = JSON.parse(JSON.stringify(this.items))

        this.canvas.drawShape([100,100],new OG.A_HumanTask('bbb'), [100,100]);
        this.canvas._CONFIG.AUTOMATIC_GUIDANCE = false;
      },

SvgTestElement.vue

watch: {
      canvas(){
        console.log('canvas');
      },
SeungpilPark commented 7 years ago

Vue SVG Width & Height Auto Fix

뷰 js 에서는 SVG 의 width, height 를 상위 컨테이너와 svg 의 transform 인자를 계산하여 자동으로 제어한다.

따라서, 아래 기존의 오픈그래프 svg 기본 컨테이너의 양식을 쓸 경우

<div id="container" style="overflow:scroll">
    <svg width="2000px" height="2000px"></svg>
</div>
SeungpilPark commented 7 years ago

Array Svg 에서 splice 처리 할 경우 모든 svg 요소들이 destroy 된 후 다시 mounted 된다.

따라서 Svg 히스토리 및 에디트 핸들링 중에는 삭제 되는 svg 들에 대해서 Arrary 에 Null 로 대체하고, 익덱스는 유지토록 한다.

디버깅 후 잘못 파악했던 것:

뷰에서는 Array 타입의 옵저버시 각 인덱스별로 옵저버 아이디를 부여하고, splice 메소드에 대해 캐쉬를 사용함으로 문제되지 않는다. 문제되는 것은 히스토리를 Json stringify 로 인해 옵저버 메소드가 전부 파괴되기 때문에 컴포넌트까지 파기되었던 것!!

SeungpilPark commented 7 years ago

예기치 못한 상황의 상위 컴포넌트에 의한 Svg 컴포넌트 삭제시 watch 이벤트를 다시 받아들일 수 있도록 한다.

SeungpilPark commented 7 years ago

기존 연결선의 끝점을 다른 Shape 로 이동시켰을 경우 => Connect 이벤트 발생 => 기존 연결선 컴포넌트 삭제 => 새로운 relation (source,target,vertices) => definition change => 연결선 생성

SeungpilPark commented 7 years ago

//아이디가 변경될 때 => //TODO 아래 순서를 프로퍼티 패널에서 트레이싱 태그 변경시 적용토록 한다. //me.element 는 이전에 등록된 element //해당 경우는 트레이신 태그를 가진 activity 에서만 통용된다. //프로퍼티 패널 아이디 변경 //릴레이션 source, target 변경. from,to 를 source,target 아이디로 변경 //릴레이션 아이디가 틀리게 옴. //선연결이 사라짐. //새로 선연결을 함.

//신규 등록시 => 캔버스의 generate 메소드 제작. => 롤의 경우 그냥. => 액티비티의 경우 트레이싱 태그를 생성

//로딩 후 최초 상태를 히스토리에 추가하기. //라우터 변경시 히스토리를 초기화하기. //저장 하기.

SeungpilPark commented 7 years ago

도형 클릭하여 나오는 서브메뉴로 신규 도형 생성시 definition 추가 및 트레이싱 태그 추가, 릴레이션 추가. => 이때 Definition 추가되면 자동으로 Lane 위로 그룹처리 된다. Lane 분기시 자식 Lane 들 definition 추가.

SeungpilPark commented 7 years ago

history 를 반복하거나, 프로퍼티 패널에서 스타일을 변경을 반복하였을때 (100회 이상) 반응속도가 현저히 떨어짐.

=> 메모리 확인결과, 뷰 컴포넌트에서 오픈그래프 element 를 가비지로 누적하여 가지고 가고 있음. => 도형 업데이트시 깔끔하게 element 를 지우고 신규 element 를 뷰 컴포넌트에 등록하자.

SeungpilPark commented 7 years ago

x,y,width,height,style 을 프로퍼티 패널에서 변경시 문제 - 해당 컴포넌트의 외형만 변하고, 해당 컴포넌트와 연결 관계 및 부모-자식 관계의 컴포넌트들은 스테이.

TODO

    1  x,y,width,height 변경시는 move,resize 메소드를 통한다.

    2  style 은 formStyle 은 버린다. 데이터에서 element style 을 가져오고, watch 시에 setShapeStyle 을 시도한다.

    3. 1 과 2 를 통해서 연결선 및 그룹 변경시의 사이드 이펙트를 처리했다고 하자. 그 이후에는 각 컴포넌트의 updateVue 가 필요하다. => 캔버스 히스토리 트리거

    4. 1의 과정에서 x,y,width,height 변경시 부모(각 컴포넌트)의 값 동기화를 피하도록한다. 그러기 위해서 직접 x,y 등을 데이터처리로 바꾸도록 한다.

    5. 4의 이유는 직접 어세스 할 경우 데이터가 틀려짐으로 판단하여 삭제 후 다시 그릴때, 연결선도 삭제되는데, 연결선을 복구시키기 위해 별도의 메소드 호출을 피하기 위함.
SeungpilPark commented 7 years ago

오픈그래프의 shape 관련 함수 완전 파기. 태그 기반 svg 꾸미기를 위해...?

shape 단위로 컴포넌트 생성. 사용자는 컴포넌트 조합으로 원하는 도형 제작. 컴포넌트 안에는 geometry 정의를 프로퍼티로 가능. 릴레이션 정보는...? 릴레이션 정보도 프로퍼티로 가지고 있음. 컴포넌트 간의 연결 funtion 을 제공함. 컴포넌트의 이벤트 함수를 제공함. (캔버스 레벨) 컴포넌트 추가 정보는...? => 그런거 없음. 그림만 제공. 컴포넌트를 활용한 상위 컴포넌트에서 드리빙.

SeungpilPark commented 7 years ago

노가다 시작

각 Bpmn 컴포넌트 제작하기.

SeungpilPark commented 7 years ago

shape 파기

SeungpilPark commented 7 years ago

Element 내부 props watch 고도화 작업. (렌더링 재수행 판별 고도화)

SeungpilPark commented 7 years ago

Element 이벤트와 Vue 이벤트 emit 동기화 작업.

TODO

  1. 개별 엘리먼트의 이벤트 emit
  2. 캔버스 레벨 이벤트 emit
SeungpilPark commented 7 years ago

TODO 캔버스 프로퍼티 => 뷰 props

  1. 캔버스 전역 스타일 prop
  2. 캔버스 동작 옵션 prop
완성된 props, 경험을 토대로 실제로 쓰이는 캔버스 옵션들만 표현.
뷰 static 리소스 경로를 사용하기 위해  IMAGE_BASE 가 추가됨.

props: {
      /**
       * 캔버스 가로 (px)
       */
      width: {
        default: function () {
          return 2000;
        },
        type: Number
      },
      /**
       * 캔버스 세로 (px)
       */
      height: {
        default: function () {
          return 2000;
        },
        type: Number
      },
      /**
       * 캔버스 스케일(리얼 사이즈 : Scale = 1)
       */
      scale: {
        default: function () {
          return 1;
        },
        type: Number
      },
      /**
       * 캔버스 최소 스케일
       */
      scaleMin: {
        default: function () {
          return 0.1;
        },
        type: Number
      },
      /**
       * 캔버스 최대 스케일
       */
      scaleMax: {
        default: function () {
          return 10;
        },
        type: Number
      },
      /**
       * 캔버스 배경색
       */
      backgroundColor: {
        default: function () {
          return '#f9f9f9';
        },
        type: String
      },
      /**
       * 캔버스 배경이미지
       */
      backgroundImage: String,
      /**
       * 이미지 베이스 패스
       */
      imageBase: {
        default: function () {
          return '/static/image/symbol/';
        },
        type: String
      },
      /**
       * 풀, 래인 도형의 드랍시 자동 위치 조정 기능
       */
      poolDropEvent: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 도형, 스팟 이동시 이웃한 도형에 대해 자동보정이 이루어지는 여부.
       */
      automaticGuidance: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 선연결을 클릭하였을때만 변곡점 변경 가능 여부
       */
      spotOnSelect: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 자동 슬라이더 업데이트 여부
       */
      autoSliderUpdate: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 자동 히스토리 저장
       */
      autoHistory: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 마우스 휠 스케일 변경 여부
       */
      wheelScalable: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 마우스 드래그 페이지 이동 가능 여부
       */
      dragPageMovable: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 도형 선택시 캔버스 포거싱 여부
       */
      focusCanvasOnSelect: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 연결된 두 오브젝트의 소속에 따른 연결선 스타일 변화 여부
       */
      checkBridgeEdge: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 스틱 가이드 생성 여부
       */
      stickGuide: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 네비게이터 사용
       */
      navigator: {
        default: function () {
          return false;
        },
        type: Boolean
      },
      /**
       * 클릭선택 가능여부
       */
      selectable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 마우스드래그선택 가능여부
       */
      dragSelectable: {
        default: function () {
          return true;
        },
        type: Boolean
      },

      /**
       * 이동 가능여부
       */
      movable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 리사이즈 가능여부
       */
      resizable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 연결 가능여부
       */
      connectable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * Self 연결 가능여부
       */
      selfConnectable: {
        default: function () {
          return true;
        },
        type: Boolean
      },

      /**
       * 가이드에 자기자신을 복사하는 컨트롤러 여부.
       */
      selfCloneable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 드래그하여 연결시 그룹을 건너뛸때 스타일 변경 여부
       */
      connectStyleChange: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 가이드에 삭제 컨트롤러 여부
       */
      deletable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 라벨 수정여부
       */
      labelEditable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 그룹핑 가능여부
       */
      groupDropable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 이동, 리사이즈 드래그시 MOVE_SNAP_SIZE 적용 여부
       */
      dragGridable: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 핫키 가능여부
       */
      enableHotkey: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 마우스 우클릭 메뉴 가능여부
       */
      enableContextmenu: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 루트 컨텍스트 메뉴 가능여부
       */
      enableRootContextmenu: {
        default: function () {
          return true;
        },
        type: Boolean
      },
      /**
       * 도형 디폴트 스타일
       */
      defaultStyleShape: {
        default: function () {
          return {
            cursor: "default"
          };
        },
        type: Object
      }
      ,
      /**
       * geometry 도형 디폴트 스타일
       */
      defaultStyleGeometry: {
        default: function () {
          return {
            stroke: "black",
            "fill-r": ".5",
            "fill-cx": ".5",
            "fill-cy": ".5",
            fill: "white",
            "fill-opacity": 0,
            "label-position": "center"
          };
        },
        type: Object
      },
      /**
       * Text 도형 디폴트 스타일
       */
      defaultStyleText: {
        default: function () {
          return {
            stroke: "none", "text-anchor": "middle"
          };
        },
        type: Object
      },
      /**
       * Html 도형 디폴트 스타일
       */
      defaultStyleHtml: {
        default: function () {
          return {
            "label-position": "bottom", "text-anchor": "middle", "vertical-align": "top"
          };
        },
        type: Object
      }
      ,
      /**
       * Image 도형 디폴트 스타일
       */
      defaultStyleImage: {
        default: function () {
          return {
            "label-position": "bottom", "text-anchor": "middle", "vertical-align": "top"
          };
        },
        type: Object
      }
      ,
      /**
       * Svg 도형 디폴트 스타일
       */
      defaultStyleSvg: {
        default: function () {
          return {
            "label-position": "bottom", "text-anchor": "middle", "vertical-align": "top"
          };
        },
        type: Object
      }
      ,
      /**
       * Edge 도형 디폴트 스타일
       */
      defaultStyleEdge: {
        default: function () {
          return {
            stroke: "black",
            fill: "none",
            "fill-opacity": 0,
            "stroke-width": 1.5,
            "stroke-opacity": 1,
            "edge-type": "plain",
            "arrow-start": "none",
            "arrow-end": "block",
            "stroke-dasharray": "",
            "label-position": "center",
            "stroke-linejoin": "round",
            cursor: "pointer"
          };
        },
        type: Object
      },
      /**
       * Group 도형 디폴트 스타일
       */
      defaultStyleGroup: {
        default: function () {
          return {
            stroke: "black",
            fill: "none",
            "fill-opacity": 0,
            "label-position": "bottom",
            "text-anchor": "middle",
            "vertical-align": "top"
          };
        },
        type: Object
      },
      /**
       * 가이드 툴 박스 디폴트 스타일
       */
      defaultStyleGuideBbox: {
        default: function () {
          return {
            stroke: "#00FF00",
            fill: "white",
            "fill-opacity": 0,
            "stroke-dasharray": "- ",
            "shape-rendering": "crispEdges",
            cursor: "move"
          };
        },
        type: Object
      }
      ,
      /**
       * 가이드 툴 상단 좌측 스타일
       */
      defaultStyleGuideUl: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nwse-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      }
      ,
      /**
       * 가이드 툴 상단 우측 스타일
       */
      defaultStyleGuideUr: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      }
      ,
      /**
       * 가이드 툴 하단 좌측 스타일
       */
      defaultStyleGuideLl: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      }
      ,
      /**
       * 가이드 툴 하단 우측 스타일
       */
      defaultStyleGuideLr: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 가이드 툴 좌측 센터 스타일
       */
      defaultStyleGuideLc: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 가이드 툴 상단 센터 스타일
       */
      defaultStyleGuideUc: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 가이드 툴 우측 센터 스타일
       */
      defaultStyleGuideRc: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 가이드 툴 하단 센터 스타일
       */
      defaultStyleGuideLwc: {
        default: function () {
          return {
            stroke: "#03689a",
            fill: "#03689a",
            "fill-opacity": 0.5,
            cursor: "nesw-resize",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 도형 클론 가상 연결선 스타일
       */
      defaultStyleVirtualEdge: {
        default: function () {
          return {
            stroke: "black",
            fill: "none",
            "fill-opacity": 0,
            "stroke-width": 1,
            "stroke-opacity": 1,
            "stroke-dasharray": "- ",
            "stroke-linejoin": "round",
            "arrow-start": "none",
            "arrow-end": "none"
          };
        },
        type: Object
      },
      /**
       * 화면 드래그 선택시 드래그 박스 스타일
       */
      defaultStyleRubberBand: {
        default: function () {
          return {
            stroke: "#0000FF",
            opacity: 0.2,
            fill: "#0077FF"
          };
        },
        type: Object
      },
      /**
       * 그룹 드랍 가능시 그룹 박스 스타일
       */
      defaultStyleDropOverBbox: {
        default: function () {
          return {
            stroke: "#0077FF", fill: "none", opacity: 0.3, "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 라벨 디폴트 스타일
       */
      defaultStyleLabel: {
        default: function () {
          return {
            "font-size": 12, "font-color": "black", "fill": "none"
          };
        },
        type: Object
      },
      /**
       * 연결 가능도형의 연결 테두리 스타일
       */
      defaultStyleConnectGuideBbox: {
        default: function () {
          return {
            stroke: "#00FF00",
            fill: "none",
            "stroke-dasharray": "- ",
            "shape-rendering": "crispEdges"
          };
        },
        type: Object
      },
      /**
       * 선연결 도형의 원형 Spot 스타일
       */
      defaultStyleConnectGuideSpotCircle: {
        default: function () {
          return {
            r: 7,
            stroke: "#A6A6A6",
            "stroke-width": 1,
            fill: "#FFE400",
            "fill-opacity": 0.5,
            cursor: "pointer"
          };
        },
        type: Object
      },
      /**
       * 선연결 도형의 사각형 Spot 스타일
       */
      defaultStyleConnectGuideSpotRect: {
        default: function () {
          return {
            stroke: "#A6A6A6",
            "stroke-width": 1,
            fill: "#FFE400",
            "fill-opacity": 0.2,
            cursor: "ns-resize",
            w: 20,
            h: 10
          };
        },
        type: Object
      },
      /**
       * 연결가능 표현 하이라이트 스타일
       */
      defaultStyleConnectableHighlight: {
        default: function () {
          return {
            //"stroke-width": 2
          };
        },
        type: Object
      }
    },
SeungpilPark commented 7 years ago

TODO

BPMN 모델링 다시 만들기.

  1. bpmn 컴포넌트 shape 들을 신규 태그기반재작성
  2. bpmn 메소드 재작성